<commit_before>//! Crate to `#include` C++ headers in your Rust code, and generate
//! idiomatic bindings using `cxx`. See [include_cpp] for details.

// Copyright 2020 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::Write,
    os::unix::prelude::PermissionsExt,
    path::{Path, PathBuf},
    process::Command,
};

use autocxx_engine::preprocess;
use clap::{crate_authors, crate_version, App, Arg, ArgMatches};
use indoc::indoc;
use itertools::Itertools;
use tempfile::TempDir;

static LONG_HELP: &str = indoc! {"
Command line utility to minimize autocxx bug cases.

This is a wrapper for creduce.

Example command-line:
autocxx-reduce -I my-inc-dir -h my-header -d 'generate!(\"MyClass\")' -k -- --n 64 --remove-pass pass_line_markers
"};

fn main() {
    let matches = App::new("autocxx-reduce")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Reduce a C++ test case")
        .long_about(LONG_HELP)
        .arg(
            Arg::with_name("inc")
                .short("I")
                .long("inc")
                .multiple(true)
                .number_of_values(1)
                .value_name("INCLUDE DIRS")
                .help("include path")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("define")
                .short("D")
                .long("define")
                .multiple(true)
                .number_of_values(1)
                .value_name("DEFINE")
                .help("macro definition")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("header")
                .short("h")
                .long("header")
                .multiple(true)
                .number_of_values(1)
                .required(true)
                .value_name("HEADER")
                .help("header file name")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directive")
                .short("d")
                .long("directive")
                .multiple(true)
                .number_of_values(1)
                .value_name("DIRECTIVE")
                .help("directives to put within include_cpp!")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("problem")
                .short("p")
                .long("problem")
                .required(true)
                .value_name("PROBLEM")
                .help("problem string we're looking for... may be in logs, or in generated C++, or generated .rs")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("creduce")
                .long("creduce")
                .required(true)
                .value_name("PATH")
                .help("creduce binary location")
                .default_value("/usr/bin/creduce")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("output")
                .short("o")
                .long("output")
                .value_name("OUTPUT")
                .help("where to write minimized output")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("keep")
                .short("k")
                .long("keep-dir")
                .help("keep the temporary directory for debugging purposes"),
        )
        .arg(Arg::with_name("creduce-args").last(true).multiple(true))
        .get_matches();
    run(matches).unwrap();
}

fn run(matches: ArgMatches) -> Result<(), std::io::Error> {
    let keep_tmp = matches.is_present("keep");
    let tmp_dir = TempDir::new()?;
    let r = do_run(matches, &tmp_dir);
    if keep_tmp {
        println!(
            "Keeping temp dir created at: {}",
            tmp_dir.into_path().to_str().unwrap()
        );
    }
    r
}

fn do_run(matches: ArgMatches, tmp_dir: &TempDir) -> Result<(), std::io::Error> {
    let incs: Vec<_> = matches
        .values_of("inc")
        .unwrap_or_default()
        .map(PathBuf::from)
        .collect();
    let defs: Vec<_> = matches.values_of("define").unwrap_or_default().collect();
    let headers: Vec<_> = matches.values_of("header").unwrap_or_default().collect();
    let listing_path = tmp_dir.path().join("listing.h");
    create_concatenated_header(&headers, &listing_path)?;
    let concat_path = tmp_dir.path().join("concat.h");
    announce_progress(&format!(
        "Preprocessing {:?} to {:?}",
        listing_path, concat_path
    ));
    preprocess(&listing_path, &concat_path, &incs, &defs)?;
    let rs_path = tmp_dir.path().join("input.rs");
    let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string())
        .chain(
            matches
                .values_of("directive")
                .unwrap_or_default()
                .map(|s| format!("{}\n", s)),
        )
        .collect();
    create_rs_file(&rs_path, &directives)?;
    run_sample_gen_cmd(&rs_path, &tmp_dir.path())?;
    let interestingness_test = tmp_dir.path().join("test.sh");
    create_interestingness_test(
        &interestingness_test,
        matches.value_of("problem").unwrap(),
        &rs_path,
    )?;
    run_interestingness_test(&interestingness_test);
    run_creduce(
        matches.value_of("creduce").unwrap(),
        &interestingness_test,
        &concat_path,
        matches.values_of("creduce-args").unwrap_or_default(),
    );
    let output_path = matches.value_of("output");
    match output_path {
        None => print_minimized_case(&concat_path)?,
        Some(output_path) => {
            std::fs::copy(&concat_path, &PathBuf::from(output_path))?;
        }
    };
    Ok(())
}

fn announce_progress(msg: &str) {
    println!("=== {} ===", msg);
}

fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Completed. Minimized test case:");
    let contents = std::fs::read_to_string(concat_path)?;
    println!("{}", contents);
    Ok(())
}

/// Arguments we always pass to creduce. This pass always seems to cause a crash
/// as far as I can tell, so always exclude it. It may be environment-dependent,
/// of course, but as I'm the primary user of this tool I am ruthlessly removing it.
const CREDUCE_STANDARD_ARGS: &[&str] = &["--remove-pass", "pass_line_markers"];

fn run_creduce<'a>(
    creduce_cmd: &str,
    interestingness_test: &Path,
    concat_path: &Path,
    creduce_args: impl Iterator<Item = &'a str>,
) {
    announce_progress("creduce");
    Command::new(creduce_cmd)
        .arg(interestingness_test.to_str().unwrap())
        .arg(concat_path.to_str().unwrap())
        .args(CREDUCE_STANDARD_ARGS)
        .args(creduce_args)
        .status()
        .expect("failed to creduce");
}

fn run_sample_gen_cmd(rs_file: &Path, tmp_dir: &Path) -> Result<(), std::io::Error> {
    let (gen_cmd, args) = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap())?;
    let args = args.collect::<Vec<_>>();
    let args_str = args.join(" ");
    announce_progress(&format!(
        "Running sample gen cmd: {} {}",
        gen_cmd.to_str().unwrap(),
        args_str
    ));
    Command::new(gen_cmd).args(args).status()?;
    Ok(())
}

fn format_gen_cmd(
    rs_file: &Path,
    dir: &str,
) -> Result<(PathBuf, impl Iterator<Item = String>), std::io::Error> {
    let me = std::env::current_exe()?;
    let gen = me.parent().unwrap().join("autocxx-gen");
    let args = [
        "-o".to_string(),
        dir.to_string(),
        "-I".to_string(),
        dir.to_string(),
        rs_file.to_str().unwrap().to_string(),
        "--gen-rs-complete".to_string(),
        "--gen-cpp".to_string(),
    ]
    .to_vec();
    Ok((gen, args.into_iter()))
}

fn create_interestingness_test(
    test_path: &Path,
    problem: &str,
    rs_file: &Path,
) -> Result<(), std::io::Error> {
    announce_progress("Creating interestingness test");
    // Ensure we refer to the input header by relative path
    // because creduce will invoke us in some other directory with
    // a copy thereof.
    let (gen_cmd, mut args) = format_gen_cmd(rs_file, "$(pwd)")?;
    let args = args.join(" ");
    let content = format!(
        indoc! {"
        #!/bin/sh
        ({} {} 2>&1 && cat gen.complete.rs && cat autocxxgen.h) | grep \"{}\"  >/dev/null 2>&1
    "},
        gen_cmd.to_str().unwrap(),
        args,
        problem
    );
    println!("Interestingness test:\n{}", content);
    {
        let mut file = File::create(test_path)?;
        file.write_all(content.as_bytes())?;
    }

    let mut perms = std::fs::metadata(&test_path)?.permissions();
    perms.set_mode(0o700);
    std::fs::set_permissions(&test_path, perms)?;
    Ok(())
}

fn run_interestingness_test(test_path: &Path) {
    announce_progress("Running interestingness test");
    let status = Command::new(test_path).status().unwrap();
    announce_progress(&format!(
        "Have run interestingness test - result is {:?}",
        status.code()
    ));
}

fn create_rs_file(rs_path: &Path, directives: &[String]) -> Result<(), std::io::Error> {
    announce_progress("Creating Rust input file");
    let mut file = File::create(rs_path)?;
    file.write_all("use autocxx::include_cpp;\ninclude_cpp! (\n".as_bytes())?;
    for directive in directives {
        file.write_all(directive.as_bytes())?;
    }
    file.write_all(");\n".as_bytes())?;
    Ok(())
}

fn create_concatenated_header(headers: &[&str], listing_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Creating preprocessed header");
    let mut file = File::create(listing_path)?;
    for header in headers {
        file.write_all(format!("#include \"{}\"\n", header).as_bytes())?;
    }
    Ok(())
}
<commit_msg>Facility to feed clang args to reduction.<commit_after>//! Crate to `#include` C++ headers in your Rust code, and generate
//! idiomatic bindings using `cxx`. See [include_cpp] for details.

// Copyright 2020 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::Write,
    os::unix::prelude::PermissionsExt,
    path::{Path, PathBuf},
    process::Command,
};

use autocxx_engine::preprocess;
use clap::{crate_authors, crate_version, App, Arg, ArgMatches};
use indoc::indoc;
use itertools::Itertools;
use tempfile::TempDir;

