<commit_before>#![allow(clippy::manual_assert)]

mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "e100ec5bc7cd768ec17d75448b29c9ab4a39272b";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO: impl ~const T {}
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/syntax.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/early/closing-args-token.rs",
    "src/test/ui/const-generics/early/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",
    "src/tools/rustfmt/tests/source/trait.rs",
    "src/tools/rustfmt/tests/target/trait.rs",

    // Excessive nesting
    "src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs",

    // Testing rustfmt on invalid syntax
    "src/tools/rustfmt/tests/coverage/target/comments.rs",
    "src/tools/rustfmt/tests/parser/issue-4126/invalid.rs",
    "src/tools/rustfmt/tests/parser/issue_4418.rs",
    "src/tools/rustfmt/tests/parser/unclosed-delims/issue_4466.rs",
    "src/tools/rustfmt/tests/source/configs/disable_all_formatting/true.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/source/type.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/target/type.rs",

    // Clippy lint lists represented as expressions
    "src/tools/clippy/clippy_lints/src/lib.deprecated.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_all.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_cargo.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_complexity.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_correctness.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_internal.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_lints.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_nursery.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_perf.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_restriction.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_style.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/macros/include-single-expr-helper.rs",
    "src/test/ui/macros/include-single-expr-helper-1.rs",
    "src/test/ui/parser/issues/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map_or(true, |e| e != "rs") {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Advance test suite past the let-chains PR<commit_after>#![allow(clippy::manual_assert)]

mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "5e57faa78aa7661c6000204591558f6665f11abc";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO
    "src/test/ui/expr/if/attrs/let-chains-attr.rs",
    "src/test/ui/mir/mir_let_chains_drop_order.rs",
    "src/test/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs",
    "src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs",
    "src/test/ui/rfc-2497-if-let-chains/issue-90722.rs",
    "src/test/ui/rfc-2497-if-let-chains/then-else-blocks.rs",
    "src/tools/rustfmt/tests/source/async_block.rs",
    "src/tools/rustfmt/tests/target/async_block.rs",

    // TODO: impl ~const T {}
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/syntax.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/early/closing-args-token.rs",
    "src/test/ui/const-generics/early/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",
    "src/tools/rustfmt/tests/source/trait.rs",
    "src/tools/rustfmt/tests/target/trait.rs",

    // Excessive nesting
    "src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs",

    // Testing rustfmt on invalid syntax
    "src/tools/rustfmt/tests/coverage/target/comments.rs",
    "src/tools/rustfmt/tests/parser/issue-4126/invalid.rs",
    "src/tools/rustfmt/tests/parser/issue_4418.rs",
    "src/tools/rustfmt/tests/parser/unclosed-delims/issue_4466.rs",
    "src/tools/rustfmt/tests/source/configs/disable_all_formatting/true.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/source/type.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/target/type.rs",

    // Clippy lint lists represented as expressions
    "src/tools/clippy/clippy_lints/src/lib.deprecated.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_all.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_cargo.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_complexity.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_correctness.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_internal.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_lints.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_nursery.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_perf.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_restriction.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_style.rs",
    "src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/macros/include-single-expr-helper.rs",
    "src/test/ui/macros/include-single-expr-helper-1.rs",
    "src/test/ui/parser/issues/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map_or(true, |e| e != "rs") {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>//! Coercion logic. Coercions are certain type conversions that can implicitly
//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions
//! like going from `&Vec<T>` to `&[T]`.
//!
//! See: https://doc.rust-lang.org/nomicon/coercions.html

use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
use hir_def::lang_item::LangItemTarget;

use crate::{
    autoderef, to_chalk_trait_id, traits::Solution, Interner, Substitution, TraitRef, Ty,
    TyBuilder, TyKind,
};

use super::{InEnvironment, InferenceContext};

impl<'a> InferenceContext<'a> {
    /// Unify two types, but may coerce the first one to the second one
    /// using "implicit coercion rules" if needed.
    pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
        let from_ty = self.resolve_ty_shallow(from_ty).into_owned();
        let to_ty = self.resolve_ty_shallow(to_ty);
        self.coerce_inner(from_ty, &to_ty)
    }

    /// Merge two types from different branches, with possible coercion.
    ///
    /// Mostly this means trying to coerce one to the other, but
    ///  - if we have two function types for different functions, we need to
    ///    coerce both to function pointers;
    ///  - if we were concerned with lifetime subtyping, we'd need to look for a
    ///    least upper bound.
    pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
        if self.coerce(ty1, ty2) {
            ty2.clone()
        } else if self.coerce(ty2, ty1) {
            ty1.clone()
        } else {
            if let (TyKind::FnDef(..), TyKind::FnDef(..)) =
                (ty1.kind(&Interner), ty2.kind(&Interner))
            {
                cov_mark::hit!(coerce_fn_reification);
                // Special case: two function types. Try to coerce both to
                // pointers to have a chance at getting a match. See
                // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
                let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig");
                let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig");
                let ptr_ty1 = TyBuilder::fn_ptr(sig1);
                let ptr_ty2 = TyBuilder::fn_ptr(sig2);
                self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
            } else {
                cov_mark::hit!(coerce_merge_fail_fallback);
                ty1.clone()
            }
        }
    }

    fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
            // Never type will make type variable to fallback to Never Type instead of Unknown.
            (TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => {
                self.table.type_variable_table.set_diverging(*tv, true);
                return true;
            }
            (TyKind::Never, _) => return true,

            // Trivial cases, this should go after `never` check to
            // avoid infer result type to be never
            _ => {
                if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
                    return true;
                }
            }
        }

        // Pointer weakening and function to pointer
        match (from_ty.interned_mut(), to_ty.kind(&Interner)) {
            // `*mut T` -> `*const T`
            // `&mut T` -> `&T`
            (TyKind::Raw(m1, ..), TyKind::Raw(m2 @ Mutability::Not, ..))
            | (TyKind::Ref(m1, ..), TyKind::Ref(m2 @ Mutability::Not, ..)) => {
                *m1 = *m2;
            }
            // `&T` -> `*const T`
            // `&mut T` -> `*mut T`/`*const T`
            (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..))
            | (TyKind::Ref(Mutability::Mut, substs), &TyKind::Raw(m2, ..)) => {
                from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner);
            }

            // Illegal mutability conversion
            (TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..))
            | (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false,

            // `{function_type}` -> `fn()`
            (TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) {
                None => return false,
                Some(sig) => {
                    from_ty = TyBuilder::fn_ptr(sig);
                }
            },

            (TyKind::Closure(.., substs), TyKind::Function { .. }) => {
                from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
            }

            _ => {}
        }

        if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
            return ret;
        }

        // Auto Deref if cannot coerce
        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
            // FIXME: DerefMut
            (TyKind::Ref(_, st1), TyKind::Ref(_, st2)) => self.unify_autoderef_behind_ref(st1, st2),

            // Otherwise, normal unify
            _ => self.unify(&from_ty, to_ty),
        }
    }

    /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
    ///
    /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html
    fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> {
        let krate = self.resolver.krate().unwrap();
        let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
            Some(LangItemTarget::TraitId(trait_)) => trait_,
            _ => return None,
        };

        let generic_params = crate::utils::generics(self.db.upcast(), coerce_unsized_trait.into());
        if generic_params.len() != 2 {
            // The CoerceUnsized trait should have two generic params: Self and T.
            return None;
        }

        let substs = Substitution::build_for_generics(&generic_params)
            .push(from_ty.clone())
            .push(to_ty.clone())
            .build();
        let trait_ref =
            TraitRef { trait_id: to_chalk_trait_id(coerce_unsized_trait), substitution: substs };
        let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner));

        let canonicalizer = self.canonicalizer();
        let canonicalized = canonicalizer.canonicalize_obligation(goal);

        let solution = self.db.trait_solve(krate, canonicalized.value.clone())?;

        match solution {
            Solution::Unique(v) => {
                canonicalized.apply_solution(self, v.0);
            }
            _ => return None,
        };

        Some(true)
    }

    /// Unify `from_ty` to `to_ty` with optional auto Deref
    ///
    /// Note that the parameters are already stripped the outer reference.
    fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
        let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone());
        let to_ty = self.resolve_ty_shallow(&to_ty);
        // FIXME: Auto DerefMut
        for derefed_ty in autoderef::autoderef(
            self.db,
            self.resolver.krate(),
            InEnvironment {
                goal: canonicalized.value.clone(),
                environment: self.trait_env.env.clone(),
            },
        ) {
            let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
            let from_ty = self.resolve_ty_shallow(&derefed_ty);
            // Stop when constructor matches.
            if from_ty.equals_ctor(&to_ty) {
                // It will not recurse to `coerce`.
                return self.table.unify(&from_ty, &to_ty);
            } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
                return true;
            }
        }

        false
    }
}
<commit_msg>More TyBuilder use<commit_after>//! Coercion logic. Coercions are certain type conversions that can implicitly
//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions
//! like going from `&Vec<T>` to `&[T]`.
//!
//! See: https://doc.rust-lang.org/nomicon/coercions.html

use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
use hir_def::lang_item::LangItemTarget;

use crate::{
    autoderef, traits::Solution, Interner, Ty,
    TyBuilder, TyKind,
};

use super::{InEnvironment, InferenceContext};

