<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
    fs::File,
    io::{Read, Write},
    panic::RefUnwindSafe,
    path::{Path, PathBuf},
    sync::Mutex,
};

use autocxx_engine::{Builder, BuilderContext, BuilderError, RebuildDependencyRecorder, HEADER};
use log::info;
use once_cell::sync::OnceCell;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{Item, Token};
use tempfile::{tempdir, TempDir};

const KEEP_TEMPDIRS: bool = false;

fn get_builder() -> &'static Mutex<LinkableTryBuilder> {
    static INSTANCE: OnceCell<Mutex<LinkableTryBuilder>> = OnceCell::new();
    INSTANCE.get_or_init(|| Mutex::new(LinkableTryBuilder::new()))
}

/// TryBuild which maintains a directory of libraries to link.
/// This is desirable because otherwise, if we alter the RUSTFLAGS
/// then trybuild rebuilds *everything* including all the dev-dependencies.
/// This object exists purely so that we use the same RUSTFLAGS for every
/// test case.
struct LinkableTryBuilder {
    /// Directory in which we'll keep any linkable libraries
    temp_dir: TempDir,
}

impl LinkableTryBuilder {
    fn new() -> Self {
        LinkableTryBuilder {
            temp_dir: tempdir().unwrap(),
        }
    }

    fn move_items_into_temp_dir<P1: AsRef<Path>>(&self, src_path: &P1, pattern: &str) {
        for item in std::fs::read_dir(src_path).unwrap() {
            let item = item.unwrap();
            if item.file_name().into_string().unwrap().contains(pattern) {
                let dest = self.temp_dir.path().join(item.file_name());
                if dest.exists() {
                    std::fs::remove_file(&dest).unwrap();
                }
                std::fs::rename(item.path(), dest).unwrap();
            }
        }
    }

    fn build<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path> + RefUnwindSafe>(
        &self,
        library_path: &P1,
        library_name: &str,
        header_path: &P2,
        header_names: &[&str],
        rs_path: &P3,
        generated_rs_files: Vec<PathBuf>,
    ) -> std::thread::Result<()> {
        // Copy all items from the source dir into our temporary dir if their name matches
        // the pattern given in `library_name`.
        self.move_items_into_temp_dir(library_path, library_name);
        for header_name in header_names {
            self.move_items_into_temp_dir(header_path, header_name);
        }
        for generated_rs in generated_rs_files {
            self.move_items_into_temp_dir(
                &generated_rs.parent().unwrap().to_path_buf(),
                &generated_rs.file_name().unwrap().to_str().unwrap(),
            );
        }
        let temp_path = self.temp_dir.path().to_str().unwrap();
        std::env::set_var("RUSTFLAGS", format!("-L {}", temp_path));
        std::env::set_var("AUTOCXX_RS", temp_path);
        std::panic::catch_unwind(|| {
            let test_cases = trybuild::TestCases::new();
            test_cases.pass(rs_path)
        })
    }
}

fn write_to_file(tdir: &TempDir, filename: &str, content: &str) -> PathBuf {
    let path = tdir.path().join(filename);
    let mut f = File::create(&path).unwrap();
    f.write_all(content.as_bytes()).unwrap();
    path
}

/// A positive test, we expect to pass.
pub(crate) fn run_test(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    generate: &[&str],
    generate_pods: &[&str],
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives_from_lists(generate, generate_pods, None),
        None,
        None,
        None,
    )
    .unwrap()
}

// A function applied to the resultant generated Rust code
// which can be used to inspect that code.
pub(crate) type RustCodeChecker = Box<dyn FnOnce(syn::File) -> Result<(), TestError>>;

// A function to modify Builders
pub(crate) type BuilderModifier =
    Box<dyn FnOnce(Builder<TestBuilderContext>) -> Builder<TestBuilderContext>>;

/// A positive test, we expect to pass.
#[allow(clippy::too_many_arguments)] // least typing for each test
pub(crate) fn run_test_ex(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    directives: TokenStream,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
    extra_rust: Option<TokenStream>,
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives,
        builder_modifier,
        rust_code_checker,
        extra_rust,
    )
    .unwrap()
}

pub(crate) fn run_test_expect_fail(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    generate: &[&str],
    generate_pods: &[&str],
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives_from_lists(generate, generate_pods, None),
        None,
        None,
        None,
    )
    .expect_err("Unexpected success");
}

pub(crate) fn run_test_expect_fail_ex(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    directives: TokenStream,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
    extra_rust: Option<TokenStream>,
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives,
        builder_modifier,
        rust_code_checker,
        extra_rust,
    )
    .expect_err("Unexpected success");
}

/// In the future maybe the tests will distinguish the exact type of failure expected.
#[derive(Debug)]
pub(crate) enum TestError {
    AutoCxx(BuilderError),
    CppBuild(cc::Error),
    RsBuild,
    NoRs,
    RsFileOpen(std::io::Error),
    RsFileRead(std::io::Error),
    RsFileParse(syn::Error),
    RsCodeExaminationFail,
}