static LONG_HELP: &str = indoc! {"
Command line utility to minimize autocxx bug cases.

This is a wrapper for creduce.

Example command-line:
autocxx-reduce -I my-inc-dir -h my-header -d 'generate!(\"MyClass\")' -k -- --n 64 --remove-pass pass_line_markers
"};

fn main() {
    let matches = App::new("autocxx-reduce")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Reduce a C++ test case")
        .long_about(LONG_HELP)
        .arg(
            Arg::with_name("inc")
                .short("I")
                .long("inc")
                .multiple(true)
                .number_of_values(1)
                .value_name("INCLUDE DIRS")
                .help("include path")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("define")
                .short("D")
                .long("define")
                .multiple(true)
                .number_of_values(1)
                .value_name("DEFINE")
                .help("macro definition")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("header")
                .short("h")
                .long("header")
                .multiple(true)
                .number_of_values(1)
                .required(true)
                .value_name("HEADER")
                .help("header file name")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directive")
                .short("d")
                .long("directive")
                .multiple(true)
                .number_of_values(1)
                .value_name("DIRECTIVE")
                .help("directives to put within include_cpp!")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("problem")
                .short("p")
                .long("problem")
                .required(true)
                .value_name("PROBLEM")
                .help("problem string we're looking for... may be in logs, or in generated C++, or generated .rs")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("creduce")
                .long("creduce")
                .required(true)
                .value_name("PATH")
                .help("creduce binary location")
                .default_value("/usr/bin/creduce")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("output")
                .short("o")
                .long("output")
                .value_name("OUTPUT")
                .help("where to write minimized output")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("keep")
                .short("k")
                .long("keep-dir")
                .help("keep the temporary directory for debugging purposes"),
        )
        .arg(
            Arg::with_name("clang-args")
                .short("c")
                .long("clang-arg")
                .multiple(true)
                .value_name("CLANG_ARG")
                .help("Extra arguments to pass to Clang"),
        )
        .arg(Arg::with_name("creduce-args").last(true).multiple(true))
        .get_matches();
    run(matches).unwrap();
}

fn run(matches: ArgMatches) -> Result<(), std::io::Error> {
    let keep_tmp = matches.is_present("keep");
    let tmp_dir = TempDir::new()?;
    let r = do_run(matches, &tmp_dir);
    if keep_tmp {
        println!(
            "Keeping temp dir created at: {}",
            tmp_dir.into_path().to_str().unwrap()
        );
    }
    r
}

fn do_run(matches: ArgMatches, tmp_dir: &TempDir) -> Result<(), std::io::Error> {
    let incs: Vec<_> = matches
        .values_of("inc")
        .unwrap_or_default()
        .map(PathBuf::from)
        .collect();
    let defs: Vec<_> = matches.values_of("define").unwrap_or_default().collect();
    let headers: Vec<_> = matches.values_of("header").unwrap_or_default().collect();
    let listing_path = tmp_dir.path().join("listing.h");
    create_concatenated_header(&headers, &listing_path)?;
    let concat_path = tmp_dir.path().join("concat.h");
    announce_progress(&format!(
        "Preprocessing {:?} to {:?}",
        listing_path, concat_path
    ));
    preprocess(&listing_path, &concat_path, &incs, &defs)?;
    let rs_path = tmp_dir.path().join("input.rs");
    let directives: Vec<_> = std::iter::once("#include \"concat.h\"\n".to_string())
        .chain(
            matches
                .values_of("directive")
                .unwrap_or_default()
                .map(|s| format!("{}\n", s)),
        )
        .collect();
    create_rs_file(&rs_path, &directives)?;
    let extra_clang_args: Vec<_> = matches
        .values_of("clang-args")
        .unwrap_or_default()
        .collect();
    run_sample_gen_cmd(&rs_path, &tmp_dir.path(), &extra_clang_args)?;
    let interestingness_test = tmp_dir.path().join("test.sh");
    create_interestingness_test(
        &interestingness_test,
        matches.value_of("problem").unwrap(),
        &rs_path,
        &extra_clang_args,
    )?;
    run_interestingness_test(&interestingness_test);
    run_creduce(
        matches.value_of("creduce").unwrap(),
        &interestingness_test,
        &concat_path,
        matches.values_of("creduce-args").unwrap_or_default(),
    );
    let output_path = matches.value_of("output");
    match output_path {
        None => print_minimized_case(&concat_path)?,
        Some(output_path) => {
            std::fs::copy(&concat_path, &PathBuf::from(output_path))?;
        }
    };
    Ok(())
}

fn announce_progress(msg: &str) {
    println!("=== {} ===", msg);
}

fn print_minimized_case(concat_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Completed. Minimized test case:");
    let contents = std::fs::read_to_string(concat_path)?;
    println!("{}", contents);
    Ok(())
}

/// Arguments we always pass to creduce. This pass always seems to cause a crash
/// as far as I can tell, so always exclude it. It may be environment-dependent,
/// of course, but as I'm the primary user of this tool I am ruthlessly removing it.
const CREDUCE_STANDARD_ARGS: &[&str] = &["--remove-pass", "pass_line_markers"];

fn run_creduce<'a>(
    creduce_cmd: &str,
    interestingness_test: &Path,
    concat_path: &Path,
    creduce_args: impl Iterator<Item = &'a str>,
) {
    announce_progress("creduce");
    Command::new(creduce_cmd)
        .arg(interestingness_test.to_str().unwrap())
        .arg(concat_path.to_str().unwrap())
        .args(CREDUCE_STANDARD_ARGS)
        .args(creduce_args)
        .status()
        .expect("failed to creduce");
}

fn run_sample_gen_cmd(
    rs_file: &Path,
    tmp_dir: &Path,
    extra_clang_args: &[&str],
) -> Result<(), std::io::Error> {
    let (gen_cmd, args) = format_gen_cmd(rs_file, tmp_dir.to_str().unwrap(), extra_clang_args)?;
    let args = args.collect::<Vec<_>>();
    let args_str = args.join(" ");
    announce_progress(&format!(
        "Running sample gen cmd: {} {}",
        gen_cmd.to_str().unwrap(),
        args_str
    ));
    Command::new(gen_cmd).args(args).status()?;
    Ok(())
}

fn format_gen_cmd<'a>(
    rs_file: &Path,
    dir: &str,
    extra_clang_args: &'a [&str],
) -> Result<(PathBuf, impl Iterator<Item = String> + 'a), std::io::Error> {
    let me = std::env::current_exe()?;
    let gen = me.parent().unwrap().join("autocxx-gen");
    let args = [
        "-o".to_string(),
        dir.to_string(),
        "-I".to_string(),
        dir.to_string(),
        rs_file.to_str().unwrap().to_string(),
        "--gen-rs-complete".to_string(),
        "--gen-cpp".to_string(),
        "--".to_string(),
    ]
    .to_vec();
    Ok((
        gen,
        args.into_iter()
            .chain(extra_clang_args.iter().map(|s| s.to_string())),
    ))
}