impl<'a> InferenceContext<'a> {
    /// Unify two types, but may coerce the first one to the second one
    /// using "implicit coercion rules" if needed.
    pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
        let from_ty = self.resolve_ty_shallow(from_ty).into_owned();
        let to_ty = self.resolve_ty_shallow(to_ty);
        self.coerce_inner(from_ty, &to_ty)
    }

    /// Merge two types from different branches, with possible coercion.
    ///
    /// Mostly this means trying to coerce one to the other, but
    ///  - if we have two function types for different functions, we need to
    ///    coerce both to function pointers;
    ///  - if we were concerned with lifetime subtyping, we'd need to look for a
    ///    least upper bound.
    pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty {
        if self.coerce(ty1, ty2) {
            ty2.clone()
        } else if self.coerce(ty2, ty1) {
            ty1.clone()
        } else {
            if let (TyKind::FnDef(..), TyKind::FnDef(..)) =
                (ty1.kind(&Interner), ty2.kind(&Interner))
            {
                cov_mark::hit!(coerce_fn_reification);
                // Special case: two function types. Try to coerce both to
                // pointers to have a chance at getting a match. See
                // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
                let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig");
                let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig");
                let ptr_ty1 = TyBuilder::fn_ptr(sig1);
                let ptr_ty2 = TyBuilder::fn_ptr(sig2);
                self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
            } else {
                cov_mark::hit!(coerce_merge_fail_fallback);
                ty1.clone()
            }
        }
    }

    fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
            // Never type will make type variable to fallback to Never Type instead of Unknown.
            (TyKind::Never, TyKind::InferenceVar(tv, TyVariableKind::General)) => {
                self.table.type_variable_table.set_diverging(*tv, true);
                return true;
            }
            (TyKind::Never, _) => return true,

            // Trivial cases, this should go after `never` check to
            // avoid infer result type to be never
            _ => {
                if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
                    return true;
                }
            }
        }

        // Pointer weakening and function to pointer
        match (from_ty.interned_mut(), to_ty.kind(&Interner)) {
            // `*mut T` -> `*const T`
            // `&mut T` -> `&T`
            (TyKind::Raw(m1, ..), TyKind::Raw(m2 @ Mutability::Not, ..))
            | (TyKind::Ref(m1, ..), TyKind::Ref(m2 @ Mutability::Not, ..)) => {
                *m1 = *m2;
            }
            // `&T` -> `*const T`
            // `&mut T` -> `*mut T`/`*const T`
            (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..))
            | (TyKind::Ref(Mutability::Mut, substs), &TyKind::Raw(m2, ..)) => {
                from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner);
            }

            // Illegal mutability conversion
            (TyKind::Raw(Mutability::Not, ..), TyKind::Raw(Mutability::Mut, ..))
            | (TyKind::Ref(Mutability::Not, ..), TyKind::Ref(Mutability::Mut, ..)) => return false,

            // `{function_type}` -> `fn()`
            (TyKind::FnDef(..), TyKind::Function { .. }) => match from_ty.callable_sig(self.db) {
                None => return false,
                Some(sig) => {
                    from_ty = TyBuilder::fn_ptr(sig);
                }
            },

            (TyKind::Closure(.., substs), TyKind::Function { .. }) => {
                from_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
            }

            _ => {}
        }

        if let Some(ret) = self.try_coerce_unsized(&from_ty, &to_ty) {
            return ret;
        }

        // Auto Deref if cannot coerce
        match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
            // FIXME: DerefMut
            (TyKind::Ref(_, st1), TyKind::Ref(_, st2)) => self.unify_autoderef_behind_ref(st1, st2),

            // Otherwise, normal unify
            _ => self.unify(&from_ty, to_ty),
        }
    }

    /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>`
    ///
    /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html
    fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> Option<bool> {
        let krate = self.resolver.krate().unwrap();
        let coerce_unsized_trait = match self.db.lang_item(krate, "coerce_unsized".into()) {
            Some(LangItemTarget::TraitId(trait_)) => trait_,
            _ => return None,
        };

        let trait_ref = {
            let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait);
            if b.remaining() != 2 {
                // The CoerceUnsized trait should have two generic params: Self and T.
                return None;
            }
            b.push(from_ty.clone()).push(to_ty.clone()).build()
        };

        let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner));

        let canonicalizer = self.canonicalizer();
        let canonicalized = canonicalizer.canonicalize_obligation(goal);

        let solution = self.db.trait_solve(krate, canonicalized.value.clone())?;

        match solution {
            Solution::Unique(v) => {
                canonicalized.apply_solution(self, v.0);
            }
            _ => return None,
        };

        Some(true)
    }

    /// Unify `from_ty` to `to_ty` with optional auto Deref
    ///
    /// Note that the parameters are already stripped the outer reference.
    fn unify_autoderef_behind_ref(&mut self, from_ty: &Ty, to_ty: &Ty) -> bool {
        let canonicalized = self.canonicalizer().canonicalize_ty(from_ty.clone());
        let to_ty = self.resolve_ty_shallow(&to_ty);
        // FIXME: Auto DerefMut
        for derefed_ty in autoderef::autoderef(
            self.db,
            self.resolver.krate(),
            InEnvironment {
                goal: canonicalized.value.clone(),
                environment: self.trait_env.env.clone(),
            },
        ) {
            let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
            let from_ty = self.resolve_ty_shallow(&derefed_ty);
            // Stop when constructor matches.
            if from_ty.equals_ctor(&to_ty) {
                // It will not recurse to `coerce`.
                return self.table.unify(&from_ty, &to_ty);
            } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
                return true;
            }
        }

        false
    }
}
<|endoftext|>


<commit_before>//! lint on missing cargo common metadata

use std::path::PathBuf;

use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::DUMMY_SP;

declare_clippy_lint! {
    /// ### What it does
    /// Checks to see if all common metadata is defined in
    /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
    ///
    /// ### Why is this bad?
    /// It will be more difficult for users to discover the
    /// purpose of the crate, and key information related to it.
    ///
    /// ### Example
    /// ```toml
    /// # This `Cargo.toml` is missing a description field:
    /// [package]
    /// name = "clippy"
    /// version = "0.0.212"
    /// repository = "https://github.com/rust-lang/rust-clippy"
    /// readme = "README.md"
    /// license = "MIT OR Apache-2.0"
    /// keywords = ["clippy", "lint", "plugin"]
    /// categories = ["development-tools", "development-tools::cargo-plugins"]
    /// ```
    ///
    /// Should include a description field like:
    ///
    /// ```toml
    /// # This `Cargo.toml` includes all common metadata
    /// [package]
    /// name = "clippy"
    /// version = "0.0.212"
    /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
    /// repository = "https://github.com/rust-lang/rust-clippy"
    /// readme = "README.md"
    /// license = "MIT OR Apache-2.0"
    /// keywords = ["clippy", "lint", "plugin"]
    /// categories = ["development-tools", "development-tools::cargo-plugins"]
    /// ```
    pub CARGO_COMMON_METADATA,
    cargo,
    "common metadata is defined in `Cargo.toml`"
}

#[derive(Copy, Clone, Debug)]
pub struct CargoCommonMetadata {
    ignore_publish: bool,
}

impl CargoCommonMetadata {
    pub fn new(ignore_publish: bool) -> Self {
        Self { ignore_publish }
    }
}

impl_lint_pass!(CargoCommonMetadata => [
    CARGO_COMMON_METADATA
]);

fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) {
    let message = format!("package `{}` is missing `{}` metadata", package.name, field);
    span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message);
}

fn is_empty_str(value: &Option<String>) -> bool {
    value.as_ref().map_or(true, String::is_empty)
}

fn is_empty_path(value: &Option<PathBuf>) -> bool {
    value.as_ref().and_then(|x| x.to_str()).map_or(true, str::is_empty)
}

fn is_empty_vec(value: &[String]) -> bool {
    // This works because empty iterators return true
    value.iter().all(String::is_empty)
}

impl LateLintPass<'_> for CargoCommonMetadata {
    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
        if is_lint_allowed(cx, CARGO_COMMON_METADATA, CRATE_HIR_ID) {
            return;
        }

        let metadata = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false);

        for package in metadata.packages {
            // only run the lint if publish is `None` (`publish = true` or skipped entirely)
            // or if the vector isn't empty (`publish = ["something"]`)
            if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || self.ignore_publish {
                if is_empty_str(&package.description) {
                    missing_warning(cx, &package, "package.description");
                }

                if is_empty_str(&package.license) && is_empty_path(&package.license_file) {
                    missing_warning(cx, &package, "either package.license or package.license_file");
                }

                if is_empty_str(&package.repository) {
                    missing_warning(cx, &package, "package.repository");
                }

                if is_empty_path(&package.readme) {
                    missing_warning(cx, &package, "package.readme");
                }

                if is_empty_vec(&package.keywords) {
                    missing_warning(cx, &package, "package.keywords");
                }

                if is_empty_vec(&package.categories) {
                    missing_warning(cx, &package, "package.categories");
                }
            }
        }
    }
}
<commit_msg>Prep for upgrade to cargo_metadata 0.14.0<commit_after>//! lint on missing cargo common metadata

use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::DUMMY_SP;