pub(crate) fn directives_from_lists(
    generate: &[&str],
    generate_pods: &[&str],
    extra_directives: Option<TokenStream>,
) -> TokenStream {
    let generate = generate.iter().map(|s| {
        quote! {
            generate!(#s)
        }
    });
    let generate_pods = generate_pods.iter().map(|s| {
        quote! {
            generate_pod!(#s)
        }
    });
    quote! {
        #(#generate)*
        #(#generate_pods)*
        #extra_directives
    }
}

#[allow(clippy::too_many_arguments)] // least typing for each test
fn do_run_test(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    directives: TokenStream,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
    extra_rust: Option<TokenStream>,
) -> Result<(), TestError> {
    let hexathorpe = Token![#](Span::call_site());
    let unexpanded_rust = |hdr: &str| {
        quote! {
            use autocxx::include_cpp;

            include_cpp!(
                #hexathorpe include #hdr
                safety!(unsafe_ffi)
                #directives
            );

            #extra_rust

            fn main() {
                #rust_code
            }
        }
    };
    do_run_test_manual(
        cxx_code,
        header_code,
        unexpanded_rust,
        builder_modifier,
        rust_code_checker,
    )
}

pub(crate) struct TestBuilderContext;

impl BuilderContext for TestBuilderContext {
    fn get_dependency_recorder() -> Option<Box<dyn RebuildDependencyRecorder>> {
        None
    }
}

pub(crate) fn do_run_test_manual<F>(
    cxx_code: &str,
    header_code: &str,
    rust_code_generator: F,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
) -> Result<(), TestError>
where
    F: FnOnce(&'static str) -> TokenStream,
{
    const HEADER_NAME: &str = "input.h";
    let mut rust_code = rust_code_generator(HEADER_NAME);
    // Step 2: Write the C++ header snippet to a temp file
    let tdir = tempdir().unwrap();
    write_to_file(
        &tdir,
        HEADER_NAME,
        &format!("#pragma once\n{}", header_code),
    );
    write_to_file(&tdir, "cxx.h", HEADER);

    rust_code.append_all(quote! {
        #[link(name="autocxx-demo")]
        extern {}
    });
    info!("Unexpanded Rust: {}", rust_code);

    let write_rust_to_file = |ts: &TokenStream| -> PathBuf {
        // Step 3: Write the Rust code to a temp file
        let rs_code = format!("{}", ts);
        write_to_file(&tdir, "input.rs", &rs_code)
    };

    let target_dir = tdir.path().join("target");
    std::fs::create_dir(&target_dir).unwrap();

    let rs_path = write_rust_to_file(&rust_code);

    info!("Path is {:?}", tdir.path());
    let builder = Builder::<TestBuilderContext>::new(&rs_path, &[tdir.path()])
        .custom_gendir(target_dir.clone());
    let builder = if let Some(builder_modifier) = builder_modifier {
        builder_modifier(builder)
    } else {
        builder
    };
    let build_results = builder.build_listing_files().map_err(TestError::AutoCxx)?;
    let mut b = build_results.0;
    let generated_rs_files = build_results.1;

    if let Some(rust_code_checker) = rust_code_checker {
        let mut file = File::open(generated_rs_files.get(0).ok_or(TestError::NoRs)?)
            .map_err(TestError::RsFileOpen)?;
        let mut content = String::new();
        file.read_to_string(&mut content)
            .map_err(TestError::RsFileRead)?;

        let ast = syn::parse_file(&content).map_err(TestError::RsFileParse)?;
        rust_code_checker(ast)?;
    }

    let target = rust_info::get().target_triple.unwrap();

    if !cxx_code.is_empty() {
        // Step 4: Write the C++ code snippet to a .cc file, along with a #include
        //         of the header emitted in step 5.
        let cxx_code = format!("#include \"input.h\"\n#include \"cxxgen.h\"\n{}", cxx_code);
        let cxx_path = write_to_file(&tdir, "input.cxx", &cxx_code);
        b.file(cxx_path);
    }

    let b = b
        .out_dir(&target_dir)
        .host(&target)
        .target(&target)
        .opt_level(1)
        .flag("-std=c++14");
    b.include(tdir.path())
        .try_compile("autocxx-demo")
        .map_err(TestError::CppBuild)?;
    // Step 8: use the trybuild crate to build the Rust file.
    let r = get_builder().lock().unwrap().build(
        &target_dir,
        "autocxx-demo",
        &tdir.path(),
        &["input.h", "cxx.h"],
        &rs_path,
        generated_rs_files,
    );
    if r.is_err() {
        return Err(TestError::RsBuild); // details of Rust panic are a bit messy to include, and
                                        // not important at the moment.
    }
    if KEEP_TEMPDIRS {
        println!("Tempdir: {:?}", tdir.into_path().to_str());
    }
    Ok(())
}

pub(crate) fn make_clang_arg_adder(args: &[&str]) -> Option<BuilderModifier> {
    let args: Vec<_> = args.iter().map(|a| a.to_string()).collect();
    Some(Box::new(move |b| {
        let refs: Vec<_> = args.iter().map(|s| s.as_str()).collect();
        b.extra_clang_args(&refs)
    }))
}

/// Generates a closure which can be used to ensure that the given symbol
/// is mentioned in the output and has documentation attached.
/// The idea is that this is what we do in cases where we can't generate code properly.
pub(crate) fn make_error_finder(
    error_symbol: &str,
) -> Box<dyn FnOnce(syn::File) -> Result<(), TestError>> {
    let error_symbol = error_symbol.to_string();
    Box::new(move |f| {
        let ffi_items = find_ffi_items(f)?;
        // Ensure there's some kind of struct entry for this symbol
        let error_item = ffi_items
            .into_iter()
            .filter_map(|i| match i {
                Item::Struct(its) if its.ident == error_symbol => Some(its),
                _ => None,
            })
            .next()
            .ok_or(TestError::RsCodeExaminationFail)?;
        // Ensure doc attribute
        error_item
            .attrs
            .into_iter()
            .find(|a| a.path.get_ident().filter(|p| *p == "doc").is_some())
            .ok_or(TestError::RsCodeExaminationFail)?;
        Ok(())
    })
}

fn find_ffi_items(f: syn::File) -> Result<Vec<Item>, TestError> {
    Ok(f.items
        .into_iter()
        .filter_map(|i| match i {
            Item::Mod(itm) => Some(itm),
            _ => None,
        })
        .next()
        .ok_or(TestError::RsCodeExaminationFail)?
        .content
        .ok_or(TestError::RsCodeExaminationFail)?
        .1)
}

/// Returns a closure which simply hunts for a given string in the results
pub(crate) fn make_string_finder(
    error_texts: Vec<&str>,
) -> Box<dyn FnOnce(syn::File) -> Result<(), TestError> + '_> {
    Box::new(|f| {
        let mut ts = TokenStream::new();
        f.to_tokens(&mut ts);
        let toks = ts.to_string();
        for msg in error_texts {
            if !toks.contains(msg) {
                return Err(TestError::RsCodeExaminationFail);
            };
        }
        Ok(())
    })
}
<commit_msg>Fix problem with clang arg addition.<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{
    fs::File,
    io::{Read, Write},
    panic::RefUnwindSafe,
    path::{Path, PathBuf},
    sync::Mutex,
};

use autocxx_engine::{Builder, BuilderContext, BuilderError, RebuildDependencyRecorder, HEADER};
use log::info;
use once_cell::sync::OnceCell;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{Item, Token};
use tempfile::{tempdir, TempDir};

const KEEP_TEMPDIRS: bool = false;

fn get_builder() -> &'static Mutex<LinkableTryBuilder> {
    static INSTANCE: OnceCell<Mutex<LinkableTryBuilder>> = OnceCell::new();
    INSTANCE.get_or_init(|| Mutex::new(LinkableTryBuilder::new()))
}

/// TryBuild which maintains a directory of libraries to link.
/// This is desirable because otherwise, if we alter the RUSTFLAGS
/// then trybuild rebuilds *everything* including all the dev-dependencies.
/// This object exists purely so that we use the same RUSTFLAGS for every
/// test case.
struct LinkableTryBuilder {
    /// Directory in which we'll keep any linkable libraries
    temp_dir: TempDir,
}

impl LinkableTryBuilder {
    fn new() -> Self {
        LinkableTryBuilder {
            temp_dir: tempdir().unwrap(),
        }
    }

    fn move_items_into_temp_dir<P1: AsRef<Path>>(&self, src_path: &P1, pattern: &str) {
        for item in std::fs::read_dir(src_path).unwrap() {
            let item = item.unwrap();
            if item.file_name().into_string().unwrap().contains(pattern) {
                let dest = self.temp_dir.path().join(item.file_name());
                if dest.exists() {
                    std::fs::remove_file(&dest).unwrap();
                }
                std::fs::rename(item.path(), dest).unwrap();
            }
        }
    }

    fn build<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path> + RefUnwindSafe>(
        &self,
        library_path: &P1,
        library_name: &str,
        header_path: &P2,
        header_names: &[&str],
        rs_path: &P3,
        generated_rs_files: Vec<PathBuf>,
    ) -> std::thread::Result<()> {
        // Copy all items from the source dir into our temporary dir if their name matches
        // the pattern given in `library_name`.
        self.move_items_into_temp_dir(library_path, library_name);
        for header_name in header_names {
            self.move_items_into_temp_dir(header_path, header_name);
        }
        for generated_rs in generated_rs_files {
            self.move_items_into_temp_dir(
                &generated_rs.parent().unwrap().to_path_buf(),
                &generated_rs.file_name().unwrap().to_str().unwrap(),
            );
        }
        let temp_path = self.temp_dir.path().to_str().unwrap();
        std::env::set_var("RUSTFLAGS", format!("-L {}", temp_path));
        std::env::set_var("AUTOCXX_RS", temp_path);
        std::panic::catch_unwind(|| {
            let test_cases = trybuild::TestCases::new();
            test_cases.pass(rs_path)
        })
    }
}

fn write_to_file(tdir: &TempDir, filename: &str, content: &str) -> PathBuf {
    let path = tdir.path().join(filename);
    let mut f = File::create(&path).unwrap();
    f.write_all(content.as_bytes()).unwrap();
    path
}

/// A positive test, we expect to pass.
pub(crate) fn run_test(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    generate: &[&str],
    generate_pods: &[&str],
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives_from_lists(generate, generate_pods, None),
        None,
        None,
        None,
    )
    .unwrap()
}

// A function applied to the resultant generated Rust code
// which can be used to inspect that code.
pub(crate) type RustCodeChecker = Box<dyn FnOnce(syn::File) -> Result<(), TestError>>;

// A function to modify Builders
pub(crate) trait BuilderModifierFns {
    fn modify_autocxx_builder(
        &self,
        builder: Builder<TestBuilderContext>,
    ) -> Builder<TestBuilderContext>;
    fn modify_cc_builder<'a>(&self, builder: &'a mut cc::Build) -> &'a mut cc::Build;
}

pub(crate) type BuilderModifier = Box<dyn BuilderModifierFns>;

/// A positive test, we expect to pass.
#[allow(clippy::too_many_arguments)] // least typing for each test
pub(crate) fn run_test_ex(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    directives: TokenStream,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
    extra_rust: Option<TokenStream>,
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives,
        builder_modifier,
        rust_code_checker,
        extra_rust,
    )
    .unwrap()
}

pub(crate) fn run_test_expect_fail(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    generate: &[&str],
    generate_pods: &[&str],
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives_from_lists(generate, generate_pods, None),
        None,
        None,
        None,
    )
    .expect_err("Unexpected success");
}

pub(crate) fn run_test_expect_fail_ex(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    directives: TokenStream,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
    extra_rust: Option<TokenStream>,
) {
    do_run_test(
        cxx_code,
        header_code,
        rust_code,
        directives,
        builder_modifier,
        rust_code_checker,
        extra_rust,
    )
    .expect_err("Unexpected success");
}

/// In the future maybe the tests will distinguish the exact type of failure expected.
#[derive(Debug)]
pub(crate) enum TestError {
    AutoCxx(BuilderError),
    CppBuild(cc::Error),
    RsBuild,
    NoRs,
    RsFileOpen(std::io::Error),
    RsFileRead(std::io::Error),
    RsFileParse(syn::Error),
    RsCodeExaminationFail,
}