fn create_interestingness_test(
    test_path: &Path,
    problem: &str,
    rs_file: &Path,
    extra_clang_args: &[&str],
) -> Result<(), std::io::Error> {
    announce_progress("Creating interestingness test");
    // Ensure we refer to the input header by relative path
    // because creduce will invoke us in some other directory with
    // a copy thereof.
    let (gen_cmd, mut args) = format_gen_cmd(rs_file, "$(pwd)", extra_clang_args)?;
    let args = args.join(" ");
    let content = format!(
        indoc! {"
        #!/bin/sh
        ({} {} 2>&1 && cat gen.complete.rs && cat autocxxgen.h) | grep \"{}\"  >/dev/null 2>&1
    "},
        gen_cmd.to_str().unwrap(),
        args,
        problem
    );
    println!("Interestingness test:\n{}", content);
    {
        let mut file = File::create(test_path)?;
        file.write_all(content.as_bytes())?;
    }

    let mut perms = std::fs::metadata(&test_path)?.permissions();
    perms.set_mode(0o700);
    std::fs::set_permissions(&test_path, perms)?;
    Ok(())
}

fn run_interestingness_test(test_path: &Path) {
    announce_progress("Running interestingness test");
    let status = Command::new(test_path).status().unwrap();
    announce_progress(&format!(
        "Have run interestingness test - result is {:?}",
        status.code()
    ));
}

fn create_rs_file(rs_path: &Path, directives: &[String]) -> Result<(), std::io::Error> {
    announce_progress("Creating Rust input file");
    let mut file = File::create(rs_path)?;
    file.write_all("use autocxx::include_cpp;\ninclude_cpp! (\n".as_bytes())?;
    for directive in directives {
        file.write_all(directive.as_bytes())?;
    }
    file.write_all(");\n".as_bytes())?;
    Ok(())
}

fn create_concatenated_header(headers: &[&str], listing_path: &Path) -> Result<(), std::io::Error> {
    announce_progress("Creating preprocessed header");
    let mut file = File::create(listing_path)?;
    for header in headers {
        file.write_all(format!("#include \"{}\"\n", header).as_bytes())?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>extern crate test_server;
extern crate modbus;

mod connection_tests {
    use modbus::tcp::{Config, Transport};
    use std::time::{Duration, Instant};

    #[test]
    fn test_connect_timeout() {
        let mut cfg = Config::default();
        cfg.tcp_connect_timeout = Some(Duration::from_millis(1000));
        let now = Instant::now();
        if let Err(_) = Transport::new_with_cfg("30.30.30.30", cfg) {
            let elapsed = now.elapsed().as_secs();
            assert_eq!(elapsed, 1, "Elapsed: {}", elapsed);
        }
    }

    #[test]
    fn test_default_timeout() {
        let cfg = Config::default();
        let now = Instant::now();
        if let Err(_) = Transport::new_with_cfg("30.30.30.30", cfg) {
            let elapsed = now.elapsed().as_secs();
            println!("Default OS TCP Timeout: {} secs", elapsed);
            assert!(true);
        }
    }
}

#[cfg(feature="modbus-server-tests")]
mod modbus_server_tests {
    use test_server::{ChildKiller, start_dummy_server};
    use modbus::tcp::{Config, Transport};
    use modbus::{Client, Coil};
    use modbus::scoped::{ScopedCoil, ScopedRegister, CoilDropFunction, RegisterDropFunction};

    fn start_dummy_server_with_cfg() -> (ChildKiller, Config) {
        let (k, port) = start_dummy_server(None);
        let mut cfg = Config::default();
        cfg.tcp_port = port;
        (k, cfg)
    }

    /// /////////////////////
    /// simple READ tests
    #[test]
    fn test_read_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_coils(0, 5).unwrap().len(), 5);
        assert!(trans.read_coils(0, 5).unwrap().iter().all(|c| *c == Coil::Off));
    }

    #[test]
    fn test_read_discrete_inputs() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_discrete_inputs(0, 5).unwrap().len(), 5);
        assert!(trans.read_discrete_inputs(0, 5).unwrap().iter().all(|c| *c == Coil::Off));
    }

    #[test]
    fn test_read_holding_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_holding_registers(0, 5).unwrap().len(), 5);
        assert!(trans.read_holding_registers(0, 5).unwrap().iter().all(|c| *c == 0));
    }

    #[test]
    fn test_read_input_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_input_registers(0, 5).unwrap().len(), 5);
        assert!(trans.read_input_registers(0, 5).unwrap().iter().all(|c| *c == 0));
    }

    /// /////////////////////
    /// simple WRITE tests
    #[test]
    fn test_write_single_coil() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_single_coil(0, Coil::On).is_ok());
    }

    #[test]
    fn test_write_single_register() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_single_register(0, 1).is_ok());
    }

    #[test]
    fn test_write_multiple_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_coils(0, &[Coil::On, Coil::Off]).is_ok());
        // assert!(write_multiple_coils(&mut trans, 0, &[]).is_err());
    }

    #[test]
    fn test_write_multiple_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_registers(0, &[0, 1, 2, 3]).is_ok());
        // assert!(write_multiple_registers(&mut trans, 0, &[]).is_err());
    }

    /// /////////////////////
    /// coil WRITE-READ tests
    #[test]
    fn test_write_read_single_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();

        assert!(trans.write_single_coil(1, Coil::On).is_ok());
        assert!(trans.write_single_coil(3, Coil::On).is_ok());
        assert_eq!(trans.read_coils(0, 5).unwrap(),
                   vec![Coil::Off, Coil::On, Coil::Off, Coil::On, Coil::Off]);
        assert_eq!(trans.read_coils(1, 5).unwrap(),
                   vec![Coil::On, Coil::Off, Coil::On, Coil::Off, Coil::Off]);
        assert!(trans.write_single_coil(10, Coil::On).is_ok());
        assert!(trans.write_single_coil(11, Coil::On).is_ok());
        assert_eq!(trans.read_coils(9, 4).unwrap(),
                   vec![Coil::Off, Coil::On, Coil::On, Coil::Off]);
        assert!(trans.write_single_coil(10, Coil::Off).is_ok());
        assert!(trans.write_single_coil(11, Coil::Off).is_ok());
        assert_eq!(trans.read_coils(9, 4).unwrap(),
                   vec![Coil::Off, Coil::Off, Coil::Off, Coil::Off]);
    }

    #[test]
    fn test_write_read_single_register() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_single_register(0, 23).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![23]);
        assert!(trans.write_single_register(0, 0).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0]);
        assert_eq!(trans.read_input_registers(0, 1).unwrap(), vec![0]);
        assert!(trans.write_single_register(0, 23).is_ok());
        assert!(trans.write_single_register(1, 24).is_ok());
        assert_eq!(trans.read_holding_registers(0, 2).unwrap(), vec![23, 24]);
    }

    #[test]
    fn test_write_read_multiple_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_coils(0, &[Coil::Off, Coil::On]).is_ok());
        assert_eq!(trans.read_coils(0, 3).unwrap(),
                   &[Coil::Off, Coil::On, Coil::Off]);
        assert!(trans.write_multiple_coils(0, &[Coil::On; 9]).is_ok());
        assert_eq!(trans.read_coils(0, 9).unwrap(), &[Coil::On; 9]);
    }

    #[test]
    fn test_write_read_multiple_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        // assert!(write_multiple_registers(&mut trans, 0, &[]).is_err());
        assert!(trans.write_multiple_registers(0, &[23]).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[23]);
        assert!(trans.write_multiple_registers(0, &[1, 2, 3]).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[1]);
        assert_eq!(trans.read_holding_registers(1, 1).unwrap(), &[2]);
        assert_eq!(trans.read_holding_registers(2, 1).unwrap(), &[3]);
        assert_eq!(trans.read_holding_registers(0, 3).unwrap(), &[1, 2, 3]);
    }

    #[test]
    fn test_write_too_big() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_registers(0, &[0xdead; 123]).is_ok());
        assert!(trans.write_multiple_registers(0, &[0xdead; 124]).is_err());
    }

    #[test]
    fn test_scoped_coil() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::On).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::Off]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::On]);

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::Off).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::On]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::Off]);

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::Toggle).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::Off]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::On]);

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::Toggle).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::On]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::Off]);

        // coil address 1
        {
            let mut auto = ScopedCoil::new(&mut trans, 1, CoilDropFunction::Toggle).unwrap();
            assert_eq!(auto.mut_transport().read_coils(1, 1).unwrap(),
                       vec![Coil::Off]);
        }
        assert_eq!(trans.read_coils(1, 1).unwrap(), vec![Coil::On]);

    }

    #[test]
    fn test_scoped_register() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Value(0xbeef))
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0000]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0xbeef]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Zero).unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0xbeef]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0000]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Increment)
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0000]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0001]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Increment)
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0001]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0002]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Decrement)
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0002]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0001]);

        {
            let fun = |v| v + 0xbeee;
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Fun(&fun))
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0001]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0xbeef]);

    }
}
<commit_msg>Changes according to code review in PR #26<commit_after>extern crate test_server;
extern crate modbus;

mod connection_tests {
    use modbus::tcp::{Config, Transport};
    use std::time::{Duration, Instant};

    #[test]
    fn test_connect_timeout() {
        let mut cfg = Config::default();
        cfg.tcp_connect_timeout = Some(Duration::from_millis(1000));
        let now = Instant::now();
        if Transport::new_with_cfg("30.30.30.30", cfg).is_err() {
            let elapsed = now.elapsed().as_secs();
            assert_eq!(elapsed, 1, "Elapsed: {}", elapsed);
        }
    }
}

#[cfg(feature="modbus-server-tests")]
mod modbus_server_tests {
    use test_server::{ChildKiller, start_dummy_server};
    use modbus::tcp::{Config, Transport};
    use modbus::{Client, Coil};
    use modbus::scoped::{ScopedCoil, ScopedRegister, CoilDropFunction, RegisterDropFunction};

    fn start_dummy_server_with_cfg() -> (ChildKiller, Config) {
        let (k, port) = start_dummy_server(None);
        let mut cfg = Config::default();
        cfg.tcp_port = port;
        (k, cfg)
    }