declare_clippy_lint! {
    /// ### What it does
    /// Checks to see if all common metadata is defined in
    /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
    ///
    /// ### Why is this bad?
    /// It will be more difficult for users to discover the
    /// purpose of the crate, and key information related to it.
    ///
    /// ### Example
    /// ```toml
    /// # This `Cargo.toml` is missing a description field:
    /// [package]
    /// name = "clippy"
    /// version = "0.0.212"
    /// repository = "https://github.com/rust-lang/rust-clippy"
    /// readme = "README.md"
    /// license = "MIT OR Apache-2.0"
    /// keywords = ["clippy", "lint", "plugin"]
    /// categories = ["development-tools", "development-tools::cargo-plugins"]
    /// ```
    ///
    /// Should include a description field like:
    ///
    /// ```toml
    /// # This `Cargo.toml` includes all common metadata
    /// [package]
    /// name = "clippy"
    /// version = "0.0.212"
    /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
    /// repository = "https://github.com/rust-lang/rust-clippy"
    /// readme = "README.md"
    /// license = "MIT OR Apache-2.0"
    /// keywords = ["clippy", "lint", "plugin"]
    /// categories = ["development-tools", "development-tools::cargo-plugins"]
    /// ```
    pub CARGO_COMMON_METADATA,
    cargo,
    "common metadata is defined in `Cargo.toml`"
}

#[derive(Copy, Clone, Debug)]
pub struct CargoCommonMetadata {
    ignore_publish: bool,
}

impl CargoCommonMetadata {
    pub fn new(ignore_publish: bool) -> Self {
        Self { ignore_publish }
    }
}

impl_lint_pass!(CargoCommonMetadata => [
    CARGO_COMMON_METADATA
]);

fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) {
    let message = format!("package `{}` is missing `{}` metadata", package.name, field);
    span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message);
}

fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: &Option<T>) -> bool {
    value.as_ref().map_or(true, |s| s.as_ref().is_empty())
}

fn is_empty_vec(value: &[String]) -> bool {
    // This works because empty iterators return true
    value.iter().all(String::is_empty)
}