pub(crate) fn directives_from_lists(
    generate: &[&str],
    generate_pods: &[&str],
    extra_directives: Option<TokenStream>,
) -> TokenStream {
    let generate = generate.iter().map(|s| {
        quote! {
            generate!(#s)
        }
    });
    let generate_pods = generate_pods.iter().map(|s| {
        quote! {
            generate_pod!(#s)
        }
    });
    quote! {
        #(#generate)*
        #(#generate_pods)*
        #extra_directives
    }
}

#[allow(clippy::too_many_arguments)] // least typing for each test
fn do_run_test(
    cxx_code: &str,
    header_code: &str,
    rust_code: TokenStream,
    directives: TokenStream,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
    extra_rust: Option<TokenStream>,
) -> Result<(), TestError> {
    let hexathorpe = Token![#](Span::call_site());
    let unexpanded_rust = |hdr: &str| {
        quote! {
            use autocxx::include_cpp;

            include_cpp!(
                #hexathorpe include #hdr
                safety!(unsafe_ffi)
                #directives
            );

            #extra_rust

            fn main() {
                #rust_code
            }
        }
    };
    do_run_test_manual(
        cxx_code,
        header_code,
        unexpanded_rust,
        builder_modifier,
        rust_code_checker,
    )
}

pub(crate) struct TestBuilderContext;

impl BuilderContext for TestBuilderContext {
    fn get_dependency_recorder() -> Option<Box<dyn RebuildDependencyRecorder>> {
        None
    }
}

pub(crate) fn do_run_test_manual<F>(
    cxx_code: &str,
    header_code: &str,
    rust_code_generator: F,
    builder_modifier: Option<BuilderModifier>,
    rust_code_checker: Option<RustCodeChecker>,
) -> Result<(), TestError>
where
    F: FnOnce(&'static str) -> TokenStream,
{
    const HEADER_NAME: &str = "input.h";
    let mut rust_code = rust_code_generator(HEADER_NAME);
    // Step 2: Write the C++ header snippet to a temp file
    let tdir = tempdir().unwrap();
    write_to_file(
        &tdir,
        HEADER_NAME,
        &format!("#pragma once\n{}", header_code),
    );
    write_to_file(&tdir, "cxx.h", HEADER);

    rust_code.append_all(quote! {
        #[link(name="autocxx-demo")]
        extern {}
    });
    info!("Unexpanded Rust: {}", rust_code);

    let write_rust_to_file = |ts: &TokenStream| -> PathBuf {
        // Step 3: Write the Rust code to a temp file
        let rs_code = format!("{}", ts);
        write_to_file(&tdir, "input.rs", &rs_code)
    };

    let target_dir = tdir.path().join("target");
    std::fs::create_dir(&target_dir).unwrap();

    let rs_path = write_rust_to_file(&rust_code);

    info!("Path is {:?}", tdir.path());
    let builder = Builder::<TestBuilderContext>::new(&rs_path, &[tdir.path()])
        .custom_gendir(target_dir.clone());
    let builder = if let Some(builder_modifier) = &builder_modifier {
        builder_modifier.modify_autocxx_builder(builder)
    } else {
        builder
    };
    let build_results = builder.build_listing_files().map_err(TestError::AutoCxx)?;
    let mut b = build_results.0;
    let generated_rs_files = build_results.1;

    if let Some(rust_code_checker) = rust_code_checker {
        let mut file = File::open(generated_rs_files.get(0).ok_or(TestError::NoRs)?)
            .map_err(TestError::RsFileOpen)?;
        let mut content = String::new();
        file.read_to_string(&mut content)
            .map_err(TestError::RsFileRead)?;

        let ast = syn::parse_file(&content).map_err(TestError::RsFileParse)?;
        rust_code_checker(ast)?;
    }

    let target = rust_info::get().target_triple.unwrap();

    if !cxx_code.is_empty() {
        // Step 4: Write the C++ code snippet to a .cc file, along with a #include
        //         of the header emitted in step 5.
        let cxx_code = format!("#include \"input.h\"\n#include \"cxxgen.h\"\n{}", cxx_code);
        let cxx_path = write_to_file(&tdir, "input.cxx", &cxx_code);
        b.file(cxx_path);
    }

    let b = b
        .out_dir(&target_dir)
        .host(&target)
        .target(&target)
        .opt_level(1)
        .flag("-std=c++14");
    let b = if let Some(builder_modifier) = builder_modifier {
        builder_modifier.modify_cc_builder(b)
    } else {
        b
    };
    b.include(tdir.path())
        .try_compile("autocxx-demo")
        .map_err(TestError::CppBuild)?;
    // Step 8: use the trybuild crate to build the Rust file.
    let r = get_builder().lock().unwrap().build(
        &target_dir,
        "autocxx-demo",
        &tdir.path(),
        &["input.h", "cxx.h"],
        &rs_path,
        generated_rs_files,
    );
    if r.is_err() {
        return Err(TestError::RsBuild); // details of Rust panic are a bit messy to include, and
                                        // not important at the moment.
    }
    if KEEP_TEMPDIRS {
        println!("Tempdir: {:?}", tdir.into_path().to_str());
    }
    Ok(())
}

struct ClangArgAdder(Vec<String>);

pub(crate) fn make_clang_arg_adder(args: &[&str]) -> Option<BuilderModifier> {
    let args: Vec<_> = args.iter().map(|a| a.to_string()).collect();
    Some(Box::new(ClangArgAdder(args)))
}

impl BuilderModifierFns for ClangArgAdder {
    fn modify_autocxx_builder(
        &self,
        builder: Builder<TestBuilderContext>,
    ) -> Builder<TestBuilderContext> {
        let refs: Vec<_> = self.0.iter().map(|s| s.as_str()).collect();
        builder.extra_clang_args(&refs)
    }

    fn modify_cc_builder<'a>(&self, mut builder: &'a mut cc::Build) -> &'a mut cc::Build {
        for f in &self.0 {
            builder = builder.flag(f);
        }
        builder
    }
}

/// Generates a closure which can be used to ensure that the given symbol
/// is mentioned in the output and has documentation attached.
/// The idea is that this is what we do in cases where we can't generate code properly.
pub(crate) fn make_error_finder(
    error_symbol: &str,
) -> Box<dyn FnOnce(syn::File) -> Result<(), TestError>> {
    let error_symbol = error_symbol.to_string();
    Box::new(move |f| {
        let ffi_items = find_ffi_items(f)?;
        // Ensure there's some kind of struct entry for this symbol
        let error_item = ffi_items
            .into_iter()
            .filter_map(|i| match i {
                Item::Struct(its) if its.ident == error_symbol => Some(its),
                _ => None,
            })
            .next()
            .ok_or(TestError::RsCodeExaminationFail)?;
        // Ensure doc attribute
        error_item
            .attrs
            .into_iter()
            .find(|a| a.path.get_ident().filter(|p| *p == "doc").is_some())
            .ok_or(TestError::RsCodeExaminationFail)?;
        Ok(())
    })
}

fn find_ffi_items(f: syn::File) -> Result<Vec<Item>, TestError> {
    Ok(f.items
        .into_iter()
        .filter_map(|i| match i {
            Item::Mod(itm) => Some(itm),
            _ => None,
        })
        .next()
        .ok_or(TestError::RsCodeExaminationFail)?
        .content
        .ok_or(TestError::RsCodeExaminationFail)?
        .1)
}

/// Returns a closure which simply hunts for a given string in the results
pub(crate) fn make_string_finder(
    error_texts: Vec<&str>,
) -> Box<dyn FnOnce(syn::File) -> Result<(), TestError> + '_> {
    Box::new(|f| {
        let mut ts = TokenStream::new();
        f.to_tokens(&mut ts);
        let toks = ts.to_string();
        for msg in error_texts {
            if !toks.contains(msg) {
                return Err(TestError::RsCodeExaminationFail);
            };
        }
        Ok(())
    })
}
<|endoftext|>


<commit_before>use std::time::{Duration, SystemTime, Instant};
use std::convert::TryFrom;

use rustc_target::abi::LayoutOf;

use crate::stacked_borrows::Tag;
use crate::*;
use helpers::{immty_from_int_checked, immty_from_uint_checked};

/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
    time.duration_since(SystemTime::UNIX_EPOCH)
        .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into())
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn clock_gettime(
        &mut self,
        clk_id_op: OpTy<'tcx, Tag>,
        tp_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("linux", "clock_gettime");
        this.check_no_isolation("clock_gettime")?;

        let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
        let tp = this.deref_operand(tp_op)?;

        let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? {
            system_time_to_duration(&SystemTime::now())?
        } else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
            // Absolute time does not matter, only relative time does, so we can just
            // use our own time anchor here.
            Instant::now().duration_since(this.machine.time_anchor)
        } else {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        };

        let tv_sec = duration.as_secs();
        let tv_nsec = duration.subsec_nanos();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_nsec, this.libc_ty_layout("c_long")?)?,
        ];

        this.write_packed_immediates(tp, &imms)?;

        Ok(0)
    }

    fn gettimeofday(
        &mut self,
        tv_op: OpTy<'tcx, Tag>,
        tz_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("macos", "gettimeofday");
        this.check_no_isolation("gettimeofday")?;

        // Using tz is obsolete and should always be null
        let tz = this.read_scalar(tz_op)?.not_undef()?;
        if !this.is_null(tz)? {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        }

        let tv = this.deref_operand(tv_op)?;

        let duration = system_time_to_duration(&SystemTime::now())?;
        let tv_sec = duration.as_secs();
        let tv_usec = duration.subsec_micros();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?,
        ];

        this.write_packed_immediates(tv, &imms)?;

        Ok(0)
    }

    #[allow(non_snake_case)]
    fn GetSystemTimeAsFileTime(&mut self, LPFILETIME_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "GetSystemTimeAsFileTime");
        this.check_no_isolation("GetSystemTimeAsFileTime")?;

        let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC")?;
        let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC")?;
        let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH")?;
        let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
        let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;

        let duration = system_time_to_duration(&SystemTime::now())? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH);
        let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
            .map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;

        let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
        let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
        let DWORD_tylayout = this.layout_of(this.tcx.types.u32)?;
        let imms = [
            immty_from_uint_checked(dwLowDateTime, DWORD_tylayout)?,
            immty_from_uint_checked(dwHighDateTime, DWORD_tylayout)?,
        ];
        this.write_packed_immediates(this.deref_operand(LPFILETIME_op)?, &imms)?;
        Ok(())
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceCounter(&mut self, lpPerformanceCount_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceCounter");
        this.check_no_isolation("QueryPerformanceCounter")?;

        // QueryPerformanceCounter uses a hardware counter as its basis.
        // Miri will emulate a counter with a resolution of 1 nanosecond.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        let qpc = i64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported"))?;
        this.write_scalar(Scalar::from_i64(qpc), this.deref_operand(lpPerformanceCount_op)?.into())?;
        Ok(-1) // return non-zero on success
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceFrequency(&mut self, lpFrequency_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceFrequency");
        this.check_no_isolation("QueryPerformanceFrequency")?;

        // Retrieves the frequency of the hardware performance counter.
        // The frequency of the performance counter is fixed at system boot and
        // is consistent across all processors.
        // Miri emulates a "hardware" performance counter with a resolution of 1ns,
        // and thus 10^9 counts per second.
        this.write_scalar(Scalar::from_i64(1_000_000_000), this.deref_operand(lpFrequency_op)?.into())?;
        Ok(-1) // Return non-zero on success
    }

    fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> {
        let this = self.eval_context_ref();

        this.assert_target_os("macos", "mach_absolute_time");
        this.check_no_isolation("mach_absolute_time")?;

        // This returns a u64, with time units determined dynamically by `mach_timebase_info`.
        // We return plain nanoseconds.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        u64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
    }

    fn mach_timebase_info(&mut self, info_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("macos", "mach_timebase_info");
        this.check_no_isolation("mach_timebase_info")?;

        let info = this.deref_operand(info_op)?;

        // Since we return nanoseconds instead of ticks from
        // `mach_absolute_time`, we don't need to scale the absolute
        // time.
        let (numer, denom) = (1,1);
        let uint32_layout = this.layout_of(this.tcx.types.u32)?;
        let imms = [
            immty_from_int_checked(numer, uint32_layout)?,
            immty_from_int_checked(denom, uint32_layout)?
        ];

        this.write_packed_immediates(info, &imms)?;
        Ok(0)
    }
}
<commit_msg>Use precomputed TyLayout from `machine.layouts`<commit_after>use std::time::{Duration, SystemTime, Instant};
use std::convert::TryFrom;