    /// /////////////////////
    /// simple READ tests
    #[test]
    fn test_read_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_coils(0, 5).unwrap().len(), 5);
        assert!(trans.read_coils(0, 5).unwrap().iter().all(|c| *c == Coil::Off));
    }

    #[test]
    fn test_read_discrete_inputs() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_discrete_inputs(0, 5).unwrap().len(), 5);
        assert!(trans.read_discrete_inputs(0, 5).unwrap().iter().all(|c| *c == Coil::Off));
    }

    #[test]
    fn test_read_holding_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_holding_registers(0, 5).unwrap().len(), 5);
        assert!(trans.read_holding_registers(0, 5).unwrap().iter().all(|c| *c == 0));
    }

    #[test]
    fn test_read_input_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert_eq!(trans.read_input_registers(0, 5).unwrap().len(), 5);
        assert!(trans.read_input_registers(0, 5).unwrap().iter().all(|c| *c == 0));
    }

    /// /////////////////////
    /// simple WRITE tests
    #[test]
    fn test_write_single_coil() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_single_coil(0, Coil::On).is_ok());
    }

    #[test]
    fn test_write_single_register() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_single_register(0, 1).is_ok());
    }

    #[test]
    fn test_write_multiple_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_coils(0, &[Coil::On, Coil::Off]).is_ok());
        // assert!(write_multiple_coils(&mut trans, 0, &[]).is_err());
    }

    #[test]
    fn test_write_multiple_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_registers(0, &[0, 1, 2, 3]).is_ok());
        // assert!(write_multiple_registers(&mut trans, 0, &[]).is_err());
    }

    /// /////////////////////
    /// coil WRITE-READ tests
    #[test]
    fn test_write_read_single_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();

        assert!(trans.write_single_coil(1, Coil::On).is_ok());
        assert!(trans.write_single_coil(3, Coil::On).is_ok());
        assert_eq!(trans.read_coils(0, 5).unwrap(),
                   vec![Coil::Off, Coil::On, Coil::Off, Coil::On, Coil::Off]);
        assert_eq!(trans.read_coils(1, 5).unwrap(),
                   vec![Coil::On, Coil::Off, Coil::On, Coil::Off, Coil::Off]);
        assert!(trans.write_single_coil(10, Coil::On).is_ok());
        assert!(trans.write_single_coil(11, Coil::On).is_ok());
        assert_eq!(trans.read_coils(9, 4).unwrap(),
                   vec![Coil::Off, Coil::On, Coil::On, Coil::Off]);
        assert!(trans.write_single_coil(10, Coil::Off).is_ok());
        assert!(trans.write_single_coil(11, Coil::Off).is_ok());
        assert_eq!(trans.read_coils(9, 4).unwrap(),
                   vec![Coil::Off, Coil::Off, Coil::Off, Coil::Off]);
    }

    #[test]
    fn test_write_read_single_register() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_single_register(0, 23).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![23]);
        assert!(trans.write_single_register(0, 0).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0]);
        assert_eq!(trans.read_input_registers(0, 1).unwrap(), vec![0]);
        assert!(trans.write_single_register(0, 23).is_ok());
        assert!(trans.write_single_register(1, 24).is_ok());
        assert_eq!(trans.read_holding_registers(0, 2).unwrap(), vec![23, 24]);
    }

    #[test]
    fn test_write_read_multiple_coils() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_coils(0, &[Coil::Off, Coil::On]).is_ok());
        assert_eq!(trans.read_coils(0, 3).unwrap(),
                   &[Coil::Off, Coil::On, Coil::Off]);
        assert!(trans.write_multiple_coils(0, &[Coil::On; 9]).is_ok());
        assert_eq!(trans.read_coils(0, 9).unwrap(), &[Coil::On; 9]);
    }

    #[test]
    fn test_write_read_multiple_registers() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        // assert!(write_multiple_registers(&mut trans, 0, &[]).is_err());
        assert!(trans.write_multiple_registers(0, &[23]).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[23]);
        assert!(trans.write_multiple_registers(0, &[1, 2, 3]).is_ok());
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[1]);
        assert_eq!(trans.read_holding_registers(1, 1).unwrap(), &[2]);
        assert_eq!(trans.read_holding_registers(2, 1).unwrap(), &[3]);
        assert_eq!(trans.read_holding_registers(0, 3).unwrap(), &[1, 2, 3]);
    }

    #[test]
    fn test_write_too_big() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
        assert!(trans.write_multiple_registers(0, &[0xdead; 123]).is_ok());
        assert!(trans.write_multiple_registers(0, &[0xdead; 124]).is_err());
    }

    #[test]
    fn test_scoped_coil() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::On).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::Off]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::On]);

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::Off).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::On]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::Off]);

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::Toggle).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::Off]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::On]);

        {
            let mut auto = ScopedCoil::new(&mut trans, 0, CoilDropFunction::Toggle).unwrap();
            assert_eq!(auto.mut_transport().read_coils(0, 1).unwrap(),
                       vec![Coil::On]);
        }
        assert_eq!(trans.read_coils(0, 1).unwrap(), vec![Coil::Off]);

        // coil address 1
        {
            let mut auto = ScopedCoil::new(&mut trans, 1, CoilDropFunction::Toggle).unwrap();
            assert_eq!(auto.mut_transport().read_coils(1, 1).unwrap(),
                       vec![Coil::Off]);
        }
        assert_eq!(trans.read_coils(1, 1).unwrap(), vec![Coil::On]);

    }

    #[test]
    fn test_scoped_register() {
        let (_s, cfg) = start_dummy_server_with_cfg();
        let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap();

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Value(0xbeef))
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0000]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0xbeef]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Zero).unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0xbeef]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0000]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Increment)
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0000]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0001]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Increment)
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0001]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0002]);

        {
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Decrement)
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0002]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0x0001]);

        {
            let fun = |v| v + 0xbeee;
            let mut auto = ScopedRegister::new(&mut trans, 0, RegisterDropFunction::Fun(&fun))
                .unwrap();
            assert_eq!(auto.mut_transport().read_holding_registers(0, 1).unwrap(),
                       vec![0x0001]);
        }
        assert_eq!(trans.read_holding_registers(0, 1).unwrap(), vec![0xbeef]);

    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate json;
extern crate requests;

// use requests::Codes;
use requests::{delete, get, head, post, put};
use requests::Request;