impl LateLintPass<'_> for CargoCommonMetadata {
    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
        if is_lint_allowed(cx, CARGO_COMMON_METADATA, CRATE_HIR_ID) {
            return;
        }

        let metadata = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false);

        for package in metadata.packages {
            // only run the lint if publish is `None` (`publish = true` or skipped entirely)
            // or if the vector isn't empty (`publish = ["something"]`)
            if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || self.ignore_publish {
                if is_empty_str(&package.description) {
                    missing_warning(cx, &package, "package.description");
                }

                if is_empty_str(&package.license) && is_empty_str(&package.license_file) {
                    missing_warning(cx, &package, "either package.license or package.license_file");
                }

                if is_empty_str(&package.repository) {
                    missing_warning(cx, &package, "package.repository");
                }

                if is_empty_str(&package.readme) {
                    missing_warning(cx, &package, "package.readme");
                }

                if is_empty_vec(&package.keywords) {
                    missing_warning(cx, &package, "package.keywords");
                }

                if is_empty_vec(&package.categories) {
                    missing_warning(cx, &package, "package.categories");
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;

use glium::Surface;

mod support;

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
}

implement_vertex!(Vertex, position);

#[test]
fn uniforms_storage_single_value() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color;

            void main() {
                gl_FragColor = color;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color", [1.0, 0.0, 0.0, 0.5f32]);

    let texture = support::build_renderable_texture(&display);
    texture.as_surface().clear_color(0.0, 0.0, 0.0, 0.0);
    texture.as_surface().draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();

    let data: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();
    assert_eq!(data[0][0], (255, 0, 0, 128));
    assert_eq!(data.last().unwrap().last().unwrap(), &(255, 0, 0, 128));

    display.assert_no_error(None);
}

#[test]
fn uniforms_storage_multiple_values() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color1;
            uniform vec4 color2;

            void main() {
                gl_FragColor = color1 + color2;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color1", [0.7, 0.0, 0.0, 0.5f32]);
    let uniforms = uniforms.add("color2", [0.3, 0.0, 0.0, 0.5f32]);

    let texture = support::build_renderable_texture(&display);
    texture.as_surface().clear_color(0.0, 0.0, 0.0, 0.0);
    texture.as_surface().draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();

    let data: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();
    assert_eq!(data[0][0], (255, 0, 0, 255));
    assert_eq!(data.last().unwrap().last().unwrap(), &(255, 0, 0, 255));

    display.assert_no_error(None);
}

#[test]
fn uniforms_storage_ignore_inactive_uniforms() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color;

            void main() {
                gl_FragColor = color;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color", [1.0, 0.0, 0.0, 0.5f32]);
    let uniforms = uniforms.add("color2", 0.8f32);
    let uniforms = uniforms.add("color3", [0.1, 1.2f32]);

    let texture = support::build_renderable_texture(&display);
    texture.as_surface().clear_color(0.0, 0.0, 0.0, 0.0);
    texture.as_surface().draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();

    let data: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();
    assert_eq!(data[0][0], (255, 0, 0, 128));
    assert_eq!(data.last().unwrap().last().unwrap(), &(255, 0, 0, 128));

    display.assert_no_error(None);
}

#[test]
fn uniform_wrong_type() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color;

            void main() {
                gl_FragColor = color;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color", 1.0f32);

    let mut target = display.draw();
    target.clear_color(0.0, 0.0, 0.0, 0.0);
    match target.draw(&vb, &ib, &program, &uniforms, &Default::default()) {
        Err(glium::DrawError::UniformTypeMismatch { .. }) => (),
        a => panic!("{:?}", a)
    };
    target.finish().unwrap();

    display.assert_no_error(None);
}
<commit_msg>Add range of tests for simple uniform types<commit_after>#[macro_use]
extern crate glium;

use glium::Surface;

mod support;

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
}

implement_vertex!(Vertex, position);

#[test]
fn uniforms_storage_single_value() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color;

            void main() {
                gl_FragColor = color;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color", [1.0, 0.0, 0.0, 0.5f32]);

    let texture = support::build_renderable_texture(&display);
    texture.as_surface().clear_color(0.0, 0.0, 0.0, 0.0);
    texture.as_surface().draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();

    let data: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();
    assert_eq!(data[0][0], (255, 0, 0, 128));
    assert_eq!(data.last().unwrap().last().unwrap(), &(255, 0, 0, 128));

    display.assert_no_error(None);
}

#[test]
fn uniforms_storage_multiple_values() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color1;
            uniform vec4 color2;

            void main() {
                gl_FragColor = color1 + color2;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color1", [0.7, 0.0, 0.0, 0.5f32]);
    let uniforms = uniforms.add("color2", [0.3, 0.0, 0.0, 0.5f32]);

    let texture = support::build_renderable_texture(&display);
    texture.as_surface().clear_color(0.0, 0.0, 0.0, 0.0);
    texture.as_surface().draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();

    let data: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();
    assert_eq!(data[0][0], (255, 0, 0, 255));
    assert_eq!(data.last().unwrap().last().unwrap(), &(255, 0, 0, 255));

    display.assert_no_error(None);
}

#[test]
fn uniforms_storage_ignore_inactive_uniforms() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color;

            void main() {
                gl_FragColor = color;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color", [1.0, 0.0, 0.0, 0.5f32]);
    let uniforms = uniforms.add("color2", 0.8f32);
    let uniforms = uniforms.add("color3", [0.1, 1.2f32]);

    let texture = support::build_renderable_texture(&display);
    texture.as_surface().clear_color(0.0, 0.0, 0.0, 0.0);
    texture.as_surface().draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();

    let data: Vec<Vec<(u8, u8, u8, u8)>> = texture.read();
    assert_eq!(data[0][0], (255, 0, 0, 128));
    assert_eq!(data.last().unwrap().last().unwrap(), &(255, 0, 0, 128));

    display.assert_no_error(None);
}

#[test]
fn uniform_wrong_type() {
    let display = support::build_display();
    let (vb, ib) = support::build_rectangle_vb_ib(&display);

    let program = glium::Program::from_source(&display,
        "
            #version 110

            attribute vec2 position;

            void main() {
                gl_Position = vec4(position, 0.0, 1.0);
            }
        ",
        "
            #version 110

            uniform vec4 color;

            void main() {
                gl_FragColor = color;
            }
        ",
        None).unwrap();

    let uniforms = glium::uniforms::UniformsStorage::new("color", 1.0f32);

    let mut target = display.draw();
    target.clear_color(0.0, 0.0, 0.0, 0.0);
    match target.draw(&vb, &ib, &program, &uniforms, &Default::default()) {
        Err(glium::DrawError::UniformTypeMismatch { .. }) => (),
        a => panic!("{:?}", a)
    };
    target.finish().unwrap();

    display.assert_no_error(None);
}

macro_rules! uniform_test(
    ($name:ident, $glsl_ty:expr, $value:expr) => (
        #[test]
        fn $name() {
            let display = support::build_display();
            let (vb, ib) = support::build_rectangle_vb_ib(&display);
            // TODO Supply #version 400 for doubles and #version 100 for fallback (but uint needs 130 I believe)
            let program = glium::Program::from_source(&display,
                &format!("
                    #version 130
                    attribute vec2 position;
                    uniform {} my_uniform;
                    void main() {{
                        gl_Position = vec4(position, 0.0, 1.0);
                    }}
                ", $glsl_ty),
                &format!("
                    #version 130
                    uniform {} my_uniform;
                    void main() {{
                        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
                    }}
                ", $glsl_ty),
                None).unwrap();

            let uniforms = glium::uniforms::UniformsStorage::new("my_uniform", $value);
            let mut target = display.draw();
            target.clear_color(0.0, 0.0, 0.0, 0.0);
            target.draw(&vb, &ib, &program, &uniforms, &Default::default()).unwrap();
            target.finish().unwrap();
            display.assert_no_error(None);
        }
    )
);

// Floats
uniform_test!(uniform_type_f32_float, "float", 12.567f32);
uniform_test!(uniform_type_f32arr_floatvec2, "vec2", [1.0f32, 2.4]);
uniform_test!(uniform_type_f32tup_floatvec2, "vec2", (1.0f32, 2.4));
uniform_test!(uniform_type_f32arr_floatvec3, "vec3", [1.0f32, 2.4, 0.0003]);
uniform_test!(uniform_type_f32tup_floatvec3, "vec3", (1.0f32, 2.4, 0.0003));
uniform_test!(uniform_type_f32arr_floatvec4, "vec4", [1.0f32, 2.4, 0.0003, 123456.55]);
uniform_test!(uniform_type_f32tup_floatvec4, "vec4", (1.0f32, 2.4, 0.0003, 123456.55));
uniform_test!(uniform_type_f32arr_floatmat2, "mat2", [[1.0f32, 2.4], [-2.0f32, -7.8867]]);
uniform_test!(uniform_type_f32arr_floatmat3, "mat3", [[ 1.0f32,     2.4, -1000000.0],
                                                      [-2.0f32, -7.8867,     6.6666],
                                                      [ 1.1f32,     7.7,       -6.1]]);
uniform_test!(uniform_type_f32arr_floatmat4, "mat4", [[ 1.0f32,     2.4, -1000000.0,  0.0],
                                                      [-2.0f32, -7.8867,     6.6666, -0.0],
                                                      [ 1.1f32,     7.7,       -6.1,  0.0],
                                                      [12.0f32, 12345.0,    0.11111,  0.0]]);

// Doubles
// TODO these need at least version 400 shaders to compile.
// These get ignored until double precision uniforms are implemented.
// uniform_test!(uniform_type_f64_double, "double", 12.567f64);
// uniform_test!(uniform_type_f64arr_doublevec2, "dvec2", [1.0f64, 2.4]);
// uniform_test!(uniform_type_f64tup_doublevec2, "dvec2", (1.0f64, 2.4));
// uniform_test!(uniform_type_f64arr_doublevec3, "dvec3", [1.0f64, 2.4, 0.0003]);
// uniform_test!(uniform_type_f64tup_doublevec3, "dvec3", (1.0f64, 2.4, 0.0003));
// uniform_test!(uniform_type_f64arr_doublevec4, "dvec4", [1.0f64, 2.4, 0.0003, 123456]);
// uniform_test!(uniform_type_f64tup_doublevec4, "dvec4", (1.0f64, 2.4, 0.0003, 123456));
// uniform_test!(uniform_type_f64arr_doublemat2, "dmat2", [[1.0f64, 2.4], [-2.0f64, -7.8867]]);
// uniform_test!(uniform_type_f64arr_doublemat3, "dmat3", [[ 1.0f64,     2.4, -1000000.0],
//                                                         [-2.0f64, -7.8867,     6.6666],
//                                                         [ 1.1f64,     7.7,       -6.1]]);
// uniform_test!(uniform_type_f64arr_doublemat4, "dmat4", [[ 1.0f64,     2.4, -1000000.0,  0.0],
//                                                         [-2.0f64, -7.8867,     6.6666, -0.0],
//                                                         [ 1.1f64,     7.7,       -6.1,  0.0],
//                                                         [12.0f64, 12345.0,    0.11111,  0.0]]);

// Integer
uniform_test!(uniform_type_i32_int, "int", 12i32);
uniform_test!(uniform_type_i32arr_intvec2, "ivec2", [1i32, 24]);
uniform_test!(uniform_type_i32tup_intvec2, "ivec2", (1i32, 24));
uniform_test!(uniform_type_i32arr_intvec3, "ivec3", [1i32, 24, -7]);
uniform_test!(uniform_type_i32tup_intvec3, "ivec3", (1i32, 24, -7));
uniform_test!(uniform_type_i32arr_intvec4, "ivec4", [1i32, 24, -7, -123456]);
uniform_test!(uniform_type_i32tup_intvec4, "ivec4", (1i32, 24, -7, -123456));

// Unsigned integer
uniform_test!(uniform_type_u32_uint, "uint", 12u32);
uniform_test!(uniform_type_u32arr_uintvec2, "uvec2", [1u32, 24]);
uniform_test!(uniform_type_u32tup_uintvec2, "uvec2", (1u32, 24));
uniform_test!(uniform_type_u32arr_uintvec3, "uvec3", [1u32, 24, 7]);
uniform_test!(uniform_type_u32tup_uintvec3, "uvec3", (1u32, 24, 7));
uniform_test!(uniform_type_u32arr_uintvec4, "uvec4", [1u32, 24, 7, 123456]);
uniform_test!(uniform_type_u32tup_uintvec4, "uvec4", (1u32, 24, 7, 123456));

// Booleans
uniform_test!(uniform_type_bool_bool, "bool", true);
uniform_test!(uniform_type_boolarr_boolvec2, "bvec2", [true, false]);
uniform_test!(uniform_type_booltup_boolvec2, "bvec2", (false, true));
uniform_test!(uniform_type_boolarr_boolvec3, "bvec3", [true, true, true]);
uniform_test!(uniform_type_booltup_boolvec3, "bvec3", (false, false, false));
uniform_test!(uniform_type_boolarr_boolvec4, "bvec4", [true, false, false, true]);
uniform_test!(uniform_type_booltup_boolvec4, "bvec4", (false, true, true, false));
<|endoftext|>


<commit_before>use crate::conn;
use libtiny_tui::test_utils::expect_screen;
use libtiny_tui::{TUI, UI};
use libtiny_wire::{Cmd, Msg, MsgTarget, Pfx};
use term_input;
use termbox_simple::CellBuf;

use libtiny_client as client;
use term_input as input;

use tokio::stream::StreamExt;
use tokio::sync::mpsc;

use std::future::Future;
use std::panic::Location;

struct TestClient {
    nick: String,
}

impl conn::Client for TestClient {
    fn get_serv_name(&self) -> &str {
        SERV_NAME
    }

    fn get_nick(&self) -> String {
        self.nick.clone()
    }

    fn is_nick_accepted(&self) -> bool {
        true
    }
}

static SERV_NAME: &str = "x.y.z";
const DEFAULT_TUI_WIDTH: u16 = 40;
const DEFAULT_TUI_HEIGHT: u16 = 5;

struct TestSetup {
    /// TUI test instance
    tui: Box<TUI>,
    /// Send input events to the TUI using this channel
    snd_input_ev: mpsc::Sender<input::Event>,
    /// Send connection events to connection handler (`conn::task`) using this channel
    snd_conn_ev: mpsc::Sender<client::Event>,
}

fn run_test<F, Fut>(nick: String, test: F)
where
    F: Fn(TestSetup) -> Fut,
    Fut: Future<Output = ()>,
{
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap();
    let local = tokio::task::LocalSet::new();

    local.block_on(&runtime, async move {
        // Create test TUI
        let (snd_input_ev, rcv_input_ev) = mpsc::channel::<term_input::Event>(100);
        let (tui, _rcv_tui_ev) = TUI::run_test(
            DEFAULT_TUI_WIDTH,
            DEFAULT_TUI_HEIGHT,
            rcv_input_ev.map(|ev| Ok(ev)),
        );

        let tui = Box::new(tui);

        // Create test connection event channel
        let (snd_conn_ev, rcv_conn_ev) = mpsc::channel::<client::Event>(100);

        // Spawn connection event handler task
        tokio::task::spawn_local(conn::task(
            rcv_conn_ev,
            tui.clone(),
            Box::new(TestClient { nick }),
        ));

        tui.new_server_tab(SERV_NAME, None);
        tui.draw();

        test(TestSetup {
            tui,
            snd_input_ev,
            snd_conn_ev,
        })
        .await;
    });
}

#[test]
fn test_bouncer_relay_issue_271() {
    run_test(
        "osa1-soju".to_owned(),
        |TestSetup {
             tui,
             snd_input_ev,
             snd_conn_ev,
         }| async move {
            snd_conn_ev.send(client::Event::Connected).await.unwrap();
            snd_conn_ev
                .send(client::Event::NickChange {
                    new_nick: "osa1-soju".to_owned(),
                })
                .await
                .unwrap();

            let msg = Msg {
                pfx: Some(Pfx::User {
                    nick: "osa1-soju".to_owned(),
                    user: "osa1-soju@127.0.0.1".to_owned(),
                }),
                cmd: Cmd::PRIVMSG {
                    target: MsgTarget::User("osa1/oftc".to_owned()),
                    msg: "blah blah".to_owned(),
                    is_notice: false,
                    ctcp: None,
                },
            };

            snd_conn_ev.send(client::Event::Msg(msg)).await.unwrap();

            yield_(5).await;
            tui.draw();

            next_tab(&snd_input_ev).await; // server tab
            next_tab(&snd_input_ev).await; // privmsg tab
            yield_(5).await;

            tui.draw();

            #[rustfmt::skip]
            let screen =
            "|                                        |
             |                                        |
             |00:00 osa1-soju: blah blah              |
             |osa1-soju:                              |
             |mentions x.y.z osa1/oftc                |";

            let mut front_buffer = tui.get_front_buffer();
            normalize_timestamps(&mut front_buffer, DEFAULT_TUI_WIDTH, DEFAULT_TUI_HEIGHT);
            expect_screen(
                screen,
                &front_buffer,
                DEFAULT_TUI_WIDTH,
                DEFAULT_TUI_HEIGHT,
                Location::caller(),
            );
        },
    );
}

#[test]
fn test_privmsg_targetmask_issue_278() {
    run_test(
        "osa1".to_owned(),
        |TestSetup {
             tui,
             snd_input_ev,
             snd_conn_ev,
         }| async move {
            next_tab(&snd_input_ev).await;
            snd_conn_ev.send(client::Event::Connected).await.unwrap();
            snd_conn_ev
                .send(client::Event::NickChange {
                    new_nick: "osa1".to_owned(),
                })
                .await
                .unwrap();

            snd_conn_ev
                .send(client::Event::Msg(Msg {
                    pfx: Some(Pfx::User {
                        nick: "e".to_owned(),
                        user: "e@a/b/c.d".to_owned(),
                    }),
                    cmd: Cmd::PRIVMSG {
                        target: MsgTarget::User("$$*".to_owned()),
                        msg: "blah blah blah".to_owned(),
                        is_notice: true,
                        ctcp: None,
                    },
                }))
                .await
                .unwrap();

            yield_(3).await;

            next_tab(&snd_input_ev).await;

            tui.draw();

            yield_(3).await;

            #[rustfmt::skip]
            let screen =
            "|                                        |
             |                                        |
             |00:00 e: blah blah blah                 |
             |osa1:                                   |
             |mentions x.y.z e                        |";

            let mut front_buffer = tui.get_front_buffer();
            normalize_timestamps(&mut front_buffer, DEFAULT_TUI_WIDTH, DEFAULT_TUI_HEIGHT);
            expect_screen(
                screen,
                &front_buffer,
                DEFAULT_TUI_WIDTH,
                DEFAULT_TUI_HEIGHT,
                Location::caller(),
            );
        },
    );
}

async fn next_tab(snd_input_ev: &mpsc::Sender<input::Event>) {
    snd_input_ev
        .send(term_input::Event::Key(term_input::Key::Ctrl('n')))
        .await
        .unwrap();
}

async fn yield_(n: usize) {
    for _ in 0..n {
        tokio::task::yield_now().await;
    }
}

/// Makes all timestamps 00:00
fn normalize_timestamps(cells: &mut CellBuf, w: u16, h: u16) {
    let cells = &mut cells.cells;
    for y in 0..h {
        let x = (w * y) as usize;
        if cells[x].ch.is_ascii_digit()
            && cells[x + 1].ch.is_ascii_digit()
            && cells[x + 2].ch == ':'
            && cells[x + 3].ch.is_ascii_digit()
            && cells[x + 4].ch.is_ascii_digit()
        {
            cells[x].ch = '0';
            cells[x + 1].ch = '0';
            cells[x + 2].ch = ':';
            cells[x + 3].ch = '0';
            cells[x + 4].ch = '0';
        }
    }
}
<commit_msg>Add test for #247<commit_after>use crate::conn;
use libtiny_common::ChanName;
use libtiny_tui::test_utils::expect_screen;
use libtiny_tui::{TUI, UI};
use libtiny_wire::{Cmd, Msg, MsgTarget, Pfx};
use term_input;
use termbox_simple::CellBuf;

use libtiny_client as client;
use term_input as input;

use tokio::stream::StreamExt;
use tokio::sync::mpsc;

use std::future::Future;
use std::panic::Location;

struct TestClient {
    nick: String,
}

impl conn::Client for TestClient {
    fn get_serv_name(&self) -> &str {
        SERV_NAME
    }

    fn get_nick(&self) -> String {
        self.nick.clone()
    }

    fn is_nick_accepted(&self) -> bool {
        true
    }
}

static SERV_NAME: &str = "x.y.z";
const DEFAULT_TUI_WIDTH: u16 = 40;
const DEFAULT_TUI_HEIGHT: u16 = 5;

struct TestSetup {
    /// TUI test instance
    tui: Box<TUI>,
    /// Send input events to the TUI using this channel
    snd_input_ev: mpsc::Sender<input::Event>,
    /// Send connection events to connection handler (`conn::task`) using this channel
    snd_conn_ev: mpsc::Sender<client::Event>,
}

fn run_test<F, Fut>(nick: String, test: F)
where
    F: Fn(TestSetup) -> Fut,
    Fut: Future<Output = ()>,
{
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap();
    let local = tokio::task::LocalSet::new();

    local.block_on(&runtime, async move {
        // Create test TUI
        let (snd_input_ev, rcv_input_ev) = mpsc::channel::<term_input::Event>(100);
        let (tui, _rcv_tui_ev) = TUI::run_test(
            DEFAULT_TUI_WIDTH,
            DEFAULT_TUI_HEIGHT,
            rcv_input_ev.map(|ev| Ok(ev)),
        );

        let tui = Box::new(tui);

        // Create test connection event channel
        let (snd_conn_ev, rcv_conn_ev) = mpsc::channel::<client::Event>(100);

        // Spawn connection event handler task
        tokio::task::spawn_local(conn::task(
            rcv_conn_ev,
            tui.clone(),
            Box::new(TestClient { nick }),
        ));

        tui.new_server_tab(SERV_NAME, None);
        tui.draw();

        test(TestSetup {
            tui,
            snd_input_ev,
            snd_conn_ev,
        })
        .await;
    });
}

#[test]
fn test_privmsg_from_user_without_user_or_host_part_issue_247() {
    run_test(
        "osa1".to_owned(),
        |TestSetup {
             tui,
             snd_input_ev,
             snd_conn_ev,
         }| async move {
            snd_conn_ev.send(client::Event::Connected).await.unwrap();
            snd_conn_ev
                .send(client::Event::NickChange {
                    new_nick: "osa1".to_owned(),
                })
                .await
                .unwrap();
            yield_(5).await;

            // Join a channel to test msg sent to channel
            let join = Msg {
                pfx: Some(Pfx::User {
                    nick: "osa1".to_owned(),
                    user: "a@b".to_owned(),
                }),
                cmd: Cmd::JOIN {
                    chan: ChanName::new("#chan".to_owned()),
                },
            };
            snd_conn_ev.send(client::Event::Msg(join)).await.unwrap();
            yield_(5).await;

            // Send a PRIVMSG to the channel
            let chan_msg = Msg {
                pfx: Some(Pfx::Ambiguous("blah".to_owned())),
                cmd: Cmd::PRIVMSG {
                    target: MsgTarget::Chan(ChanName::new("#chan".to_owned())),
                    msg: "msg to chan".to_owned(),
                    is_notice: false,
                    ctcp: None,
                },
            };
            snd_conn_ev
                .send(client::Event::Msg(chan_msg))
                .await
                .unwrap();
            yield_(5).await;

            // Send a PRIVMSG to current nick
            let msg = Msg {
                pfx: Some(Pfx::Ambiguous("blah".to_owned())),
                cmd: Cmd::PRIVMSG {
                    target: MsgTarget::User("osa1".to_owned()),
                    msg: "msg to user".to_owned(),
                    is_notice: false,
                    ctcp: None,
                },
            };
            snd_conn_ev.send(client::Event::Msg(msg)).await.unwrap();
            yield_(5).await;

            // Check channel tab
            next_tab(&snd_input_ev).await; // server tab
            next_tab(&snd_input_ev).await; // channel tab
            yield_(5).await;
            tui.draw();

            #[rustfmt::skip]
            let screen =
            "|                                        |
             |                                        |
             |00:00 blah: msg to chan                 |
             |osa1:                                   |
             |mentions x.y.z #chan blah               |";

            let mut front_buffer = tui.get_front_buffer();
            normalize_timestamps(&mut front_buffer, DEFAULT_TUI_WIDTH, DEFAULT_TUI_HEIGHT);
            expect_screen(
                screen,
                &front_buffer,
                DEFAULT_TUI_WIDTH,
                DEFAULT_TUI_HEIGHT,
                Location::caller(),
            );

            // Check privmsg tab
            next_tab(&snd_input_ev).await; // privmsg tab
            yield_(5).await;
            tui.draw();

            #[rustfmt::skip]
            let screen =
            "|                                        |
             |                                        |
             |00:00 blah: msg to user                 |
             |osa1:                                   |
             |mentions x.y.z #chan blah               |";

            let mut front_buffer = tui.get_front_buffer();
            normalize_timestamps(&mut front_buffer, DEFAULT_TUI_WIDTH, DEFAULT_TUI_HEIGHT);
            expect_screen(
                screen,
                &front_buffer,
                DEFAULT_TUI_WIDTH,
                DEFAULT_TUI_HEIGHT,
                Location::caller(),
            );
        },
    )
}

#[test]
fn test_bouncer_relay_issue_271() {
    run_test(
        "osa1-soju".to_owned(),
        |TestSetup {
             tui,
             snd_input_ev,
             snd_conn_ev,
         }| async move {
            snd_conn_ev.send(client::Event::Connected).await.unwrap();
            snd_conn_ev
                .send(client::Event::NickChange {
                    new_nick: "osa1-soju".to_owned(),
                })
                .await
                .unwrap();

            let msg = Msg {
                pfx: Some(Pfx::User {
                    nick: "osa1-soju".to_owned(),
                    user: "osa1-soju@127.0.0.1".to_owned(),
                }),
                cmd: Cmd::PRIVMSG {
                    target: MsgTarget::User("osa1/oftc".to_owned()),
                    msg: "blah blah".to_owned(),
                    is_notice: false,
                    ctcp: None,
                },
            };

            snd_conn_ev.send(client::Event::Msg(msg)).await.unwrap();

            yield_(5).await;
            tui.draw();

            next_tab(&snd_input_ev).await; // server tab
            next_tab(&snd_input_ev).await; // privmsg tab
            yield_(5).await;

            tui.draw();

            #[rustfmt::skip]
            let screen =
            "|                                        |
             |                                        |
             |00:00 osa1-soju: blah blah              |
             |osa1-soju:                              |
             |mentions x.y.z osa1/oftc                |";

            let mut front_buffer = tui.get_front_buffer();
            normalize_timestamps(&mut front_buffer, DEFAULT_TUI_WIDTH, DEFAULT_TUI_HEIGHT);
            expect_screen(
                screen,
                &front_buffer,
                DEFAULT_TUI_WIDTH,
                DEFAULT_TUI_HEIGHT,
                Location::caller(),
            );
        },
    )
}

#[test]
fn test_privmsg_targetmask_issue_278() {
    run_test(
        "osa1".to_owned(),
        |TestSetup {
             tui,
             snd_input_ev,
             snd_conn_ev,
         }| async move {
            next_tab(&snd_input_ev).await;
            snd_conn_ev.send(client::Event::Connected).await.unwrap();
            snd_conn_ev
                .send(client::Event::NickChange {
                    new_nick: "osa1".to_owned(),
                })
                .await
                .unwrap();

            snd_conn_ev
                .send(client::Event::Msg(Msg {
                    pfx: Some(Pfx::User {
                        nick: "e".to_owned(),
                        user: "e@a/b/c.d".to_owned(),
                    }),
                    cmd: Cmd::PRIVMSG {
                        target: MsgTarget::User("$$*".to_owned()),
                        msg: "blah blah blah".to_owned(),
                        is_notice: true,
                        ctcp: None,
                    },
                }))
                .await
                .unwrap();

            yield_(3).await;

            next_tab(&snd_input_ev).await;

            tui.draw();

            yield_(3).await;

            #[rustfmt::skip]
            let screen =
            "|                                        |
             |                                        |
             |00:00 e: blah blah blah                 |
             |osa1:                                   |
             |mentions x.y.z e                        |";

            let mut front_buffer = tui.get_front_buffer();
            normalize_timestamps(&mut front_buffer, DEFAULT_TUI_WIDTH, DEFAULT_TUI_HEIGHT);
            expect_screen(
                screen,
                &front_buffer,
                DEFAULT_TUI_WIDTH,
                DEFAULT_TUI_HEIGHT,
                Location::caller(),
            );
        },
    )
}

async fn next_tab(snd_input_ev: &mpsc::Sender<input::Event>) {
    snd_input_ev
        .send(term_input::Event::Key(term_input::Key::Ctrl('n')))
        .await
        .unwrap();
}

async fn yield_(n: usize) {
    for _ in 0..n {
        tokio::task::yield_now().await;
    }
}

/// Makes all timestamps 00:00
fn normalize_timestamps(cells: &mut CellBuf, w: u16, h: u16) {
    let cells = &mut cells.cells;
    for y in 0..h {
        let x = (w * y) as usize;
        if cells[x].ch.is_ascii_digit()
            && cells[x + 1].ch.is_ascii_digit()
            && cells[x + 2].ch == ':'
            && cells[x + 3].ch.is_ascii_digit()
            && cells[x + 4].ch.is_ascii_digit()
        {
            cells[x].ch = '0';
            cells[x + 1].ch = '0';
            cells[x + 2].ch = ':';
            cells[x + 3].ch = '0';
            cells[x + 4].ch = '0';
        }
    }
}
<|endoftext|>


<commit_before>// use std::io::fs::{mkdir_recursive,rmdir_recursive};
use std;
use std::io;
use std::io::fs;
use std::io::process::{ProcessOutput};
use std::os;
use std::path::{Path,BytesContainer};
use std::str;
use std::vec::Vec;
use std::fmt::Show;
use ham = hamcrest;
use cargo::util::{process,ProcessBuilder};
use cargo::util::ProcessError;

pub mod paths;

/*
 *
 * ===== Builders =====
 *
 */

#[deriving(PartialEq,Clone)]
struct FileBuilder {
    path: Path,
    body: String
}

impl FileBuilder {
    pub fn new(path: Path, body: &str) -> FileBuilder {
        FileBuilder { path: path, body: body.to_str() }
    }

    fn mk(&self) -> Result<(), String> {
        try!(mkdir_recursive(&self.dirname()));

        let mut file = try!(
            fs::File::create(&self.path)
                .with_err_msg(format!("Could not create file; path={}",
                                      self.path.display())));

        file.write_str(self.body.as_slice())
            .with_err_msg(format!("Could not write to file; path={}",
                                  self.path.display()))
    }

    fn dirname(&self) -> Path {
        Path::new(self.path.dirname())
    }
}

#[deriving(PartialEq,Clone)]
pub struct ProjectBuilder {
    name: String,
    root: Path,
    files: Vec<FileBuilder>
}

impl ProjectBuilder {
    pub fn new(name: &str, root: Path) -> ProjectBuilder {
        ProjectBuilder {
            name: name.to_str(),
            root: root,
            files: vec!()
        }
    }

    pub fn root(&self) -> Path {
        self.root.clone()
    }

    pub fn bin(&self, b: &str) -> Path {
        self.root.join("target").join(format!("{}{}", b, os::consts::EXE_SUFFIX))
    }

    pub fn process<T: ToCStr>(&self, program: T) -> ProcessBuilder {
        process(program)
            .cwd(self.root())
            .env("HOME", Some(paths::home().display().to_str().as_slice()))
            .extra_path(cargo_dir())
    }

    pub fn cargo_process(&self, program: &str) -> ProcessBuilder {
        self.build();
        self.process(cargo_dir().join(program))
    }

    pub fn file<B: BytesContainer, S: Str>(mut self, path: B,
                                           body: S) -> ProjectBuilder {
        self.files.push(FileBuilder::new(self.root.join(path), body.as_slice()));
        self
    }

    // TODO: return something different than a ProjectBuilder
    pub fn build<'a>(&'a self) -> &'a ProjectBuilder {
        match self.build_with_result() {
            Err(e) => fail!(e),
            _ => return self
        }
    }

    pub fn build_with_result(&self) -> Result<(), String> {
        // First, clean the directory if it already exists
        try!(self.rm_root());

        // Create the empty directory
        try!(mkdir_recursive(&self.root));

        for file in self.files.iter() {
          try!(file.mk());
        }

        Ok(())
    }

    fn rm_root(&self) -> Result<(), String> {
        if self.root.exists() {
            rmdir_recursive(&self.root)
        }
        else {
            Ok(())
        }
    }
}

// Generates a project layout
pub fn project(name: &str) -> ProjectBuilder {
    ProjectBuilder::new(name, paths::root().join(name))
}

// === Helpers ===

pub fn mkdir_recursive(path: &Path) -> Result<(), String> {
    fs::mkdir_recursive(path, io::UserDir)
        .with_err_msg(format!("could not create directory; path={}",
                              path.display()))
}

pub fn rmdir_recursive(path: &Path) -> Result<(), String> {
    fs::rmdir_recursive(path)
        .with_err_msg(format!("could not rm directory; path={}",
                              path.display()))
}

pub fn main_file<T: Str>(println: T, deps: &[&str]) -> String {
    let mut buf = String::new();

    for dep in deps.iter() {
        buf.push_str(format!("extern crate {};\n", dep).as_slice());
    }

    buf.push_str("fn main() { println!(");
    buf.push_str(println.as_slice());
    buf.push_str("); }\n");

    buf.to_str()
}

trait ErrMsg<T> {
    fn with_err_msg(self, val: String) -> Result<T, String>;
}

impl<T, E: Show> ErrMsg<T> for Result<T, E> {
    fn with_err_msg(self, val: String) -> Result<T, String> {
        match self {
            Ok(val) => Ok(val),
            Err(err) => Err(format!("{}; original={}", val, err))
        }
    }
}

// Path to cargo executables
pub fn cargo_dir() -> Path {
    os::getenv("CARGO_BIN_PATH").map(Path::new)
        .or_else(|| os::self_exe_path().map(|p| p.dir_path()))
        .unwrap_or_else(|| {
            fail!("CARGO_BIN_PATH wasn't set. Cannot continue running test")
        })
}

/// Returns an absolute path in the filesystem that `path` points to. The
/// returned path does not contain any symlinks in its hierarchy.
/*
 *
 * ===== Matchers =====
 *
 */

#[deriving(Clone)]
struct Execs {
    expect_stdout: Option<String>,
    expect_stdin: Option<String>,
    expect_stderr: Option<String>,
    expect_exit_code: Option<int>
}

impl Execs {

    pub fn with_stdout<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
        self.expect_stdout = Some(expected.to_str());
        self
    }

    pub fn with_stderr<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
        self.expect_stderr = Some(expected.to_str());
        self
    }

    pub fn with_status(mut ~self, expected: int) -> Box<Execs> {
        self.expect_exit_code = Some(expected);
        self
    }

    fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
        self.match_status(actual)
            .and(self.match_stdout(actual))
            .and(self.match_stderr(actual))
    }

    fn match_status(&self, actual: &ProcessOutput) -> ham::MatchResult {
        match self.expect_exit_code {
            None => ham::success(),
            Some(code) => {
                ham::expect(
                    actual.status.matches_exit_status(code),
                    format!("exited with {}\n--- stdout\n{}\n--- stderr\n{}",
                            actual.status,
                            str::from_utf8(actual.output.as_slice()),
                            str::from_utf8(actual.error.as_slice())))
            }
        }
    }

    fn match_stdout(&self, actual: &ProcessOutput) -> ham::MatchResult {
        self.match_std(self.expect_stdout.as_ref(), actual.output.as_slice(),
        "stdout", actual.error.as_slice())
    }

    fn match_stderr(&self, actual: &ProcessOutput) -> ham::MatchResult {
        self.match_std(self.expect_stderr.as_ref(), actual.error.as_slice(),
        "stderr", actual.output.as_slice())
    }

    fn match_std(&self, expected: Option<&String>, actual: &[u8],
                 description: &str, extra: &[u8]) -> ham::MatchResult {
        match expected.as_ref().map(|s| s.as_slice()) {
            None => ham::success(),
            Some(out) => {
                match str::from_utf8(actual) {
                    None => Err(format!("{} was not utf8 encoded", description)),
                    Some(actual) => {
                        ham::expect(actual == out,
                                    format!("{} was:\n\
                                            `{}`\n\n\
                                            expected:\n\
                                            `{}`\n\n\
                                            other output:\n\
                                            `{}`", description, actual, out,
                                            str::from_utf8_lossy(extra)))
                    }
                }
            }
        }
    }
}