use rustc_target::abi::LayoutOf;

use crate::stacked_borrows::Tag;
use crate::*;
use helpers::{immty_from_int_checked, immty_from_uint_checked};

/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
    time.duration_since(SystemTime::UNIX_EPOCH)
        .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into())
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn clock_gettime(
        &mut self,
        clk_id_op: OpTy<'tcx, Tag>,
        tp_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("linux", "clock_gettime");
        this.check_no_isolation("clock_gettime")?;

        let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
        let tp = this.deref_operand(tp_op)?;

        let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? {
            system_time_to_duration(&SystemTime::now())?
        } else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
            // Absolute time does not matter, only relative time does, so we can just
            // use our own time anchor here.
            Instant::now().duration_since(this.machine.time_anchor)
        } else {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        };

        let tv_sec = duration.as_secs();
        let tv_nsec = duration.subsec_nanos();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_nsec, this.libc_ty_layout("c_long")?)?,
        ];

        this.write_packed_immediates(tp, &imms)?;

        Ok(0)
    }

    fn gettimeofday(
        &mut self,
        tv_op: OpTy<'tcx, Tag>,
        tz_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("macos", "gettimeofday");
        this.check_no_isolation("gettimeofday")?;

        // Using tz is obsolete and should always be null
        let tz = this.read_scalar(tz_op)?.not_undef()?;
        if !this.is_null(tz)? {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        }

        let tv = this.deref_operand(tv_op)?;

        let duration = system_time_to_duration(&SystemTime::now())?;
        let tv_sec = duration.as_secs();
        let tv_usec = duration.subsec_micros();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?,
        ];

        this.write_packed_immediates(tv, &imms)?;

        Ok(0)
    }

    #[allow(non_snake_case)]
    fn GetSystemTimeAsFileTime(&mut self, LPFILETIME_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "GetSystemTimeAsFileTime");
        this.check_no_isolation("GetSystemTimeAsFileTime")?;

        let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC")?;
        let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC")?;
        let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH")?;
        let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
        let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;

        let duration = system_time_to_duration(&SystemTime::now())? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH);
        let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
            .map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;

        let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
        let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
        let DWORD_tylayout = this.layout_of(this.tcx.types.u32)?;
        let imms = [
            immty_from_uint_checked(dwLowDateTime, DWORD_tylayout)?,
            immty_from_uint_checked(dwHighDateTime, DWORD_tylayout)?,
        ];
        this.write_packed_immediates(this.deref_operand(LPFILETIME_op)?, &imms)?;
        Ok(())
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceCounter(&mut self, lpPerformanceCount_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceCounter");
        this.check_no_isolation("QueryPerformanceCounter")?;

        // QueryPerformanceCounter uses a hardware counter as its basis.
        // Miri will emulate a counter with a resolution of 1 nanosecond.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        let qpc = i64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported"))?;
        this.write_scalar(Scalar::from_i64(qpc), this.deref_operand(lpPerformanceCount_op)?.into())?;
        Ok(-1) // return non-zero on success
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceFrequency(&mut self, lpFrequency_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceFrequency");
        this.check_no_isolation("QueryPerformanceFrequency")?;

        // Retrieves the frequency of the hardware performance counter.
        // The frequency of the performance counter is fixed at system boot and
        // is consistent across all processors.
        // Miri emulates a "hardware" performance counter with a resolution of 1ns,
        // and thus 10^9 counts per second.
        this.write_scalar(Scalar::from_i64(1_000_000_000), this.deref_operand(lpFrequency_op)?.into())?;
        Ok(-1) // Return non-zero on success
    }

    fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> {
        let this = self.eval_context_ref();

        this.assert_target_os("macos", "mach_absolute_time");
        this.check_no_isolation("mach_absolute_time")?;

        // This returns a u64, with time units determined dynamically by `mach_timebase_info`.
        // We return plain nanoseconds.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        u64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
    }

    fn mach_timebase_info(&mut self, info_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("macos", "mach_timebase_info");
        this.check_no_isolation("mach_timebase_info")?;

        let info = this.deref_operand(info_op)?;

        // Since we return nanoseconds instead of ticks from
        // `mach_absolute_time`, we don't need to scale the absolute
        // time.
        let (numer, denom) = (1,1);
        let imms = [
            immty_from_int_checked(numer, this.machine.layouts.u32)?,
            immty_from_int_checked(denom, this.machine.layouts.u32)?
        ];

        this.write_packed_immediates(info, &imms)?;
        Ok(0) // KERN_SUCCESS
    }
}
<|endoftext|>


<commit_before>//! Shared mathematical utility functions.