#[test]
fn simple_get() {
    const URL: &'static str = "http://httpbin.org/get";
    let res = get(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
    let data = res.json().unwrap();
    println!("{:?}", data);
    assert_eq!(data["url"], URL);
    assert_eq!(data["headers"]["Host"], "httpbin.org");
    assert_eq!(data["headers"]["User-Agent"],
               concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
}

#[test]
fn simple_post() {
    const URL: &'static str = "http://httpbin.org/post";
    let res = post(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
}

#[test]
fn simple_put() {
    const URL: &'static str = "http://httpbin.org/put";
    let res = put(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
}

#[test]
fn simple_head() {
    const URL: &'static str = "http://httpbin.org/get";
    let res = head(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
}

#[test]
fn simple_delete() {
    const URL: &'static str = "http://httpbin.org/delete";
    let res = delete(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
}

#[test]
fn user_agent() {
    let useragent = concat!("{\n  \"user-agent\": \"requests-rs/",
                            env!("CARGO_PKG_VERSION"),
                            "\"\n}\n");
    const URL: &'static str = "http://httpbin.org/user-agent";
    let res = get(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
    assert_eq!(res.text(), Some(useragent));
}

#[test]
fn custom_user_agent() {
    const UA: &'static str = concat!("requests-rs-tests/", env!("CARGO_PKG_VERSION"));
    const URL: &'static str = "http://httpbin.org/user-agent";
    let mut request = Request::new();
    request.user_agent(UA);
    let res = request.get(URL).unwrap();
    assert_eq!(res.url(), URL);
    assert_eq!(res.status_code(), hyper::Ok);
    assert_eq!(res.reason(), "OK");
    assert!(res.is_json());

    let ua = res.json().unwrap();
    assert_eq!(ua["user-agent"], UA);
}

#[test]
fn user_agent_json() {

    const URL: &'static str = "http://httpbin.org/user-agent";
    let res = get(URL).unwrap();
    assert!(res.is_json());

    let ua = res.json().unwrap();
    assert_eq!(ua["user-agent"],
               concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
}

#[test]
fn content() {
    const URL: &'static str = "http://httpbin.org/headers";
    let res = get(URL).unwrap();
    let content = concat!("{\n  \"headers\": {\n    \"Host\": \"httpbin.org\",",
                          " \n    \"User-Agent\": \"requests-rs/",
                          env!("CARGO_PKG_VERSION"),
                          "\"\n  }\n}\n");

    assert_eq!(res.content(), &content.as_bytes());
}

macro_rules! status_code_test {
    ($($name:ident: $numeric:expr,)+) => {
        $(#[test]
        fn $name() {
            let res = get(&format!("http://httpbin.org/status/{}", $numeric)).unwrap();
            println!("{}", res.text().unwrap());
            assert_eq!(res.status_code(), hyper::status::StatusCode::from_u16($numeric));
        })+
    }
}

status_code_test! {
    status_code_100: 100,
    status_code_101: 101,
    status_code_102: 102,
    status_code_200: 200,
    status_code_201: 201,
    status_code_202: 202,
    status_code_203: 203,
    status_code_400: 400,
    status_code_401: 401,
    status_code_402: 402,
    status_code_403: 403,
    status_code_404: 404,
    status_code_500: 500,
}
<commit_msg>Refactor common code into aux function<commit_after>extern crate hyper;
extern crate json;
extern crate requests;

// use requests::Codes;
use requests::{delete, get, head, post, put};
use requests::{Request, Response};

fn assert_response_is_ok(response: &Response, url: &str) {
    assert_eq!(response.url(), url);
    assert_eq!(response.status_code(), hyper::Ok);
    assert_eq!(response.reason(), "OK");
}

#[test]
fn simple_get() {
    const URL: &'static str = "http://httpbin.org/get";
    let res = get(URL).unwrap();
    assert_response_is_ok(&res, URL);
    let data = res.json().unwrap();
    println!("{:?}", data);
    assert_eq!(data["url"], URL);
    assert_eq!(data["headers"]["Host"], "httpbin.org");
    assert_eq!(data["headers"]["User-Agent"],
               concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
}

#[test]
fn simple_post() {
    const URL: &'static str = "http://httpbin.org/post";
    let res = post(URL).unwrap();
    assert_response_is_ok(&res, URL);
}

#[test]
fn simple_put() {
    const URL: &'static str = "http://httpbin.org/put";
    let res = put(URL).unwrap();
    assert_response_is_ok(&res, URL);
}

#[test]
fn simple_head() {
    const URL: &'static str = "http://httpbin.org/get";
    let res = head(URL).unwrap();
    assert_response_is_ok(&res, URL);
}

#[test]
fn simple_delete() {
    const URL: &'static str = "http://httpbin.org/delete";
    let res = delete(URL).unwrap();
    assert_response_is_ok(&res, URL);
}

#[test]
fn user_agent() {
    let useragent = concat!("{\n  \"user-agent\": \"requests-rs/",
                            env!("CARGO_PKG_VERSION"),
                            "\"\n}\n");
    const URL: &'static str = "http://httpbin.org/user-agent";
    let res = get(URL).unwrap();
    assert_response_is_ok(&res, URL);
    assert_eq!(res.text(), Some(useragent));
}

#[test]
fn custom_user_agent() {
    const UA: &'static str = concat!("requests-rs-tests/", env!("CARGO_PKG_VERSION"));
    const URL: &'static str = "http://httpbin.org/user-agent";
    let mut request = Request::new();
    request.user_agent(UA);
    let res = request.get(URL).unwrap();
    assert_response_is_ok(&res, URL);
    assert!(res.is_json());

    let ua = res.json().unwrap();
    assert_eq!(ua["user-agent"], UA);
}

#[test]
fn user_agent_json() {

    const URL: &'static str = "http://httpbin.org/user-agent";
    let res = get(URL).unwrap();
    assert_response_is_ok(&res, URL);
    assert!(res.is_json());

    let ua = res.json().unwrap();
    assert_eq!(ua["user-agent"],
               concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
}

#[test]
fn content() {
    const URL: &'static str = "http://httpbin.org/headers";
    let content = concat!("{\n  \"headers\": {\n    \"Host\": \"httpbin.org\",",
                          " \n    \"User-Agent\": \"requests-rs/",
                          env!("CARGO_PKG_VERSION"),
                          "\"\n  }\n}\n");

    let res = get(URL).unwrap();
    assert_response_is_ok(&res, URL);
    assert_eq!(res.content(), &content.as_bytes());
}

macro_rules! status_code_test {
    ($($name:ident: $numeric:expr,)+) => {
        $(#[test]
        fn $name() {
            let res = get(&format!("http://httpbin.org/status/{}", $numeric)).unwrap();
            println!("{}", res.text().unwrap());
            assert_eq!(res.status_code(), hyper::status::StatusCode::from_u16($numeric));
        })+
    }
}

status_code_test! {
    status_code_100: 100,
    status_code_101: 101,
    status_code_102: 102,
    status_code_200: 200,
    status_code_201: 201,
    status_code_202: 202,
    status_code_203: 203,
    status_code_400: 400,
    status_code_401: 401,
    status_code_402: 402,
    status_code_403: 403,
    status_code_404: 404,
    status_code_500: 500,
}
<|endoftext|>


<commit_before>use std::io::{Read};
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::collections::HashMap;

use mime::Mime;
use mime_guess::guess_mime_type;

use tar::Archive;

pub struct Content {
	pub mime: Mime,
	pub payload: Vec<u8>,
}

pub type StaticMap = HashMap<String, Content>;

pub trait StaticProvider: Sync + Send + 'static {
	fn get_content(&self, path: &str) -> Option<&Content>;
}

impl StaticProvider for StaticMap {
	fn get_content(&self, path: &str) -> Option<&Content> {
		self.get(path)
	}
}

pub struct TryRewrite {
	map: StaticMap,
	path: String,
}

impl TryRewrite {
	pub fn new(map: StaticMap, path: String) -> Self {
		TryRewrite { map: map, path: path }
	}
}

impl StaticProvider for TryRewrite {
	fn get_content(&self, path: &str) -> Option<&Content> {
		let result = self.map.get(path);
		if result.is_none() {
			self.map.get(&self.path)
		} else {
			result
		}
	}
}

pub fn provider_from_folder(path: &Path) -> StaticMap {
	let mut result = StaticMap::new();
	let path = PathBuf::from(path);
	read_dir_with_subdirs(path, "".to_owned(), &mut result);
	result
}

pub fn provider_from_tar(path: &Path) -> StaticMap {
	let mut result = StaticMap::new();
	let arch_file = File::open(path).unwrap();
	let mut arch = Archive::new(arch_file);

	for file in arch.entries().unwrap() {
		let mut file = file.unwrap();

		match file.header().size() {
			Ok(0) => (),
			Ok(size) => {

				let str_path = {
					let mut pb = PathBuf::new();
					let file_path = file.header().path().unwrap();
					pb.push(file_path);
					let mut s = String::new();
					s.push_str(pb.to_str().unwrap());
					s.remove(0); // Drop first `.` in `./filename.ext`
					s
				};
				let mut payload = Vec::with_capacity(size as usize);
				file.read_to_end(&mut payload).unwrap();
				let content = Content {
					mime: guess_mime_type(path),
					payload: payload,
				};
				result.insert(str_path, content);
			},
			_ => ()
		}
	}
	result
}

fn read_file_to_vec(path: &Path) -> Content {
	match File::open(path) {
		Ok(mut file) => {
			let mut payload = Vec::new();
			file.read_to_end(&mut payload).unwrap();
			Content {
				mime: guess_mime_type(path),
				payload: payload,
			}
		}
		Err(_) => {
			panic!("Can't read file.");
		},
	}
}

fn read_dir_with_subdirs(path: PathBuf, prefix: String, map: &mut StaticMap) {
	for entry in fs::read_dir(&path).unwrap() {
		let entry = entry.unwrap();
		let file_name = entry.file_name();
		let new_path = path.join(&file_name);

		let mut new_prefix = prefix.clone();
		new_prefix.push('/');
		new_prefix.push_str(&file_name.to_str().unwrap());

		let meta = entry.metadata().unwrap();
		if meta.is_dir() {
			read_dir_with_subdirs(new_path, new_prefix, map);
		} else if meta.is_file() {
			let content = read_file_to_vec(&new_path);
			map.insert(new_prefix, content);
		}
	}
}

<commit_msg>Fix tar provider bug<commit_after>use std::io::{Read};
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::collections::HashMap;

use mime::Mime;
use mime_guess::guess_mime_type;

use tar::Archive;

pub struct Content {
	pub mime: Mime,
	pub payload: Vec<u8>,
}

pub type StaticMap = HashMap<String, Content>;

pub trait StaticProvider: Sync + Send + 'static {
	fn get_content(&self, path: &str) -> Option<&Content>;
}

impl StaticProvider for StaticMap {
	fn get_content(&self, path: &str) -> Option<&Content> {
		self.get(path)
	}
}

pub struct TryRewrite {
	map: StaticMap,
	path: String,
}

impl TryRewrite {
	pub fn new(map: StaticMap, path: String) -> Self {
		TryRewrite { map: map, path: path }
	}
}

impl StaticProvider for TryRewrite {
	fn get_content(&self, path: &str) -> Option<&Content> {
		let result = self.map.get(path);
		if result.is_none() {
			self.map.get(&self.path)
		} else {
			result
		}
	}
}

pub fn provider_from_folder(path: &Path) -> StaticMap {
	let mut result = StaticMap::new();
	let path = PathBuf::from(path);
	read_dir_with_subdirs(path, "".to_owned(), &mut result);
	result
}

pub fn provider_from_tar(path: &Path) -> StaticMap {
	let mut result = StaticMap::new();
	let arch_file = File::open(path).unwrap();
	let mut arch = Archive::new(arch_file);

	for file in arch.entries().unwrap() {
		let mut file = file.unwrap();

		match file.header().size() {
			Ok(0) => (),
			Ok(size) => {

				let str_path = {
					let mut pb = PathBuf::new();
					let file_path = file.header().path().unwrap();
					pb.push(file_path);
					let mut s = String::new();
					s.push_str(pb.to_str().unwrap());
					s.remove(0); // Drop first `.` in `./filename.ext`
					s
				};
				let mut payload = Vec::with_capacity(size as usize);
				file.read_to_end(&mut payload).unwrap();
				let content = Content {
					mime: guess_mime_type(&str_path),
					payload: payload,
				};
				result.insert(str_path, content);
			},
			_ => ()
		}
	}
	result
}

fn read_file_to_vec(path: &Path) -> Content {
	match File::open(path) {
		Ok(mut file) => {
			let mut payload = Vec::new();
			file.read_to_end(&mut payload).unwrap();
			Content {
				mime: guess_mime_type(path),
				payload: payload,
			}
		}
		Err(_) => {
			panic!("Can't read file.");
		},
	}
}

fn read_dir_with_subdirs(path: PathBuf, prefix: String, map: &mut StaticMap) {
	for entry in fs::read_dir(&path).unwrap() {
		let entry = entry.unwrap();
		let file_name = entry.file_name();
		let new_path = path.join(&file_name);

		let mut new_prefix = prefix.clone();
		new_prefix.push('/');
		new_prefix.push_str(&file_name.to_str().unwrap());

		let meta = entry.metadata().unwrap();
		if meta.is_dir() {
			read_dir_with_subdirs(new_path, new_prefix, map);
		} else if meta.is_file() {
			let content = read_file_to_vec(&new_path);
			map.insert(new_prefix, content);
		}
	}
}

<|endoftext|>


<commit_before>// Copyright 2018 The Exonum Team
//
// 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
//
//   http://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.

//! This is a basic balance service. The only available operation is a wallet balance update.
//! Regression test for transactions processing is built on a base of this service.
extern crate bodyparser;
#[macro_use]
extern crate exonum;
extern crate futures;
extern crate iron;
extern crate router;
extern crate serde;
extern crate serde_json;

pub mod schema {
    use exonum::storage::{Entry, Fork, Snapshot};

    pub struct BalanceSchema<T> {
        view: T,
    }

    impl<T: AsRef<Snapshot>> BalanceSchema<T> {
        pub fn new(view: T) -> Self {
            Self { view }
        }

        pub fn balance(&self) -> Entry<&Snapshot, u64> {
            Entry::new("balance", self.view.as_ref())
        }
    }

    impl<'a> BalanceSchema<&'a mut Fork> {
        pub fn balance_mut(&mut self) -> Entry<&mut Fork, u64> {
            Entry::new("balance", self.view)
        }
    }
}

pub mod transactions {
    use service::SERVICE_ID;

    transactions! {
        pub BalanceTransactions {
            const SERVICE_ID = SERVICE_ID;

            struct TxAddBalance {
                amount: u64,
                seed: u64
            }
        }
    }
}

pub mod contracts {
    use exonum::blockchain::{ExecutionResult, Transaction};
    use exonum::storage::Fork;

    use transactions::TxAddBalance;
    use schema::BalanceSchema;

    impl Transaction for TxAddBalance {
        fn verify(&self) -> bool {
            true
        }

        fn execute(&self, view: &mut Fork) -> ExecutionResult {
            let mut schema = BalanceSchema::new(view);
            let new_balance = schema.balance().get().unwrap() + self.amount();
            schema.balance_mut().set(new_balance);

            Ok(())
        }
    }
}

pub mod service {
    use exonum::blockchain::{Service, TimeoutAdjusterConfig, Transaction, TransactionSet};
    use exonum::{encoding, messages::RawTransaction};
    use exonum::crypto::{gen_keypair, Hash};
    use exonum::storage::{Database, Fork, MemoryDB, Snapshot};
    use exonum::node::{ExternalMessage, Node, TransactionSend};
    use exonum::helpers;
    use serde_json::Value;

    use transactions::{BalanceTransactions, TxAddBalance};
    use schema::BalanceSchema;

    use std::sync::Arc;
    use std::thread;
    use std::time;

    pub const SERVICE_ID: u16 = 1;

    pub struct BalanceService();

    impl Service for BalanceService {
        fn service_id(&self) -> u16 {
            SERVICE_ID
        }

        fn service_name(&self) -> &str {
            "balance"
        }

        fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
            vec![]
        }

        fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
            let tx = BalanceTransactions::tx_from_raw(raw)?;
            Ok(tx.into())
        }

        fn initialize(&self, fork: &mut Fork) -> Value {
            let mut schema = BalanceSchema::new(fork);
            schema.balance_mut().set(0);
            Value::Null
        }
    }

    #[test]
    fn test_duplicated_transaction() {
        let (_, private_key) = gen_keypair();

        let db = Arc::from(Box::new(MemoryDB::new()) as Box<Database>) as Arc<Database>;
        let mut node_cfg = helpers::generate_testnet_config(1, 16_500)[0].clone();

        // Override timeouts to little values, so we won't have to wait for consensus too long.
        node_cfg.genesis.consensus.timeout_adjuster =
            TimeoutAdjusterConfig::Constant { timeout: 10 };
        node_cfg.genesis.consensus.round_timeout = 20;

        let service = Box::new(BalanceService());
        let node = Node::new(db.clone(), vec![service], node_cfg.clone());
        let api_tx = node.channel();

        let node_thread = thread::spawn(move || {
            node.run().unwrap();
        });

        let tx_orig = Box::new(TxAddBalance::new(10, 0, &private_key));
        let tx_copy = tx_orig.clone();

        // Send two identical transactions.
        api_tx.send(tx_orig).unwrap();

        api_tx.send(tx_copy).unwrap();

        // Wait to be sure that transaction was processed.
        thread::sleep(time::Duration::from_millis(100));

        // Shut down the node
        api_tx
            .send_external_message(ExternalMessage::Shutdown)
            .unwrap();

        node_thread.join().unwrap();

        // Check that only one transaction was accepted.
        let schema = BalanceSchema::new(db.snapshot());
        let balance = schema.balance().get().unwrap();

        assert_eq!(balance, 10);
    }
}
<commit_msg>Increased time of awaiting in balance service test (#657)<commit_after>// Copyright 2018 The Exonum Team
//
// 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
//
//   http://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.

//! This is a basic balance service. The only available operation is a wallet balance update.
//! Regression test for transactions processing is built on a base of this service.
extern crate bodyparser;
#[macro_use]
extern crate exonum;
extern crate futures;
extern crate iron;
extern crate router;
extern crate serde;
extern crate serde_json;

pub mod schema {
    use exonum::storage::{Entry, Fork, Snapshot};

    pub struct BalanceSchema<T> {
        view: T,
    }

    impl<T: AsRef<Snapshot>> BalanceSchema<T> {
        pub fn new(view: T) -> Self {
            Self { view }
        }

        pub fn balance(&self) -> Entry<&Snapshot, u64> {
            Entry::new("balance", self.view.as_ref())
        }
    }

    impl<'a> BalanceSchema<&'a mut Fork> {
        pub fn balance_mut(&mut self) -> Entry<&mut Fork, u64> {
            Entry::new("balance", self.view)
        }
    }
}

pub mod transactions {
    use service::SERVICE_ID;

    transactions! {
        pub BalanceTransactions {
            const SERVICE_ID = SERVICE_ID;

            struct TxAddBalance {
                amount: u64,
                seed: u64
            }
        }
    }
}

pub mod contracts {
    use exonum::blockchain::{ExecutionResult, Transaction};
    use exonum::storage::Fork;

    use transactions::TxAddBalance;
    use schema::BalanceSchema;

    impl Transaction for TxAddBalance {
        fn verify(&self) -> bool {
            true
        }

        fn execute(&self, view: &mut Fork) -> ExecutionResult {
            let mut schema = BalanceSchema::new(view);
            let new_balance = schema.balance().get().unwrap() + self.amount();
            schema.balance_mut().set(new_balance);

            Ok(())
        }
    }
}

pub mod service {
    use exonum::blockchain::{Service, TimeoutAdjusterConfig, Transaction, TransactionSet};
    use exonum::{encoding, messages::RawTransaction};
    use exonum::crypto::{gen_keypair, Hash};
    use exonum::storage::{Database, Fork, MemoryDB, Snapshot};
    use exonum::node::{ExternalMessage, Node, TransactionSend};
    use exonum::helpers;
    use serde_json::Value;

    use transactions::{BalanceTransactions, TxAddBalance};
    use schema::BalanceSchema;

    use std::sync::Arc;
    use std::thread;
    use std::time;

    pub const SERVICE_ID: u16 = 1;

    pub struct BalanceService();

    impl Service for BalanceService {
        fn service_id(&self) -> u16 {
            SERVICE_ID
        }

        fn service_name(&self) -> &str {
            "balance"
        }

        fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
            vec![]
        }

        fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
            let tx = BalanceTransactions::tx_from_raw(raw)?;
            Ok(tx.into())
        }

        fn initialize(&self, fork: &mut Fork) -> Value {
            let mut schema = BalanceSchema::new(fork);
            schema.balance_mut().set(0);
            Value::Null
        }
    }

    #[test]
    fn test_duplicated_transaction() {
        let (_, private_key) = gen_keypair();

        let db = Arc::from(Box::new(MemoryDB::new()) as Box<Database>) as Arc<Database>;
        let mut node_cfg = helpers::generate_testnet_config(1, 16_500)[0].clone();

        // Override timeouts to little values, so we won't have to wait for consensus too long.
        node_cfg.genesis.consensus.timeout_adjuster =
            TimeoutAdjusterConfig::Constant { timeout: 20 };
        node_cfg.genesis.consensus.round_timeout = 40;

        let service = Box::new(BalanceService());
        let node = Node::new(db.clone(), vec![service], node_cfg.clone());
        let api_tx = node.channel();

        let node_thread = thread::spawn(move || {
            node.run().unwrap();
        });

        let tx_orig = Box::new(TxAddBalance::new(10, 0, &private_key));
        let tx_copy = tx_orig.clone();

        // Send two identical transactions.
        api_tx.send(tx_orig).unwrap();

        api_tx.send(tx_copy).unwrap();

        // Wait to be sure that transaction was processed.
        thread::sleep(time::Duration::from_millis(200));

        // Shut down the node
        api_tx
            .send_external_message(ExternalMessage::Shutdown)
            .unwrap();

        node_thread.join().unwrap();

        // Check that only one transaction was accepted.
        let schema = BalanceSchema::new(db.snapshot());
        let balance = schema.balance().get().unwrap();

        assert_eq!(balance, 10);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Exonum Team
//
// 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
//
//   http://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.

//! Property testing for proofs of existence / absence in `ProofMapIndex`.
//!
//! To adjust the number of test cases for each test, set the `PROPTEST_CASES` environment
//! variable as per `proptest` docs. The number of test cases for large tests will be scaled
//! back automatically. A reasonable value for `PROPTEST_CASES` is `256`
//! (default; results in running time ~30 sec for larger tests) or more. The run time
//! scales linearly with the number of cases.

// cspell:ignore proptest

extern crate exonum;
#[macro_use]
extern crate proptest;

use exonum::storage::{
    proof_map_index::{ProofMapKey, ProofPath}, Database, MapProof, MemoryDB, ProofMapIndex,
    Snapshot, StorageValue,
};
use proptest::{prelude::*, test_runner::Config};

use std::{
    collections::{BTreeMap, BTreeSet}, fmt::Debug, ops::Range,
};

const INDEX_NAME: &str = "index";

fn check_map_proof<T, K, V>(proof: MapProof<K, V>, key: Option<K>, table: &ProofMapIndex<T, K, V>)
where
    T: AsRef<Snapshot>,
    K: ProofMapKey + PartialEq + Debug,
    V: StorageValue + PartialEq + Debug,
{
    let entries = key.map(|key| {
        let value = table.get(&key).unwrap();
        (key, value)
    });

    let proof = proof.check().unwrap();
    assert_eq!(
        proof.entries(),
        entries
            .iter()
            .map(|&(ref k, ref v)| (k, v))
            .collect::<Vec<_>>()
    );
    assert_eq!(proof.merkle_root(), table.merkle_root());
}

fn check_map_multiproof<T, K, V>(
    proof: MapProof<K, V>,
    keys: BTreeSet<K>,
    table: &ProofMapIndex<T, K, V>,
) where
    T: AsRef<Snapshot>,
    K: ProofMapKey + Clone + PartialEq + Debug,
    V: StorageValue + PartialEq + Debug,
{
    let (entries, missing_keys) = {
        let mut entries: Vec<(K, V)> = Vec::new();
        let mut missing_keys: Vec<K> = Vec::new();

        for key in keys {
            if table.contains(&key) {
                let value = table.get(&key).unwrap();
                entries.push((key, value));
            } else {
                missing_keys.push(key);
            }
        }

        // Sort entries and missing keys by the order imposed by the `ProofPath`
        // serialization of the keys
        entries.sort_unstable_by(|&(ref x, _), &(ref y, _)| {
            ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap()
        });
        missing_keys
            .sort_unstable_by(|x, y| ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap());

        (entries, missing_keys)
    };

    let proof = proof.check().unwrap();
    assert_eq!(proof.merkle_root(), table.merkle_root());
    assert_eq!(missing_keys.iter().collect::<Vec<&_>>(), {
        let mut actual_keys = proof.missing_keys();
        actual_keys
            .sort_unstable_by(|&x, &y| ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap());
        actual_keys
    });
    assert_eq!(
        entries
            .iter()
            .map(|&(ref k, ref v)| (k, v))
            .collect::<Vec<_>>(),
        {
            let mut actual_entries = proof.entries();
            actual_entries.sort_unstable_by(|&(x, _), &(y, _)| {
                ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap()
            });
            actual_entries
        }
    );
}

// Creates data a random-filled `ProofMapIndex<_, [u8; 32], u64>`.
fn index_data<S>(key_bytes: S, elements_len: Range<usize>) -> BoxedStrategy<BTreeMap<[u8; 32], u64>>
where
    S: 'static + Strategy<Value = u8>,
{
    prop::collection::btree_map(
        prop::array::uniform32(key_bytes),
        any::<u64>(),
        elements_len,
    ).boxed()
}

// Converts raw data to a database.
fn data_to_db(data: BTreeMap<[u8; 32], u64>) -> MemoryDB {
    let db = MemoryDB::new();
    let mut fork = db.fork();
    {
        let mut table = ProofMapIndex::new(INDEX_NAME, &mut fork);
        for (key, value) in data {
            table.put(&key, value);
        }
    }
    db.merge(fork.into_patch()).unwrap();
    db
}

macro_rules! proof_map_tests {
    (cases = $cases:expr,sizes = $sizes:expr,bytes = $bytes:expr) => {
        proptest! {
            #![proptest_config(Config::with_cases($cases))]

            #[test]
            fn proof_of_presence(
                (key, ref db) in index_data($bytes, $sizes)
                    .prop_flat_map(|data| (0..data.len(), Just(data)))
                    .prop_map(|(index, data)| {
                        (*data.keys().nth(index).unwrap(), data_to_db(data))
                    })
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                let proof = table.get_proof(key);
                check_map_proof(proof, Some(key), &table);
            }

            #[test]
            #[cfg_attr(feature = "cargo-clippy", allow(useless_format))]
            fn proof_of_absence(
                ref db in index_data($bytes, $sizes).prop_map(data_to_db),
                key in prop::array::uniform32($bytes)
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                prop_assert!(!table.contains(&key));

                let proof = table.get_proof(key);
                check_map_proof(proof, None, &table);
            }

            #[test]
            fn multiproof_of_existing_elements(
                (ref keys, ref db) in index_data($bytes, $sizes)
                    .prop_flat_map(|data| {
                        (prop::collection::vec(0..data.len(), data.len() / 5), Just(data))
                    })
                    .prop_map(|(indexes, data)| {
                        // Note that keys may coincide; this is intentional.
                        let keys: Vec<_> = indexes
                            .into_iter()
                            .map(|i| *data.keys().nth(i).unwrap())
                            .collect();
                        (keys, data_to_db(data))
                    })
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                let proof = table.get_multiproof(keys.clone());

                let unique_keys: BTreeSet<_> = keys.iter().cloned().collect();
                check_map_multiproof(proof, unique_keys, &table);
            }

            #[test]
            #[cfg_attr(feature = "cargo-clippy", allow(useless_format))]
            fn multiproof_of_nonexisting_elements(
                ref db in index_data($bytes, $sizes).prop_map(data_to_db),
                ref keys in prop::collection::vec(prop::array::uniform32($bytes), 20)
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                prop_assert!(keys.iter().all(|key| !table.contains(key)));

                let proof = table.get_multiproof(keys.clone());
                let unique_keys: BTreeSet<_> = keys.iter().cloned().collect();
                check_map_multiproof(proof, unique_keys, &table);
            }

            #[test]
            fn mixed_multiproof(
                (ref keys, ref db) in index_data($bytes, $sizes)
                    .prop_flat_map(|data| {
                        (prop::collection::vec(0..data.len(), data.len() / 5), Just(data))
                    })
                    .prop_map(|(indexes, data)| {
                        // Note that keys may coincide; this is intentional.
                        let keys: Vec<_> = indexes
                            .into_iter()
                            .map(|i| *data.keys().nth(i).unwrap())
                            .collect();
                        (keys, data_to_db(data))
                    }),
                ref absent_keys in prop::collection::vec(prop::array::uniform32($bytes), 20)
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());

                let mut all_keys = keys.clone();
                all_keys.extend_from_slice(absent_keys);
                let proof = table.get_multiproof(all_keys.clone());

                let unique_keys: BTreeSet<_> = all_keys.into_iter().collect();
                check_map_multiproof(proof, unique_keys, &table);
            }
        }
    };
}

mod small_index {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases,
        sizes = 10..100,
        bytes = 0_u8..
    );
}

mod small_index_skewed {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases,
        sizes = 10..100,
        bytes = 0_u8..3
    );
}

mod large_index {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases >> 5,
        sizes = 5_000..10_000,
        bytes = 0_u8..
    );
}

mod large_index_skewed {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases >> 5,
        sizes = 5_000..10_000,
        bytes = 0_u8..3
    );
}
<commit_msg>Use `prop_assume` in property testing (#882)<commit_after>// Copyright 2018 The Exonum Team
//
// 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
//
//   http://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.

//! Property testing for proofs of existence / absence in `ProofMapIndex`.
//!
//! To adjust the number of test cases for each test, set the `PROPTEST_CASES` environment
//! variable as per `proptest` docs. The number of test cases for large tests will be scaled
//! back automatically. A reasonable value for `PROPTEST_CASES` is `256`
//! (default; results in running time ~30 sec for larger tests) or more. The run time
//! scales linearly with the number of cases.

// cspell:ignore proptest

extern crate exonum;
#[macro_use]
extern crate proptest;

use exonum::storage::{
    proof_map_index::{ProofMapKey, ProofPath}, Database, MapProof, MemoryDB, ProofMapIndex,
    Snapshot, StorageValue,
};
use prop::{
    array, collection::{btree_map, vec},
};
use proptest::{prelude::*, test_runner::Config};

use std::{
    collections::{BTreeMap, BTreeSet}, fmt::Debug, ops::Range,
};

const INDEX_NAME: &str = "index";

fn check_map_proof<T, K, V>(proof: MapProof<K, V>, key: Option<K>, table: &ProofMapIndex<T, K, V>)
where
    T: AsRef<Snapshot>,
    K: ProofMapKey + PartialEq + Debug,
    V: StorageValue + PartialEq + Debug,
{
    let entries = key.map(|key| {
        let value = table.get(&key).unwrap();
        (key, value)
    });

    let proof = proof.check().unwrap();
    assert_eq!(
        proof.entries(),
        entries
            .iter()
            .map(|&(ref k, ref v)| (k, v))
            .collect::<Vec<_>>()
    );
    assert_eq!(proof.merkle_root(), table.merkle_root());
}

fn check_map_multiproof<T, K, V>(
    proof: MapProof<K, V>,
    keys: BTreeSet<K>,
    table: &ProofMapIndex<T, K, V>,
) where
    T: AsRef<Snapshot>,
    K: ProofMapKey + Clone + PartialEq + Debug,
    V: StorageValue + PartialEq + Debug,
{
    let (entries, missing_keys) = {
        let mut entries: Vec<(K, V)> = Vec::new();
        let mut missing_keys: Vec<K> = Vec::new();

        for key in keys {
            if table.contains(&key) {
                let value = table.get(&key).unwrap();
                entries.push((key, value));
            } else {
                missing_keys.push(key);
            }
        }

        // Sort entries and missing keys by the order imposed by the `ProofPath`
        // serialization of the keys
        entries.sort_unstable_by(|&(ref x, _), &(ref y, _)| {
            ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap()
        });
        missing_keys
            .sort_unstable_by(|x, y| ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap());

        (entries, missing_keys)
    };

    let proof = proof.check().unwrap();
    assert_eq!(proof.merkle_root(), table.merkle_root());
    assert_eq!(missing_keys.iter().collect::<Vec<&_>>(), {
        let mut actual_keys = proof.missing_keys();
        actual_keys
            .sort_unstable_by(|&x, &y| ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap());
        actual_keys
    });
    assert_eq!(
        entries
            .iter()
            .map(|&(ref k, ref v)| (k, v))
            .collect::<Vec<_>>(),
        {
            let mut actual_entries = proof.entries();
            actual_entries.sort_unstable_by(|&(x, _), &(y, _)| {
                ProofPath::new(x).partial_cmp(&ProofPath::new(y)).unwrap()
            });
            actual_entries
        }
    );
}

// Creates data a random-filled `ProofMapIndex<_, [u8; 32], u64>`.
fn index_data<S>(key_bytes: S, elements_len: Range<usize>) -> BoxedStrategy<BTreeMap<[u8; 32], u64>>
where
    S: 'static + Strategy<Value = u8>,
{
    btree_map(array::uniform32(key_bytes), any::<u64>(), elements_len).boxed()
}

// Converts raw data to a database.
fn data_to_db(data: BTreeMap<[u8; 32], u64>) -> MemoryDB {
    let db = MemoryDB::new();
    let mut fork = db.fork();
    {
        let mut table = ProofMapIndex::new(INDEX_NAME, &mut fork);
        for (key, value) in data {
            table.put(&key, value);
        }
    }
    db.merge(fork.into_patch()).unwrap();
    db
}

macro_rules! proof_map_tests {
    (cases = $cases:expr,sizes = $sizes:expr,bytes = $bytes:expr) => {
        proptest! {
            #![proptest_config(Config::with_cases($cases))]

            #[test]
            fn proof_of_presence(
                (key, ref db) in index_data($bytes, $sizes)
                    .prop_flat_map(|data| (0..data.len(), Just(data)))
                    .prop_map(|(index, data)| {
                        (*data.keys().nth(index).unwrap(), data_to_db(data))
                    })
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                let proof = table.get_proof(key);
                check_map_proof(proof, Some(key), &table);
            }

            #[test]
            fn proof_of_absence(
                ref db in index_data($bytes, $sizes).prop_map(data_to_db),
                key in array::uniform32($bytes)
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                prop_assume!(!table.contains(&key));

                let proof = table.get_proof(key);
                check_map_proof(proof, None, &table);
            }

            #[test]
            fn multiproof_of_existing_elements(
                (ref keys, ref db) in index_data($bytes, $sizes)
                    .prop_flat_map(|data| {
                        (vec(0..data.len(), data.len() / 5), Just(data))
                    })
                    .prop_map(|(indexes, data)| {
                        // Note that keys may coincide; this is intentional.
                        let keys: Vec<_> = indexes
                            .into_iter()
                            .map(|i| *data.keys().nth(i).unwrap())
                            .collect();
                        (keys, data_to_db(data))
                    })
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                let proof = table.get_multiproof(keys.clone());

                let unique_keys: BTreeSet<_> = keys.iter().cloned().collect();
                check_map_multiproof(proof, unique_keys, &table);
            }

            #[test]
            fn multiproof_of_nonexisting_elements(
                ref db in index_data($bytes, $sizes).prop_map(data_to_db),
                ref keys in vec(array::uniform32($bytes), 20)
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());
                prop_assume!(keys.iter().all(|key| !table.contains(key)));

                let proof = table.get_multiproof(keys.clone());
                let unique_keys: BTreeSet<_> = keys.iter().cloned().collect();
                check_map_multiproof(proof, unique_keys, &table);
            }

            #[test]
            fn mixed_multiproof(
                (ref keys, ref db) in index_data($bytes, $sizes)
                    .prop_flat_map(|data| {
                        (vec(0..data.len(), data.len() / 5), Just(data))
                    })
                    .prop_map(|(indexes, data)| {
                        // Note that keys may coincide; this is intentional.
                        let keys: Vec<_> = indexes
                            .into_iter()
                            .map(|i| *data.keys().nth(i).unwrap())
                            .collect();
                        (keys, data_to_db(data))
                    }),
                ref absent_keys in vec(array::uniform32($bytes), 20)
            ) {
                let table: ProofMapIndex<_, [u8; 32], u64> =
                    ProofMapIndex::new(INDEX_NAME, db.snapshot());

                let mut all_keys = keys.clone();
                all_keys.extend_from_slice(absent_keys);
                let proof = table.get_multiproof(all_keys.clone());

                let unique_keys: BTreeSet<_> = all_keys.into_iter().collect();
                check_map_multiproof(proof, unique_keys, &table);
            }
        }
    };
}

mod small_index {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases,
        sizes = 10..100,
        bytes = 0_u8..
    );
}

mod small_index_skewed {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases,
        sizes = 10..100,
        bytes = 0_u8..3
    );
}