impl ham::SelfDescribing for Execs {
    fn describe(&self) -> String {
        "execs".to_str()
    }
}

impl ham::Matcher<ProcessBuilder> for Execs {
    fn matches(&self, process: ProcessBuilder) -> ham::MatchResult {
        let res = process.exec_with_output();

        match res {
            Ok(out) => self.match_output(&out),
            Err(ProcessError { output: Some(ref out), .. }) => {
                self.match_output(out)
            }
            Err(e) => Err(format!("could not exec process {}: {}", process, e))
        }
    }
}

pub fn execs() -> Box<Execs> {
    box Execs {
        expect_stdout: None,
        expect_stderr: None,
        expect_stdin: None,
        expect_exit_code: None
    }
}

#[deriving(Clone)]
struct ShellWrites {
    expected: String
}

impl ham::SelfDescribing for ShellWrites {
    fn describe(&self) -> String {
        format!("`{}` written to the shell", self.expected)
    }
}

impl<'a> ham::Matcher<&'a [u8]> for ShellWrites {
    fn matches(&self, actual: &[u8])
        -> ham::MatchResult
    {
        println!("{}", actual);
        let actual = std::str::from_utf8_lossy(actual);
        let actual = actual.to_str();
        ham::expect(actual == self.expected, actual)
    }
}