/// Calculates the width and height an image should be resized to.
/// This preserves aspect ratio, and based on the `fill` parameter
/// will either fill the dimensions to fit inside the smaller constraint
/// (will overflow the specified bounds on one axis to preserve
/// aspect ratio), or will shrink so that both dimensions are
/// completely contained with in the given `width` and `height`,
/// with empty space on one axis.
pub(crate) fn resize_dimensions(
    width: u32,
    height: u32,
    nwidth: u32,
    nheight: u32,
    fill: bool,
) -> (u32, u32) {
    let ratio = u64::from(width) * u64::from(nheight);
    let nratio = u64::from(nwidth) * u64::from(height);

    let use_width = if fill {
        nratio > ratio
    } else {
        nratio <= ratio
    };
    let intermediate = if use_width {
        u64::from(height) * u64::from(nwidth) / u64::from(width)
    } else {
        u64::from(width) * u64::from(nheight) / u64::from(height)
    };
    let intermediate = std::cmp::max(1, intermediate);
    if use_width {
        if intermediate <= u64::from(::std::u32::MAX) {
            (nwidth, intermediate as u32)
        } else {
            (
                (u64::from(nwidth) * u64::from(::std::u32::MAX) / intermediate) as u32,
                ::std::u32::MAX,
            )
        }
    } else if intermediate <= u64::from(::std::u32::MAX) {
        (intermediate as u32, nheight)
    } else {
        (
            ::std::u32::MAX,
            (u64::from(nheight) * u64::from(::std::u32::MAX) / intermediate) as u32,
        )
    }
}

#[cfg(test)]
mod test {
    quickcheck! {
        fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
            if old_w == 0 || new_w == 0 { return true; }
            let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
            result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64) as u32
        }
    }

    quickcheck! {
        fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
            if old_h == 0 || new_h == 0 { return true; }
            let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
            result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64) as u32
        }
    }

    #[test]
    fn resize_handles_fill() {
        let result = super::resize_dimensions(100, 200, 200, 500, true);
        assert!(result.0 == 250);
        assert!(result.1 == 500);

        let result = super::resize_dimensions(200, 100, 500, 200, true);
        assert!(result.0 == 500);
        assert!(result.1 == 250);
    }

    #[test]
    fn resize_never_rounds_to_zero() {
        let result = super::resize_dimensions(1, 150, 128, 128, false);
        assert!(result.0 > 0);
        assert!(result.1 > 0);
    }

    #[test]
    fn resize_handles_overflow() {
        let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
        assert!(result.0 == 100);
        assert!(result.1 == ::std::u32::MAX);

        let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
        assert!(result.0 == ::std::u32::MAX);
        assert!(result.1 == 100);
    }
}
<commit_msg>Fix resize_dimensions to round values instead of truncating them.<commit_after>//! Shared mathematical utility functions.

use std::cmp::max;

/// Calculates the width and height an image should be resized to.
/// This preserves aspect ratio, and based on the `fill` parameter
/// will either fill the dimensions to fit inside the smaller constraint
/// (will overflow the specified bounds on one axis to preserve
/// aspect ratio), or will shrink so that both dimensions are
/// completely contained with in the given `width` and `height`,
/// with empty space on one axis.
pub(crate) fn resize_dimensions(
    width: u32,
    height: u32,
    nwidth: u32,
    nheight: u32,
    fill: bool,
) -> (u32, u32) {
    let wratio = nwidth as f64 / width as f64;
    let hratio = nheight as f64 / height as f64;

    let ratio = if fill {
        f64::max(wratio, hratio)
    } else {
        f64::min(wratio, hratio)
    };

    let nw = max((width as f64 * ratio).round() as u64, 1);
    let nh = max((height as f64 * ratio).round() as u64, 1);

    if nw > u64::from(u32::MAX) {
        let ratio = u32::MAX as f64 / width as f64;
        (u32::MAX, max((height as f64 * ratio).round() as u32, 1))
    } else if nh > u64::from(u32::MAX) {
        let ratio = u32::MAX as f64 / height as f64;
        (max((width as f64 * ratio).round() as u32, 1), u32::MAX)
    } else {
        (nw as u32, nh as u32)
    }
}

#[cfg(test)]
mod test {
    quickcheck! {
        fn resize_bounds_correctly_width(old_w: u32, new_w: u32) -> bool {
            if old_w == 0 || new_w == 0 { return true; }
            let result = super::resize_dimensions(old_w, 400, new_w, ::std::u32::MAX, false);
            result.0 == new_w && result.1 == (400 as f64 * new_w as f64 / old_w as f64).round() as u32
        }
    }

    quickcheck! {
        fn resize_bounds_correctly_height(old_h: u32, new_h: u32) -> bool {
            if old_h == 0 || new_h == 0 { return true; }
            let result = super::resize_dimensions(400, old_h, ::std::u32::MAX, new_h, false);
            result.1 == new_h && result.0 == (400 as f64 * new_h as f64 / old_h as f64).round() as u32
        }
    }

    #[test]
    fn resize_handles_fill() {
        let result = super::resize_dimensions(100, 200, 200, 500, true);
        assert!(result.0 == 250);
        assert!(result.1 == 500);

        let result = super::resize_dimensions(200, 100, 500, 200, true);
        assert!(result.0 == 500);
        assert!(result.1 == 250);
    }

    #[test]
    fn resize_never_rounds_to_zero() {
        let result = super::resize_dimensions(1, 150, 128, 128, false);
        assert!(result.0 > 0);
        assert!(result.1 > 0);
    }

    #[test]
    fn resize_handles_overflow() {
        let result = super::resize_dimensions(100, ::std::u32::MAX, 200, ::std::u32::MAX, true);
        assert!(result.0 == 100);
        assert!(result.1 == ::std::u32::MAX);

        let result = super::resize_dimensions(::std::u32::MAX, 100, ::std::u32::MAX, 200, true);
        assert!(result.0 == ::std::u32::MAX);
        assert!(result.1 == 100);
    }

    #[test]
    fn resize_rounds() {
        // Only truncation will result in (3840, 2229) and (2160, 3719)
        let result = super::resize_dimensions(4264, 2476, 3840, 2160, true);
        assert_eq!(result, (3840, 2230));

        let result = super::resize_dimensions(2476, 4264, 2160, 3840, false);
        assert_eq!(result, (2160, 3720));
    }

    #[test]
    fn resize_handles_zero() {
        let result = super::resize_dimensions(0, 100, 100, 100, false);
        assert_eq!(result, (1, 100));

        let result = super::resize_dimensions(100, 0, 100, 100, false);
        assert_eq!(result, (100, 1));

        let result = super::resize_dimensions(100, 100, 0, 100, false);
        assert_eq!(result, (1, 1));

        let result = super::resize_dimensions(100, 100, 100, 0, false);
        assert_eq!(result, (1, 1));
    }
}
<|endoftext|>


<commit_before>// Copyright 2020-2021 The Propverify authors
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use lazy_static::lazy_static;
use log::info;
use regex::Regex;

/// Detect lines that match #[should_panic(expected = ...)] string.
pub fn is_expected_panic(line: &str, expect: &Option<&str>, name: &str) -> bool {
    lazy_static! {
        static ref PANICKED: Regex = Regex::new(r" panicked at '([^']*)',\s+(.*)").unwrap();
    }

    if let Some(expect) = expect {
        if let Some(caps) = PANICKED.captures(line) {
            let message = caps.get(1).unwrap().as_str();
            let srcloc = caps.get(2).unwrap().as_str();
            if message.contains(expect) {
                info!(
                    "     {}: Detected expected failure '{}' at {}",
                    name, message, srcloc
                );
                info!("     Error message: {}", line);
                return true;
            }
        }
    }

    false
}
<commit_msg>cargo-verify: fix should_panic support for assert_eq<commit_after>// Copyright 2020-2021 The Propverify authors
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use lazy_static::lazy_static;
use log::info;
use regex::Regex;

/// Detect lines that match #[should_panic(expected = ...)] string.
pub fn is_expected_panic(line: &str, expect: &Option<&str>, name: &str) -> bool {
    lazy_static! {
        static ref PANICKED: Regex = Regex::new(r" panicked at '([^']*)',\s+(.*)").unwrap();
        // This second form of panic is for multi-line panic messages
        // such as that generated by assert_eq!
        static ref PANICKED2: Regex = Regex::new(r" panicked at '([^']*)").unwrap();
    }

    if let Some(expect) = expect {
        if let Some(caps) = PANICKED.captures(line) {
            let message = caps.get(1).unwrap().as_str();
            let srcloc = caps.get(2).unwrap().as_str();
            if message.contains(expect) {
                info!(
                    "     {}: Detected expected failure '{}' at {}",
                    name, message, srcloc
                );
                info!("     Error message: {}", line);
                return true;
            }
        } else if let Some(caps) = PANICKED2.captures(line) {
            let message = caps.get(1).unwrap().as_str();
            if message.contains(expect) {
                info!(
                    "     {}: Detected expected failure '{}'",
                    name, message
                );
                info!("     Error message: {}", line);
                return true;
            }
        }
    }

    false
}
<|endoftext|>