mod large_index {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases >> 5,
        sizes = 5_000..10_000,
        bytes = 0_u8..
    );
}

mod large_index_skewed {
    use super::*;

    proof_map_tests!(
        cases = Config::default().cases >> 5,
        sizes = 5_000..10_000,
        bytes = 0_u8..3
    );
}
<|endoftext|>


<commit_before>// Copyright 2018 Developers of the Rand project.
// Copyright 2013-2015 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rand_core::{Error, ErrorKind};
use core::fmt;

/// An error that can occur when [`JitterRng::test_timer`] fails.
///
/// [`JitterRng::test_timer`]: crate::JitterRng::test_timer
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TimerError {
    /// No timer available.
    NoTimer,
    /// Timer too coarse to use as an entropy source.
    CoarseTimer,
    /// Timer is not monotonically increasing.
    NotMonotonic,
    /// Variations of deltas of time too small.
    TinyVariantions,
    /// Too many stuck results (indicating no added entropy).
    TooManyStuck,
    #[doc(hidden)]
    __Nonexhaustive,
}

impl TimerError {
    fn description(&self) -> &'static str {
        match *self {
            TimerError::NoTimer => "no timer available",
            TimerError::CoarseTimer => "coarse timer",
            TimerError::NotMonotonic => "timer not monotonic",
            TimerError::TinyVariantions => "time delta variations too small",
            TimerError::TooManyStuck => "too many stuck results",
            TimerError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl fmt::Display for TimerError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

#[cfg(feature = "std")]
impl ::std::error::Error for TimerError {
    fn description(&self) -> &str {
        self.description()
    }
}

impl From<TimerError> for Error {
    fn from(err: TimerError) -> Error {
        // Timer check is already quite permissive of failures so we don't
        // expect false-positive failures, i.e. any error is irrecoverable.
        Error::with_cause(ErrorKind::Unavailable,
                              "timer jitter failed basic quality tests", err)
    }
}

<commit_msg>rand_jitter error type fix<commit_after>// Copyright 2018 Developers of the Rand project.
// Copyright 2013-2015 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rand_core::{Error, ErrorKind};
use core::fmt;

/// An error that can occur when [`JitterRng::test_timer`] fails.
///
/// [`JitterRng::test_timer`]: crate::JitterRng::test_timer
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TimerError {
    /// No timer available.
    NoTimer,
    /// Timer too coarse to use as an entropy source.
    CoarseTimer,
    /// Timer is not monotonically increasing.
    NotMonotonic,
    /// Variations of deltas of time too small.
    TinyVariantions,
    /// Too many stuck results (indicating no added entropy).
    TooManyStuck,
    #[doc(hidden)]
    __Nonexhaustive,
}

impl TimerError {
    fn description(&self) -> &'static str {
        match *self {
            TimerError::NoTimer => "no timer available",
            TimerError::CoarseTimer => "coarse timer",
            TimerError::NotMonotonic => "timer not monotonic",
            TimerError::TinyVariantions => "time delta variations too small",
            TimerError::TooManyStuck => "too many stuck results",
            TimerError::__Nonexhaustive => unreachable!(),
        }
    }
}

impl fmt::Display for TimerError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

#[cfg(feature = "std")]
impl ::std::error::Error for TimerError {
    fn description(&self) -> &str {
        self.description()
    }
}

impl From<TimerError> for Error {
    fn from(err: TimerError) -> Error {
        // Timer check is already quite permissive of failures so we don't
        // expect false-positive failures, i.e. any error is irrecoverable.
        #[cfg(feature = "std")] {
            Error::with_cause(ErrorKind::Unavailable, "timer jitter failed basic quality tests", err)
        }
        #[cfg(not(feature = "std"))] {
            Error::new(ErrorKind::Unavailable, "timer jitter failed basic quality tests")
        }
    }
}

<|endoftext|>