pub fn shell_writes<T: Show>(string: T) -> Box<ShellWrites> {
    box ShellWrites { expected: string.to_str() }
}

pub trait ResultTest<T,E> {
    fn assert(self) -> T;
}

impl<T,E: Show> ResultTest<T,E> for Result<T,E> {
    fn assert(self) -> T {
        match self {
            Ok(val) => val,
            Err(err) => fail!("Result was error: {}", err)
        }
    }
}

impl<T> ResultTest<T,()> for Option<T> {
    fn assert(self) -> T {
        match self {
            Some(val) => val,
            None => fail!("Option was None")
        }
    }
}

pub trait Tap {
    fn tap(mut self, callback: |&mut Self|) -> Self;
}

impl<T> Tap for T {
    fn tap(mut self, callback: |&mut T|) -> T {
        callback(&mut self);
        self
    }
}

pub fn escape_path(p: &Path) -> String {
    p.display().to_str().as_slice().replace("\\", "\\\\")
}
<commit_msg>Ignore \r in output matching<commit_after>// use std::io::fs::{mkdir_recursive,rmdir_recursive};
use std;
use std::io;
use std::io::fs;
use std::io::process::{ProcessOutput};
use std::os;
use std::path::{Path,BytesContainer};
use std::str;
use std::vec::Vec;
use std::fmt::Show;
use ham = hamcrest;
use cargo::util::{process,ProcessBuilder};
use cargo::util::ProcessError;