<commit_before>mod screen;
mod main_menu_screen;
mod battle_screen;

pub use self::screen::*;
pub use self::main_menu_screen::*;
pub use self::battle_screen::*;

/*
use piston_window;

mod main_menu_screen;
mod battle_screen;

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input);
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &piston_window::PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}

pub use self::main_menu_screen::*;
pub use self::battle_screen::*;
*/<commit_msg>Commented code has been removed<commit_after>mod screen;
mod main_menu_screen;
mod battle_screen;

pub use self::screen::*;
pub use self::main_menu_screen::*;
pub use self::battle_screen::*;
<|endoftext|>


<commit_before>// These just test that serde_codegen is able to produce code that compiles
// successfully when there are a variety of generics and non-(de)serializable
// types involved.

extern crate serde;
use self::serde::ser::{Serialize, Serializer};
use self::serde::de::{Deserialize, Deserializer};

//////////////////////////////////////////////////////////////////////////

#[derive(Serialize, Deserialize)]
struct With<T> {
    t: T,
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    x: X,
}

#[derive(Serialize, Deserialize)]
struct WithRef<'a, T: 'a> {
    #[serde(skip_deserializing)]
    t: Option<&'a T>,
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    x: X,
}

#[derive(Serialize, Deserialize)]
struct Bounds<T: Serialize + Deserialize> {
    t: T,
    option: Option<T>,
    boxed: Box<T>,
    option_boxed: Option<Box<T>>,
}

#[derive(Serialize, Deserialize)]
struct NoBounds<T> {
    t: T,
    option: Option<T>,
    boxed: Box<T>,
    option_boxed: Option<Box<T>>,
}

#[derive(Serialize, Deserialize)]
enum EnumWith<T> {
    Unit,
    Newtype(
        #[serde(serialize_with="ser_x", deserialize_with="de_x")]
        X),
    Tuple(
        T,
        #[serde(serialize_with="ser_x", deserialize_with="de_x")]
        X),
    Struct {
        t: T,
        #[serde(serialize_with="ser_x", deserialize_with="de_x")]
        x: X },
}

#[derive(Serialize)]
struct MultipleRef<'a, 'b, 'c, T> where T: 'c, 'c: 'b, 'b: 'a {
    t: T,
    rrrt: &'a &'b &'c T,
}

#[derive(Serialize, Deserialize)]
struct Newtype(
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    X
);

#[derive(Serialize, Deserialize)]
struct Tuple<T>(
    T,
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    X,
);

#[derive(Serialize, Deserialize)]
enum TreeNode<D> {
    Split {
        left: Box<TreeNode<D>>,
        right: Box<TreeNode<D>>,
    },
    Leaf {
        data: D,
    },
}

#[derive(Serialize, Deserialize)]
struct ListNode<D> {
    data: D,
    next: Box<ListNode<D>>,
}

#[derive(Serialize, Deserialize)]
#[serde(bound="D: SerializeWith + DeserializeWith")]
struct WithTraits1<D, E> {
    #[serde(serialize_with="SerializeWith::serialize_with",
            deserialize_with="DeserializeWith::deserialize_with")]
    d: D,
    #[serde(serialize_with="SerializeWith::serialize_with",
            deserialize_with="DeserializeWith::deserialize_with",
            bound="E: SerializeWith + DeserializeWith")]
    e: E,
}

#[derive(Serialize, Deserialize)]
#[serde(bound(serialize="D: SerializeWith",
              deserialize="D: DeserializeWith"))]
struct WithTraits2<D, E> {
    #[serde(serialize_with="SerializeWith::serialize_with",
            deserialize_with="DeserializeWith::deserialize_with")]
    d: D,
    #[serde(serialize_with="SerializeWith::serialize_with",
            bound(serialize="E: SerializeWith"))]
    #[serde(deserialize_with="DeserializeWith::deserialize_with",
            bound(deserialize="E: DeserializeWith"))]
    e: E,
}

//////////////////////////////////////////////////////////////////////////

trait SerializeWith {
    fn serialize_with<S: Serializer>(_: &Self, _: &mut S) -> Result<(), S::Error>;
}

trait DeserializeWith: Sized {
    fn deserialize_with<D: Deserializer>(_: &mut D) -> Result<Self, D::Error>;
}

// Implements neither Serialize nor Deserialize
struct X;
fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() }
fn de_x<D: Deserializer>(_: &mut D) -> Result<X, D::Error> { panic!() }

<commit_msg>Ignore unused struct in test_gen.rs<commit_after>// These just test that serde_codegen is able to produce code that compiles
// successfully when there are a variety of generics and non-(de)serializable
// types involved.

extern crate serde;
use self::serde::ser::{Serialize, Serializer};
use self::serde::de::{Deserialize, Deserializer};

//////////////////////////////////////////////////////////////////////////

#[derive(Serialize, Deserialize)]
struct With<T> {
    t: T,
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    x: X,
}

#[derive(Serialize, Deserialize)]
struct WithRef<'a, T: 'a> {
    #[serde(skip_deserializing)]
    t: Option<&'a T>,
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    x: X,
}

#[derive(Serialize, Deserialize)]
struct Bounds<T: Serialize + Deserialize> {
    t: T,
    option: Option<T>,
    boxed: Box<T>,
    option_boxed: Option<Box<T>>,
}

#[derive(Serialize, Deserialize)]
struct NoBounds<T> {
    t: T,
    option: Option<T>,
    boxed: Box<T>,
    option_boxed: Option<Box<T>>,
}

#[derive(Serialize, Deserialize)]
enum EnumWith<T> {
    Unit,
    Newtype(
        #[serde(serialize_with="ser_x", deserialize_with="de_x")]
        X),
    Tuple(
        T,
        #[serde(serialize_with="ser_x", deserialize_with="de_x")]
        X),
    Struct {
        t: T,
        #[serde(serialize_with="ser_x", deserialize_with="de_x")]
        x: X },
}

#[derive(Serialize)]
#[allow(dead_code)]
struct MultipleRef<'a, 'b, 'c, T> where T: 'c, 'c: 'b, 'b: 'a {
    t: T,
    rrrt: &'a &'b &'c T,
}

#[derive(Serialize, Deserialize)]
struct Newtype(
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    X
);

#[derive(Serialize, Deserialize)]
struct Tuple<T>(
    T,
    #[serde(serialize_with="ser_x", deserialize_with="de_x")]
    X,
);

#[derive(Serialize, Deserialize)]
enum TreeNode<D> {
    Split {
        left: Box<TreeNode<D>>,
        right: Box<TreeNode<D>>,
    },
    Leaf {
        data: D,
    },
}

#[derive(Serialize, Deserialize)]
struct ListNode<D> {
    data: D,
    next: Box<ListNode<D>>,
}

#[derive(Serialize, Deserialize)]
#[serde(bound="D: SerializeWith + DeserializeWith")]
struct WithTraits1<D, E> {
    #[serde(serialize_with="SerializeWith::serialize_with",
            deserialize_with="DeserializeWith::deserialize_with")]
    d: D,
    #[serde(serialize_with="SerializeWith::serialize_with",
            deserialize_with="DeserializeWith::deserialize_with",
            bound="E: SerializeWith + DeserializeWith")]
    e: E,
}

#[derive(Serialize, Deserialize)]
#[serde(bound(serialize="D: SerializeWith",
              deserialize="D: DeserializeWith"))]
struct WithTraits2<D, E> {
    #[serde(serialize_with="SerializeWith::serialize_with",
            deserialize_with="DeserializeWith::deserialize_with")]
    d: D,
    #[serde(serialize_with="SerializeWith::serialize_with",
            bound(serialize="E: SerializeWith"))]
    #[serde(deserialize_with="DeserializeWith::deserialize_with",
            bound(deserialize="E: DeserializeWith"))]
    e: E,
}

//////////////////////////////////////////////////////////////////////////

trait SerializeWith {
    fn serialize_with<S: Serializer>(_: &Self, _: &mut S) -> Result<(), S::Error>;
}

trait DeserializeWith: Sized {
    fn deserialize_with<D: Deserializer>(_: &mut D) -> Result<Self, D::Error>;
}

// Implements neither Serialize nor Deserialize
struct X;
fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() }
fn de_x<D: Deserializer>(_: &mut D) -> Result<X, D::Error> { panic!() }

<|endoftext|>


<commit_before>use libc::c_int;
use libc;
use libc::sigset_t;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread::ThreadId;
use local_shell::LocalShell;
use std::collections::HashMap;
use result::check_errno;
use result::ShellError;
use result::ShellResult;
use std::mem;
use errno::Errno;
use std::thread;