pub mod paths;

/*
 *
 * ===== Builders =====
 *
 */

#[deriving(PartialEq,Clone)]
struct FileBuilder {
    path: Path,
    body: String
}

impl FileBuilder {
    pub fn new(path: Path, body: &str) -> FileBuilder {
        FileBuilder { path: path, body: body.to_str() }
    }

    fn mk(&self) -> Result<(), String> {
        try!(mkdir_recursive(&self.dirname()));

        let mut file = try!(
            fs::File::create(&self.path)
                .with_err_msg(format!("Could not create file; path={}",
                                      self.path.display())));

        file.write_str(self.body.as_slice())
            .with_err_msg(format!("Could not write to file; path={}",
                                  self.path.display()))
    }

    fn dirname(&self) -> Path {
        Path::new(self.path.dirname())
    }
}

#[deriving(PartialEq,Clone)]
pub struct ProjectBuilder {
    name: String,
    root: Path,
    files: Vec<FileBuilder>
}

impl ProjectBuilder {
    pub fn new(name: &str, root: Path) -> ProjectBuilder {
        ProjectBuilder {
            name: name.to_str(),
            root: root,
            files: vec!()
        }
    }

    pub fn root(&self) -> Path {
        self.root.clone()
    }

    pub fn bin(&self, b: &str) -> Path {
        self.root.join("target").join(format!("{}{}", b, os::consts::EXE_SUFFIX))
    }

    pub fn process<T: ToCStr>(&self, program: T) -> ProcessBuilder {
        process(program)
            .cwd(self.root())
            .env("HOME", Some(paths::home().display().to_str().as_slice()))
            .extra_path(cargo_dir())
    }

    pub fn cargo_process(&self, program: &str) -> ProcessBuilder {
        self.build();
        self.process(cargo_dir().join(program))
    }

    pub fn file<B: BytesContainer, S: Str>(mut self, path: B,
                                           body: S) -> ProjectBuilder {
        self.files.push(FileBuilder::new(self.root.join(path), body.as_slice()));
        self
    }

    // TODO: return something different than a ProjectBuilder
    pub fn build<'a>(&'a self) -> &'a ProjectBuilder {
        match self.build_with_result() {
            Err(e) => fail!(e),
            _ => return self
        }
    }

    pub fn build_with_result(&self) -> Result<(), String> {
        // First, clean the directory if it already exists
        try!(self.rm_root());

        // Create the empty directory
        try!(mkdir_recursive(&self.root));

        for file in self.files.iter() {
          try!(file.mk());
        }

        Ok(())
    }

    fn rm_root(&self) -> Result<(), String> {
        if self.root.exists() {
            rmdir_recursive(&self.root)
        }
        else {
            Ok(())
        }
    }
}

// Generates a project layout
pub fn project(name: &str) -> ProjectBuilder {
    ProjectBuilder::new(name, paths::root().join(name))
}

// === Helpers ===

pub fn mkdir_recursive(path: &Path) -> Result<(), String> {
    fs::mkdir_recursive(path, io::UserDir)
        .with_err_msg(format!("could not create directory; path={}",
                              path.display()))
}

pub fn rmdir_recursive(path: &Path) -> Result<(), String> {
    fs::rmdir_recursive(path)
        .with_err_msg(format!("could not rm directory; path={}",
                              path.display()))
}

pub fn main_file<T: Str>(println: T, deps: &[&str]) -> String {
    let mut buf = String::new();

    for dep in deps.iter() {
        buf.push_str(format!("extern crate {};\n", dep).as_slice());
    }

    buf.push_str("fn main() { println!(");
    buf.push_str(println.as_slice());
    buf.push_str("); }\n");

    buf.to_str()
}

trait ErrMsg<T> {
    fn with_err_msg(self, val: String) -> Result<T, String>;
}

impl<T, E: Show> ErrMsg<T> for Result<T, E> {
    fn with_err_msg(self, val: String) -> Result<T, String> {
        match self {
            Ok(val) => Ok(val),
            Err(err) => Err(format!("{}; original={}", val, err))
        }
    }
}

// Path to cargo executables
pub fn cargo_dir() -> Path {
    os::getenv("CARGO_BIN_PATH").map(Path::new)
        .or_else(|| os::self_exe_path().map(|p| p.dir_path()))
        .unwrap_or_else(|| {
            fail!("CARGO_BIN_PATH wasn't set. Cannot continue running test")
        })
}

/// Returns an absolute path in the filesystem that `path` points to. The
/// returned path does not contain any symlinks in its hierarchy.
/*
 *
 * ===== Matchers =====
 *
 */

#[deriving(Clone)]
struct Execs {
    expect_stdout: Option<String>,
    expect_stdin: Option<String>,
    expect_stderr: Option<String>,
    expect_exit_code: Option<int>
}

impl Execs {

    pub fn with_stdout<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
        self.expect_stdout = Some(expected.to_str());
        self
    }

    pub fn with_stderr<S: ToStr>(mut ~self, expected: S) -> Box<Execs> {
        self.expect_stderr = Some(expected.to_str());
        self
    }

    pub fn with_status(mut ~self, expected: int) -> Box<Execs> {
        self.expect_exit_code = Some(expected);
        self
    }

    fn match_output(&self, actual: &ProcessOutput) -> ham::MatchResult {
        self.match_status(actual)
            .and(self.match_stdout(actual))
            .and(self.match_stderr(actual))
    }

    fn match_status(&self, actual: &ProcessOutput) -> ham::MatchResult {
        match self.expect_exit_code {
            None => ham::success(),
            Some(code) => {
                ham::expect(
                    actual.status.matches_exit_status(code),
                    format!("exited with {}\n--- stdout\n{}\n--- stderr\n{}",
                            actual.status,
                            str::from_utf8(actual.output.as_slice()),
                            str::from_utf8(actual.error.as_slice())))
            }
        }
    }

    fn match_stdout(&self, actual: &ProcessOutput) -> ham::MatchResult {
        self.match_std(self.expect_stdout.as_ref(), actual.output.as_slice(),
        "stdout", actual.error.as_slice())
    }

    fn match_stderr(&self, actual: &ProcessOutput) -> ham::MatchResult {
        self.match_std(self.expect_stderr.as_ref(), actual.error.as_slice(),
        "stderr", actual.output.as_slice())
    }

    fn match_std(&self, expected: Option<&String>, actual: &[u8],
                 description: &str, extra: &[u8]) -> ham::MatchResult {
        match expected.as_ref().map(|s| s.as_slice()) {
            None => ham::success(),
            Some(out) => {
                match str::from_utf8(actual) {
                    None => Err(format!("{} was not utf8 encoded", description)),
                    Some(actual) => {
                        // Let's not deal with \r\n vs \n on windows...
                        let actual = actual.replace("\r", "");
                        ham::expect(actual.as_slice() == out,
                                    format!("{} was:\n\
                                            `{}`\n\n\
                                            expected:\n\
                                            `{}`\n\n\
                                            other output:\n\
                                            `{}`", description, actual, out,
                                            str::from_utf8_lossy(extra)))
                    }
                }
            }
        }
    }
}

impl ham::SelfDescribing for Execs {
    fn describe(&self) -> String {
        "execs".to_str()
    }
}

impl ham::Matcher<ProcessBuilder> for Execs {
    fn matches(&self, process: ProcessBuilder) -> ham::MatchResult {
        let res = process.exec_with_output();

        match res {
            Ok(out) => self.match_output(&out),
            Err(ProcessError { output: Some(ref out), .. }) => {
                self.match_output(out)
            }
            Err(e) => Err(format!("could not exec process {}: {}", process, e))
        }
    }
}

pub fn execs() -> Box<Execs> {
    box Execs {
        expect_stdout: None,
        expect_stderr: None,
        expect_stdin: None,
        expect_exit_code: None
    }
}

#[deriving(Clone)]
struct ShellWrites {
    expected: String
}

impl ham::SelfDescribing for ShellWrites {
    fn describe(&self) -> String {
        format!("`{}` written to the shell", self.expected)
    }
}

impl<'a> ham::Matcher<&'a [u8]> for ShellWrites {
    fn matches(&self, actual: &[u8])
        -> ham::MatchResult
    {
        println!("{}", actual);
        let actual = std::str::from_utf8_lossy(actual);
        let actual = actual.to_str();
        ham::expect(actual == self.expected, actual)
    }
}

pub fn shell_writes<T: Show>(string: T) -> Box<ShellWrites> {
    box ShellWrites { expected: string.to_str() }
}

pub trait ResultTest<T,E> {
    fn assert(self) -> T;
}

impl<T,E: Show> ResultTest<T,E> for Result<T,E> {
    fn assert(self) -> T {
        match self {
            Ok(val) => val,
            Err(err) => fail!("Result was error: {}", err)
        }
    }
}

impl<T> ResultTest<T,()> for Option<T> {
    fn assert(self) -> T {
        match self {
            Some(val) => val,
            None => fail!("Option was None")
        }
    }
}

pub trait Tap {
    fn tap(mut self, callback: |&mut Self|) -> Self;
}

impl<T> Tap for T {
    fn tap(mut self, callback: |&mut T|) -> T {
        callback(&mut self);
        self
    }
}

pub fn escape_path(p: &Path) -> String {
    p.display().to_str().as_slice().replace("\\", "\\\\")
}
<|endoftext|>


<commit_before>use super::*;
use redox::*;

impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let x = self.x();
        let y = self.y();
        // Redraw window
        self.window.set(Color::BLACK);

        self.window.rect(8 * (x - self.scroll_y) as isize,
                         16 * (y - self.scroll_x) as isize,
                         8,
                         16,
                         Color::WHITE);

        let mut string = false;

        for (y, row) in self.text.iter().enumerate() {
            for (x, &c) in row.iter().enumerate() {
                // TODO: Move outta here
                let color = match c {
                    '\'' | '"' => {
                        string = !string;
                        (226, 225, 167) //(167, 222, 156)
                    },
                    _ if string => (226, 225, 167), //(167, 222, 156)
                    '!' | '@' | '#' | '$' | '%' | '^' | '&' | '|' | '*' | '+' | '-' | '/' | ':' | '=' | '<' | '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                    '.' | ',' => (241, 213, 226),
                    '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                    '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => (209, 209, 177),
                    _ => (255, 255, 255),
                };

                if self.x() == x && self.y() == y {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }
        let h = self.window.height();
        let w = self.window.width();
        self.window.rect(0, h as isize - 18, w, 18, Color::rgba(74, 74, 74, 255));

        for (n, c) in (if self.status_bar.mode.len() > w / (8 * 4) {
            self.status_bar.mode.chars().take(w / (8 * 4) - 5).chain(vec!['.', '.', '.']).collect::<Vec<_>>()
        } else {
            self.status_bar.mode.chars().collect()
        }).into_iter().enumerate() {

            self.window.char(n as isize * 8, h as isize - 16 - 1, if c == '\t' { ' ' } else { c }, Color::WHITE);
        }

        self.window.sync();
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: String::new(),
        }
    }
}
<commit_msg>Change bg color<commit_after>use super::*;
use redox::*;

impl Editor {
    /// Redraw the window
    pub fn redraw(&mut self) {
        // TODO: Only draw when relevant for the window
        let x = self.x();
        let y = self.y();
        // Redraw window
        self.window.set(Color::rgb(25, 25, 25));

        self.window.rect(8 * (x - self.scroll_y) as isize,
                         16 * (y - self.scroll_x) as isize,
                         8,
                         16,
                         Color::WHITE);

        let mut string = false;

        for (y, row) in self.text.iter().enumerate() {
            for (x, &c) in row.iter().enumerate() {
                // TODO: Move outta here
                let color = match c {
                    '\'' | '"' => {
                        string = !string;
                        (226, 225, 167) //(167, 222, 156)
                    },
                    _ if string => (226, 225, 167), //(167, 222, 156)
                    '!' | '@' | '#' | '$' | '%' | '^' | '&' | '|' | '*' | '+' | '-' | '/' | ':' | '=' | '<' | '>' => (198, 83, 83), //(228, 190, 175), //(194, 106, 71),
                    '.' | ',' => (241, 213, 226),
                    '(' | ')' | '[' | ']' | '{' | '}' => (164, 212, 125), //(195, 139, 75),
                    '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => (209, 209, 177),
                    _ => (255, 255, 255),
                };

                if self.x() == x && self.y() == y {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0 / 3, color.1 / 3, color.2 / 3));
                } else {
                    self.window.char(8 * (x - self.scroll_y) as isize,
                                     16 * (y - self.scroll_x) as isize,
                                     c,
                                     Color::rgb(color.0, color.1, color.2));
                }
            }
        }
        let h = self.window.height();
        let w = self.window.width();
        self.window.rect(0, h as isize - 18, w, 18, Color::rgba(74, 74, 74, 255));

        for (n, c) in (if self.status_bar.mode.len() > w / (8 * 4) {
            self.status_bar.mode.chars().take(w / (8 * 4) - 5).chain(vec!['.', '.', '.']).collect::<Vec<_>>()
        } else {
            self.status_bar.mode.chars().collect()
        }).into_iter().enumerate() {

            self.window.char(n as isize * 8, h as isize - 16 - 1, if c == '\t' { ' ' } else { c }, Color::WHITE);
        }

        self.window.sync();
    }
}

/// The statubar (showing various info about the current state of the editor)
pub struct StatusBar {
    /// The current mode
    pub mode: String,
    /// The cureent char
    pub file: String,
    /// The current command
    pub cmd: String,
    /// A message (such as an error or other info to the user)
    pub msg: String,
}

impl StatusBar {
    /// Create new status bar
    pub fn new() -> Self {
        StatusBar {
            mode: "Normal".to_string(),
            file: String::new(),
            cmd: String::new(),
            msg: String::new(),
        }
    }
}
<|endoftext|>