/// Managing global child process state.
pub struct ProcessManager {
    children: HashMap<ThreadId, Arc<Mutex<LocalShell>>>
}

impl ProcessManager {
    fn new() -> ProcessManager {
        ProcessManager {
            children: HashMap::new()
        }
    }

    pub fn add_local_shell(&mut self, id: &ThreadId,
                           shell: &Arc<Mutex<LocalShell>>) {
        self.children.insert(id.clone(), shell.clone());
    }

    pub fn remove_local_shell(&mut self, id: &ThreadId) {
        self.children.remove(id);
    }
}

/// Delegates SIGINT and SIGTERM to child processes.
#[allow(dead_code)]
pub fn delegate_signal() -> ShellResult {
    unsafe {
        let mut sigset = mem::uninitialized::<sigset_t>();
        check_errno("sigemptyset",
                    libc::sigemptyset(&mut sigset as *mut sigset_t))?;
        check_errno("sigaddset", libc::sigaddset(
                &mut sigset as *mut sigset_t, libc::SIGINT))?;
        check_errno("sigaddset", libc::sigaddset(
                &mut sigset as *mut sigset_t, libc::SIGTERM))?;

        let mut oldset = mem::uninitialized::<sigset_t>();
        let result = libc::pthread_sigmask(
            libc::SIG_BLOCK, &mut sigset as *mut sigset_t,
            &mut oldset as *mut sigset_t);
        if result != 0 {
            return Err(ShellError::Errno("pthread_sigmask", Errno(result)));
        }

        thread::spawn(move || {
            let mut signal: c_int = 0;
            let result = libc::sigwait(
                &sigset as *const sigset_t, &mut signal as *mut c_int);
            if result != 0 {
                eprintln!("sigwait failed {}", result);
                return;
            }
            let mut lock = PROCESS_MANAGER.lock().unwrap();
            let mut children = lock.children.drain().collect::<Vec<_>>();
            for &mut (_, ref entry) in &mut children {
                let mut lock = entry.lock().unwrap();
                lock.signal(signal);
            }
            for &mut (_, ref entry) in &mut children {
                let mut lock = entry.lock().unwrap();
                lock.wait();
            }
            ::std::process::exit(128 + signal);
        });
        Ok(())
    }
}

lazy_static! {
    pub static ref PROCESS_MANAGER: Mutex<ProcessManager> =
        Mutex::new(ProcessManager::new());
}
<commit_msg>Add more logs for signal handling<commit_after>use libc::c_int;
use libc;
use libc::sigset_t;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread::ThreadId;
use local_shell::LocalShell;
use std::collections::HashMap;
use result::check_errno;
use result::ShellError;
use result::ShellResult;
use std::mem;
use errno::Errno;
use std::thread;

/// Managing global child process state.
pub struct ProcessManager {
    children: HashMap<ThreadId, Arc<Mutex<LocalShell>>>
}

impl ProcessManager {
    fn new() -> ProcessManager {
        ProcessManager {
            children: HashMap::new()
        }
    }

    pub fn add_local_shell(&mut self, id: &ThreadId,
                           shell: &Arc<Mutex<LocalShell>>) {
        self.children.insert(id.clone(), shell.clone());
    }

    pub fn remove_local_shell(&mut self, id: &ThreadId) {
        self.children.remove(id);
    }
}

/// Delegates SIGINT and SIGTERM to child processes.
#[allow(dead_code)]
pub fn delegate_signal() -> ShellResult {
    unsafe {
        let mut sigset = mem::uninitialized::<sigset_t>();
        check_errno("sigemptyset",
                    libc::sigemptyset(&mut sigset as *mut sigset_t))?;
        check_errno("sigaddset", libc::sigaddset(
                &mut sigset as *mut sigset_t, libc::SIGINT))?;
        check_errno("sigaddset", libc::sigaddset(
                &mut sigset as *mut sigset_t, libc::SIGTERM))?;

        let mut oldset = mem::uninitialized::<sigset_t>();
        let result = libc::pthread_sigmask(
            libc::SIG_BLOCK, &mut sigset as *mut sigset_t,
            &mut oldset as *mut sigset_t);
        if result != 0 {
            return Err(ShellError::Errno("pthread_sigmask", Errno(result)));
        }

        thread::spawn(move || {
            info!("Start waitinig signal");
            let mut signal: c_int = 0;
            let result = libc::sigwait(
                &sigset as *const sigset_t, &mut signal as *mut c_int);
            if result != 0 {
                eprintln!("sigwait failed {}", result);
                return;
            }
            info!("Signal {} is received", signal);
            let mut lock = PROCESS_MANAGER.lock().unwrap();
            let mut children = lock.children.drain().collect::<Vec<_>>();
            for &mut (_, ref entry) in &mut children {
                let mut lock = entry.lock().unwrap();
                lock.signal(signal);
            }
            for &mut (_, ref entry) in &mut children {
                let mut lock = entry.lock().unwrap();
                lock.wait();
            }
            ::std::process::exit(128 + signal);
        });
        Ok(())
    }
}

lazy_static! {
    pub static ref PROCESS_MANAGER: Mutex<ProcessManager> =
        Mutex::new(ProcessManager::new());
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::mem;
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::Class;
use {Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

#[repr(C)]
struct ConcreteBlock<C: Clone> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    pub invoke: unsafe extern fn(&mut ConcreteBlock<C>, ...),
    descriptor: *const BlockDescriptor<C>,
    pub context: C,
}

impl<C: Clone> ConcreteBlock<C> {
    pub fn new(invoke: unsafe extern fn(&mut ConcreteBlock<C>, ...), context: C) -> ConcreteBlock<C> {
        ConcreteBlock {
            isa: &_NSConcreteStackBlock,
            // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
            flags: 1 << 25,
            _reserved: 0,
            invoke: invoke,
            // TODO: don't leak memory here
            descriptor: unsafe {
                mem::transmute(box BlockDescriptor::<C>::new())
            },
            context: context,
        }
    }

    pub fn copy(&self) -> Id<ConcreteBlock<C>> {
        unsafe {
            let block = msg_send![self copy] as *mut ConcreteBlock<C>;
            Id::from_retained_ptr(block)
        }
    }
}

impl<C: Clone> Message for ConcreteBlock<C> { }

impl<C: Clone> Clone for ConcreteBlock<C> {
    fn clone(&self) -> ConcreteBlock<C> {
        ConcreteBlock::new(self.invoke, self.context.clone())
    }
}

unsafe extern fn block_context_dispose<C: Clone>(block: &mut ConcreteBlock<C>) {
    let mut context = mem::uninitialized();
    ptr::copy_nonoverlapping_memory(&mut context, &block.context, 1);
    drop(context);
}

unsafe extern fn block_context_copy<C: Clone>(dst: &mut ConcreteBlock<C>, src: &ConcreteBlock<C>) {
    dst.context = src.context.clone();
}

#[repr(C)]
struct BlockDescriptor<C: Clone> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut ConcreteBlock<C>, &ConcreteBlock<C>),
    dispose_helper: unsafe extern fn(&mut ConcreteBlock<C>),
}

impl<C: Clone> BlockDescriptor<C> {
    pub fn new() -> BlockDescriptor<C> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<ConcreteBlock<C>>() as c_ulong,
            copy_helper: block_context_copy::<C>,
            dispose_helper: block_context_dispose::<C>,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use objc_test_utils::invoke_int_block;
    use super::ConcreteBlock;

    #[test]
    fn test_create_block() {
        extern fn block_get_int(block: &ConcreteBlock<int>) -> int {
            block.context
        }

        let result = unsafe {
            let block = ConcreteBlock::new(mem::transmute(block_get_int), 13i);
            invoke_int_block(mem::transmute(&block))
        };
        assert!(result == 13);
    }
}
<commit_msg>Added new Block struct.<commit_after>#![allow(dead_code)]

use std::mem;
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::Class;
use {Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

#[repr(C)]
pub struct Block<A, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: unsafe extern fn(*mut Block<A, R>, ...) -> R,
}

#[repr(C)]
struct ConcreteBlock<C: Clone> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    pub invoke: unsafe extern fn(&mut ConcreteBlock<C>, ...),
    descriptor: *const BlockDescriptor<C>,
    pub context: C,
}

impl<C: Clone> ConcreteBlock<C> {
    pub fn new(invoke: unsafe extern fn(&mut ConcreteBlock<C>, ...), context: C) -> ConcreteBlock<C> {
        ConcreteBlock {
            isa: &_NSConcreteStackBlock,
            // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
            flags: 1 << 25,
            _reserved: 0,
            invoke: invoke,
            // TODO: don't leak memory here
            descriptor: unsafe {
                mem::transmute(box BlockDescriptor::<C>::new())
            },
            context: context,
        }
    }

    pub fn copy(&self) -> Id<ConcreteBlock<C>> {
        unsafe {
            let block = msg_send![self copy] as *mut ConcreteBlock<C>;
            Id::from_retained_ptr(block)
        }
    }
}

impl<C: Clone> Message for ConcreteBlock<C> { }

impl<C: Clone> Clone for ConcreteBlock<C> {
    fn clone(&self) -> ConcreteBlock<C> {
        ConcreteBlock::new(self.invoke, self.context.clone())
    }
}

unsafe extern fn block_context_dispose<C: Clone>(block: &mut ConcreteBlock<C>) {
    let mut context = mem::uninitialized();
    ptr::copy_nonoverlapping_memory(&mut context, &block.context, 1);
    drop(context);
}

unsafe extern fn block_context_copy<C: Clone>(dst: &mut ConcreteBlock<C>, src: &ConcreteBlock<C>) {
    dst.context = src.context.clone();
}

#[repr(C)]
struct BlockDescriptor<C: Clone> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut ConcreteBlock<C>, &ConcreteBlock<C>),
    dispose_helper: unsafe extern fn(&mut ConcreteBlock<C>),
}

impl<C: Clone> BlockDescriptor<C> {
    pub fn new() -> BlockDescriptor<C> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<ConcreteBlock<C>>() as c_ulong,
            copy_helper: block_context_copy::<C>,
            dispose_helper: block_context_dispose::<C>,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::mem;
    use objc_test_utils::invoke_int_block;
    use super::ConcreteBlock;

    #[test]
    fn test_create_block() {
        extern fn block_get_int(block: &ConcreteBlock<int>) -> int {
            block.context
        }

        let result = unsafe {
            let block = ConcreteBlock::new(mem::transmute(block_get_int), 13i);
            invoke_int_block(mem::transmute(&block))
        };
        assert!(result == 13);
    }
}
<|endoftext|>


<commit_before>extern crate hyper;

use self::hyper::client::Client;

use std::thread;
use std::time::Duration;

use ::db::Db;
use super::{Image,download,req_and_parse};

use std::sync::mpsc::Receiver;

pub fn main(rc: &Receiver<()>) {
    let db = Db::new();
    let client = Client::new();
    let images_c = db.get_images(None,0).unwrap();
    let mut url_string = "https://derpibooru.org/images.json".to_string();
    let mut page = 1;

    'main: loop {
        let res = match req_and_parse(&client, &url_string) {
            Ok(x) => x,
            Err(_) => {
                thread::sleep(Duration::new(3,0));
                continue
            }
        };

        let images = res.as_object().unwrap()["images"].as_array().unwrap();
        if images.is_empty() { break }

        let images = images.iter().fold(Vec::new(), |mut acc, x| {
            let image = x.as_object().unwrap();
            let mut rating = String::new();

            let tags = image["tags"].as_string().unwrap().split(',').map(|x| x.trim().replace(" ", "_")).filter_map(|x| {
                if x.starts_with("artist:") {
                    Some(x.split(':').collect::<Vec<_>>()[1].to_string())
                } else if x == "safe" || x == "semi-grimdark" {
                    rating = "s".to_string();
                    None
                } else if x == "explicit" || x == "grimdark" || x == "grotesque" {
                    rating = "e".to_string();
                    None
                } else if x == "questionable" || x == "suggestive" {
                    rating = "q".to_string();
                    None
                } else {
                    Some(x.to_string())
                }}).collect::<Vec<_>>();
            let rating = rating.chars().collect::<Vec<_>>()[0];
            let url = format!("https:{}", image["image"].as_string().unwrap());
            let name = image["file_name"].as_string().unwrap();
            let id = image["id"].as_string().unwrap().parse::<u64>().unwrap();

            acc.push(Image{
                    name: format!("{}_{}",id,name),
                    got_from: "derpi".to_string(),
                    url: url,
                    tags: tags,
                    rating: rating,
                    post_url: format!("https://derpibooru.org/{}", id)
                });
            acc
        });

        for im in images {
            if !images_c.iter().any(|x| x.name == im.name ) {
                if let Err(_) = download(&client, &im, rc) {
                    break 'main
                }
            }
        }

        page += 1;

        url_string = format!("https://derpibooru.org/images.json?page={}", page);
    }
}
<commit_msg>Не качать картинки с рейтингом меньше 0 с дерпибуры<commit_after>extern crate hyper;

use self::hyper::client::Client;

use std::thread;
use std::time::Duration;

use ::db::Db;
use super::{Image,download,req_and_parse};

use std::sync::mpsc::Receiver;

pub fn main(rc: &Receiver<()>) {
    let db = Db::new();
    let client = Client::new();
    let images_c = db.get_images(None,0).unwrap();
    let mut url_string = "https://derpibooru.org/search.json?q=score.gt:0&filter_id=56027".to_string();
    let mut page = 1;

    'main: loop {
        let res = match req_and_parse(&client, &url_string) {
            Ok(x) => x,
            Err(_) => {
                thread::sleep(Duration::new(3,0));
                continue
            }
        };

        let images = res.as_object().unwrap()["search"].as_array().unwrap();
        if images.is_empty() { break }

        let images = images.iter().fold(Vec::new(), |mut acc, x| {
            let image = x.as_object().unwrap();
            let mut rating = String::new();

            let tags = image["tags"].as_string().unwrap().split(',').map(|x| x.trim().replace(" ", "_")).filter_map(|x| {
                if x.starts_with("artist:") {
                    Some(x.split(':').collect::<Vec<_>>()[1].to_string())
                } else if x == "safe" || x == "semi-grimdark" {
                    rating = "s".to_string();
                    None
                } else if x == "explicit" || x == "grimdark" || x == "grotesque" {
                    rating = "e".to_string();
                    None
                } else if x == "questionable" || x == "suggestive" {
                    rating = "q".to_string();
                    None
                } else {
                    Some(x.to_string())
                }}).collect::<Vec<_>>();
            let rating = rating.chars().collect::<Vec<_>>()[0];
            let url = format!("https:{}", image["image"].as_string().unwrap());
            let name = image["file_name"].as_string().unwrap();
            let id = image["id"].as_string().unwrap().parse::<u64>().unwrap();

            acc.push(Image{
                    name: format!("{}_{}",id,name),
                    got_from: "derpi".to_string(),
                    url: url,
                    tags: tags,
                    rating: rating,
                    post_url: format!("https://derpibooru.org/{}", id)
                });
            acc
        });

        for im in images {
            if !images_c.iter().any(|x| x.name == im.name ) {
                if let Err(_) = download(&client, &im, rc) {
                    break 'main
                }
            }
        }

        page += 1;

        url_string = format!("https://derpibooru.org/search.json?q=score.gt:0&filter_id=56027&page={}", page);
    }
}
<|endoftext|>


<commit_before>extern crate time;

use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%f %z";

impl ToSql for time::Timespec {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        let time_string = time::at_utc(*self)
            .strftime(SQLITE_DATETIME_FMT)
            .unwrap()
            .to_string();
        Ok(ToSqlOutput::from(time_string))
    }
}

impl FromSql for time::Timespec {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        value
            .as_str()
            .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
                          Ok(tm) => Ok(tm.to_timespec()),
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
                      })
    }
}

#[cfg(test)]
mod test {
    use Connection;
    use super::time;

    fn checked_memory_handle() -> Connection {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
            .unwrap();
        db
    }

    #[test]
    fn test_timespec() {
        let db = checked_memory_handle();

        let ts = time::Timespec {
            sec: 10_000,
            nsec: 0,
        };
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
            .unwrap();

        let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
            .unwrap();
        assert_eq!(from, ts);
    }
}
<commit_msg>Fixed loss of time information during string conversion<commit_after>extern crate time;

use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%f";

impl ToSql for time::Timespec {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        let time_string = time::at_utc(*self)
            .strftime(SQLITE_DATETIME_FMT)
            .unwrap()
            .to_string();
        Ok(ToSqlOutput::from(time_string))
    }
}

impl FromSql for time::Timespec {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        value
            .as_str()
            .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
                          Ok(tm) => Ok(tm.to_timespec()),
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
                      })
    }
}

#[cfg(test)]
mod test {
    use Connection;
    use super::time;

    fn checked_memory_handle() -> Connection {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
            .unwrap();
        db
    }

    #[test]
    fn test_timespec() {
        let db = checked_memory_handle();

        let ts = time::Timespec {
            sec: 10_000,
            nsec: 0,
        };
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
            .unwrap();

        let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
            .unwrap();
        assert_eq!(from, ts);
    }
}
<|endoftext|>