<commit_before>#[doc="
A process-wide libuv event loop for library use.
"];

export get, get_monitor_task_gl;

import ll = uv_ll;
import hl = uv_hl;
import get_gl = get;
import task::{spawn_sched, single_threaded};
import priv::{chan_from_global_ptr, weaken_task};
import comm::{port, chan, methods, select2, listen};
import either::{left, right};

native mod rustrt {
    fn rust_uv_get_kernel_global_chan_ptr() -> *libc::uintptr_t;
}

#[doc ="
Race-free helper to get access to a global task where a libuv
loop is running.

Use `uv::hl::interact` to do operations against the global
loop that this function returns.

# Return

* A `hl::high_level_loop` that encapsulates communication with the global
loop.
"]
fn get() -> hl::high_level_loop {
    ret get_monitor_task_gl();
}

#[doc(hidden)]
fn get_monitor_task_gl() -> hl::high_level_loop unsafe {

    let monitor_loop_chan_ptr = rustrt::rust_uv_get_kernel_global_chan_ptr();

    #debug("ENTERING global_loop::get() loop chan: %?",
           monitor_loop_chan_ptr);

    let builder_fn = {||
        let builder = task::builder();
        task::set_opts(builder, {
            supervise: false,
            sched: some({
                mode: single_threaded,
                native_stack_size: none
            })
            with task::get_opts(builder)
        });
        builder
    };

    #debug("before priv::chan_from_global_ptr");
    type monchan = chan<hl::high_level_loop>;

    let monitor_ch = chan_from_global_ptr::<monchan>(monitor_loop_chan_ptr,
                                                     builder_fn) {|msg_po|
        #debug("global monitor task starting");

        // As a weak task the runtime will notify us when to exit
        weaken_task() {|weak_exit_po|
            #debug("global monitor task is now weak");
            let hl_loop = spawn_high_level_loop();
            loop {
                #debug("in outer_loop...");
                alt select2(weak_exit_po, msg_po) {
                  left(weak_exit) {
                    // all normal tasks have ended, tell the
                    // libuv loop to tear_down, then exit
                    #debug("weak_exit_po recv'd msg: %?", weak_exit);
                    hl::exit(hl_loop);
                    break;
                  }
                  right(fetch_ch) {
                    #debug("hl_loop req recv'd: %?", fetch_ch);
                    fetch_ch.send(hl_loop);
                  }
                }
            }
            #debug("global monitor task is leaving weakend state");
        };
        #debug("global monitor task exiting");
    };

    // once we have a chan to the monitor loop, we ask it for
    // the libuv loop's async handle
    listen { |fetch_ch|
        monitor_ch.send(fetch_ch);
        fetch_ch.recv()
    }
}

fn spawn_high_level_loop() -> hl::high_level_loop unsafe {
    let hll_po = port::<hl::high_level_loop>();
    let hll_ch = hll_po.chan();

    spawn_sched(single_threaded) {||
        #debug("entering global libuv task");
        weaken_task() {|weak_exit_po|
            #debug("global libuv task is now weak %?", weak_exit_po);
            hl::run_high_level_loop(hll_ch);
            #debug("global libuv task is leaving weakened state");
        };
        #debug("global libuv task exiting");
    };

    hll_po.recv()
}

#[cfg(test)]
mod test {
    crust fn simple_timer_close_cb(timer_ptr: *ll::uv_timer_t) unsafe {
        let exit_ch_ptr = ll::get_data_for_uv_handle(
            timer_ptr as *libc::c_void) as *comm::chan<bool>;
        let exit_ch = *exit_ch_ptr;
        comm::send(exit_ch, true);
        log(debug, #fmt("EXIT_CH_PTR simple_timer_close_cb exit_ch_ptr: %?",
                       exit_ch_ptr));
    }
    crust fn simple_timer_cb(timer_ptr: *ll::uv_timer_t,
                             _status: libc::c_int) unsafe {
        log(debug, "in simple timer cb");
        ll::timer_stop(timer_ptr);
        let hl_loop = get_gl();
        hl::interact(hl_loop) {|_loop_ptr|
            log(debug, "closing timer");
            ll::close(timer_ptr, simple_timer_close_cb);
            log(debug, "about to deref exit_ch_ptr");
            log(debug, "after msg sent on deref'd exit_ch");
        };
        log(debug, "exiting simple timer cb");
    }

    fn impl_uv_hl_simple_timer(hl_loop: hl::high_level_loop) unsafe {
        let exit_po = comm::port::<bool>();
        let exit_ch = comm::chan(exit_po);
        let exit_ch_ptr = ptr::addr_of(exit_ch);
        log(debug, #fmt("EXIT_CH_PTR newly created exit_ch_ptr: %?",
                       exit_ch_ptr));
        let timer_handle = ll::timer_t();
        let timer_ptr = ptr::addr_of(timer_handle);
        hl::interact(hl_loop) {|loop_ptr|
            log(debug, "user code inside interact loop!!!");
            let init_status = ll::timer_init(loop_ptr, timer_ptr);
            if(init_status == 0i32) {
                ll::set_data_for_uv_handle(
                    timer_ptr as *libc::c_void,
                    exit_ch_ptr as *libc::c_void);
                let start_status = ll::timer_start(timer_ptr, simple_timer_cb,
                                                   1u, 0u);
                if(start_status == 0i32) {
                }
                else {
                    fail "failure on ll::timer_start()";
                }
            }
            else {
                fail "failure on ll::timer_init()";
            }
        };
        comm::recv(exit_po);
        log(debug, "global_loop timer test: msg recv on exit_po, done..");
    }

    #[test]
    fn test_gl_uv_global_loop_high_level_global_timer() unsafe {
        let hl_loop = get_gl();
        let exit_po = comm::port::<()>();
        let exit_ch = comm::chan(exit_po);
        task::spawn_sched(task::manual_threads(1u), {||
            impl_uv_hl_simple_timer(hl_loop);
            comm::send(exit_ch, ());
        });
        impl_uv_hl_simple_timer(hl_loop);
        comm::recv(exit_po);
    }

    // keeping this test ignored until some kind of stress-test-harness
    // is set up for the build bots
    #[test]
    #[ignore]
    fn test_stress_gl_uv_global_loop_high_level_global_timer() unsafe {
        let hl_loop = get_gl();
        let exit_po = comm::port::<()>();
        let exit_ch = comm::chan(exit_po);
        let cycles = 5000u;
        iter::repeat(cycles) {||
            task::spawn_sched(task::manual_threads(1u), {||
                impl_uv_hl_simple_timer(hl_loop);
                comm::send(exit_ch, ());
            });
        };
        iter::repeat(cycles) {||
            comm::recv(exit_po);
        };
        log(debug, "test_stress_gl_uv_global_loop_high_level_global_timer"+
            " exiting sucessfully!");
    }
}<commit_msg>std: Separate weakening the I/O task from spawning it<commit_after>#[doc="
A process-wide libuv event loop for library use.
"];

export get, get_monitor_task_gl;

import ll = uv_ll;
import hl = uv_hl;
import get_gl = get;
import task::{run, single_threaded};
import priv::{chan_from_global_ptr, weaken_task};
import comm::{port, chan, methods, select2, listen};
import either::{left, right};

native mod rustrt {
    fn rust_uv_get_kernel_global_chan_ptr() -> *libc::uintptr_t;
}

#[doc ="
Race-free helper to get access to a global task where a libuv
loop is running.

Use `uv::hl::interact` to do operations against the global
loop that this function returns.

# Return

* A `hl::high_level_loop` that encapsulates communication with the global
loop.
"]
fn get() -> hl::high_level_loop {
    ret get_monitor_task_gl();
}

#[doc(hidden)]
fn get_monitor_task_gl() -> hl::high_level_loop unsafe {

    let monitor_loop_chan_ptr = rustrt::rust_uv_get_kernel_global_chan_ptr();

    #debug("ENTERING global_loop::get() loop chan: %?",
           monitor_loop_chan_ptr);

    let builder_fn = {||
        let builder = task::builder();
        task::set_opts(builder, {
            supervise: false,
            sched: some({
                mode: single_threaded,
                native_stack_size: none
            })
            with task::get_opts(builder)
        });
        builder
    };

    #debug("before priv::chan_from_global_ptr");
    type monchan = chan<hl::high_level_loop>;

    let monitor_ch = chan_from_global_ptr::<monchan>(monitor_loop_chan_ptr,
                                                     builder_fn) {|msg_po|
        #debug("global monitor task starting");

        // As a weak task the runtime will notify us when to exit
        weaken_task() {|weak_exit_po|
            #debug("global monitor task is now weak");
            let hl_loop = spawn_loop();
            loop {
                #debug("in outer_loop...");
                alt select2(weak_exit_po, msg_po) {
                  left(weak_exit) {
                    // all normal tasks have ended, tell the
                    // libuv loop to tear_down, then exit
                    #debug("weak_exit_po recv'd msg: %?", weak_exit);
                    hl::exit(hl_loop);
                    break;
                  }
                  right(fetch_ch) {
                    #debug("hl_loop req recv'd: %?", fetch_ch);
                    fetch_ch.send(hl_loop);
                  }
                }
            }
            #debug("global monitor task is leaving weakend state");
        };
        #debug("global monitor task exiting");
    };

    // once we have a chan to the monitor loop, we ask it for
    // the libuv loop's async handle
    listen { |fetch_ch|
        monitor_ch.send(fetch_ch);
        fetch_ch.recv()
    }
}

fn spawn_loop() -> hl::high_level_loop unsafe {
    let builder = task::builder();
    task::add_wrapper(builder) {|task_body|
        fn~(move task_body) {
            // The I/O loop task also needs to be weak so it doesn't keep
            // the runtime alive
            weaken_task {|weak_exit_po|
                #debug("global libuv task is now weak %?", weak_exit_po);
                task_body();

                // We don't wait for the exit message on weak_exit_po
                // because the monitor task will tell the uv loop when to
                // exit

                #debug("global libuv task is leaving weakened state");
            }
        }
    }
    spawn_high_level_loop(builder)
}

fn spawn_high_level_loop(-builder: task::builder
                        ) -> hl::high_level_loop unsafe {

    let hll_po = port::<hl::high_level_loop>();
    let hll_ch = hll_po.chan();

    task::set_opts(builder, {
        sched: some({
            mode: single_threaded,
            native_stack_size: none
        })
        with task::get_opts(builder)
    });

    run(builder) {||
        #debug("entering libuv task");
        hl::run_high_level_loop(hll_ch);
        #debug("libuv task exiting");
    };

    hll_po.recv()
}

#[cfg(test)]
mod test {
    crust fn simple_timer_close_cb(timer_ptr: *ll::uv_timer_t) unsafe {
        let exit_ch_ptr = ll::get_data_for_uv_handle(
            timer_ptr as *libc::c_void) as *comm::chan<bool>;
        let exit_ch = *exit_ch_ptr;
        comm::send(exit_ch, true);
        log(debug, #fmt("EXIT_CH_PTR simple_timer_close_cb exit_ch_ptr: %?",
                       exit_ch_ptr));
    }
    crust fn simple_timer_cb(timer_ptr: *ll::uv_timer_t,
                             _status: libc::c_int) unsafe {
        log(debug, "in simple timer cb");
        ll::timer_stop(timer_ptr);
        let hl_loop = get_gl();
        hl::interact(hl_loop) {|_loop_ptr|
            log(debug, "closing timer");
            ll::close(timer_ptr, simple_timer_close_cb);
            log(debug, "about to deref exit_ch_ptr");
            log(debug, "after msg sent on deref'd exit_ch");
        };
        log(debug, "exiting simple timer cb");
    }

    fn impl_uv_hl_simple_timer(hl_loop: hl::high_level_loop) unsafe {
        let exit_po = comm::port::<bool>();
        let exit_ch = comm::chan(exit_po);
        let exit_ch_ptr = ptr::addr_of(exit_ch);
        log(debug, #fmt("EXIT_CH_PTR newly created exit_ch_ptr: %?",
                       exit_ch_ptr));
        let timer_handle = ll::timer_t();
        let timer_ptr = ptr::addr_of(timer_handle);
        hl::interact(hl_loop) {|loop_ptr|
            log(debug, "user code inside interact loop!!!");
            let init_status = ll::timer_init(loop_ptr, timer_ptr);
            if(init_status == 0i32) {
                ll::set_data_for_uv_handle(
                    timer_ptr as *libc::c_void,
                    exit_ch_ptr as *libc::c_void);
                let start_status = ll::timer_start(timer_ptr, simple_timer_cb,
                                                   1u, 0u);
                if(start_status == 0i32) {
                }
                else {
                    fail "failure on ll::timer_start()";
                }
            }
            else {
                fail "failure on ll::timer_init()";
            }
        };
        comm::recv(exit_po);
        log(debug, "global_loop timer test: msg recv on exit_po, done..");
    }

    #[test]
    fn test_gl_uv_global_loop_high_level_global_timer() unsafe {
        let hl_loop = get_gl();
        let exit_po = comm::port::<()>();
        let exit_ch = comm::chan(exit_po);
        task::spawn_sched(task::manual_threads(1u), {||
            impl_uv_hl_simple_timer(hl_loop);
            comm::send(exit_ch, ());
        });
        impl_uv_hl_simple_timer(hl_loop);
        comm::recv(exit_po);
    }

    // keeping this test ignored until some kind of stress-test-harness
    // is set up for the build bots
    #[test]
    #[ignore]
    fn test_stress_gl_uv_global_loop_high_level_global_timer() unsafe {
        let hl_loop = get_gl();
        let exit_po = comm::port::<()>();
        let exit_ch = comm::chan(exit_po);
        let cycles = 5000u;
        iter::repeat(cycles) {||
            task::spawn_sched(task::manual_threads(1u), {||
                impl_uv_hl_simple_timer(hl_loop);
                comm::send(exit_ch, ());
            });
        };
        iter::repeat(cycles) {||
            comm::recv(exit_po);
        };
        log(debug, "test_stress_gl_uv_global_loop_high_level_global_timer"+
            " exiting sucessfully!");
    }
}<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::OP_COUNTER;
use num_derive::ToPrimitive;
use num_traits::ToPrimitive;
use once_cell::sync::Lazy;
use prometheus::IntGaugeVec;
#[cfg(test)]
use proptest::{collection::hash_map, prelude::*};
#[cfg(test)]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use strum::IntoEnumIterator;
use strum_macros::{AsRefStr, EnumIter};

// register Prometheus counters
pub static LIBRA_STORAGE_LEDGER: Lazy<IntGaugeVec> = Lazy::new(|| {
    register_int_gauge_vec!(
        // metric name
        "libra_storage_ledger",
        // metric description
        "Libra storage ledger counters",
        // metric labels (dimensions)
        &["type"]
    )
    .unwrap()
});

/// Types of ledger counters.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ToPrimitive, EnumIter, AsRefStr)]
#[cfg_attr(test, derive(Arbitrary))]
#[strum(serialize_all = "snake_case")]
pub(crate) enum LedgerCounter {
    EventsCreated = 101,

    NewStateLeaves = 201,
    StaleStateLeaves = 202,

    NewStateNodes = 301,
    StaleStateNodes = 302,
}

#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
struct InnerLedgerCounters {
    counters: BTreeMap<u16, usize>,
}

impl InnerLedgerCounters {
    pub fn new() -> Self {
        Self {
            counters: BTreeMap::new(),
        }
    }

    fn raw_key(counter: LedgerCounter) -> u16 {
        counter
            .to_u16()
            .expect("LedgerCounter should convert to u16.")
    }

    fn get(&self, counter: LedgerCounter) -> usize {
        self.counters
            .get(&Self::raw_key(counter))
            .cloned()
            .unwrap_or(0)
    }

    fn inc(&mut self, counter: LedgerCounter, by: usize) -> &mut Self {
        self.raw_inc(Self::raw_key(counter), by)
    }

    fn raw_inc(&mut self, key: u16, by: usize) -> &mut Self {
        let value = self.counters.entry(key).or_insert(0);
        *value += by;

        self
    }
}

/// Represents `LedgerCounter` bumps yielded by saving a batch of transactions.
pub(crate) struct LedgerCounterBumps {
    bumps: InnerLedgerCounters,
}

impl LedgerCounterBumps {
    /// Construsts an empty set of bumps.
    pub fn new() -> Self {
        Self {
            bumps: InnerLedgerCounters::new(),
        }
    }

    /// Makes the bump of a certain counter bigger.
    ///
    /// If a bump has not already been recorded for the counter, assumes current value of 0.
    pub fn bump(&mut self, counter: LedgerCounter, by: usize) -> &mut Self {
        self.bumps.inc(counter, by);

        self
    }

    /// Get the current value of the bump of `counter`.
    ///
    /// Defaults to 0.
    #[cfg(test)]
    pub fn get(&mut self, counter: LedgerCounter) -> usize {
        self.bumps.get(counter)
    }
}

/// Represents ledger counter values at a certain version.
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub(crate) struct LedgerCounters {
    counters: InnerLedgerCounters,
}

impl LedgerCounters {
    /// Constructs a new empty counter set.
    pub fn new() -> Self {
        Self {
            counters: InnerLedgerCounters::new(),
        }
    }

    /// Bump each counter in `bumps` with the value in `bumps`.
    pub fn bump(&mut self, bumps: LedgerCounterBumps) -> &mut Self {
        for (key, value) in bumps.bumps.counters.into_iter() {
            self.counters.raw_inc(key, value);
        }

        self
    }

    /// Bump Prometheus counters.
    pub fn bump_op_counters(&self) {
        for counter in LedgerCounter::iter() {
            OP_COUNTER.set(counter.as_ref(), self.get(counter));
            LIBRA_STORAGE_LEDGER
                .with_label_values(&[counter.as_ref()])
                .set(self.get(counter) as i64);
        }
    }

    /// Get the value of `counter`.
    pub fn get(&self, counter: LedgerCounter) -> usize {
        self.counters.get(counter)
    }
}

#[cfg(test)]
prop_compose! {
    pub(crate) fn ledger_counters_strategy()(
        counters_map in hash_map(any::<LedgerCounter>(), any::<usize>(), 0..3)
    ) -> LedgerCounters {
        let mut counters = InnerLedgerCounters::new();
        for (counter, value) in counters_map {
            counters.inc(counter, value);
        }

        LedgerCounters { counters }
    }
}

#[cfg(test)]
impl Arbitrary for LedgerCounters {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
        ledger_counters_strategy().boxed()
    }
}

#[cfg(test)]
mod test;
<commit_msg>[storage] remove usage of strum::AsRef<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::OP_COUNTER;
use num_derive::ToPrimitive;
use num_traits::ToPrimitive;
use once_cell::sync::Lazy;
use prometheus::IntGaugeVec;
#[cfg(test)]
use proptest::{collection::hash_map, prelude::*};
#[cfg(test)]
use proptest_derive::Arbitrary;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;

// register Prometheus counters
pub static LIBRA_STORAGE_LEDGER: Lazy<IntGaugeVec> = Lazy::new(|| {
    register_int_gauge_vec!(
        // metric name
        "libra_storage_ledger",
        // metric description
        "Libra storage ledger counters",
        // metric labels (dimensions)
        &["type"]
    )
    .unwrap()
});

/// Types of ledger counters.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ToPrimitive, EnumIter)]
#[cfg_attr(test, derive(Arbitrary))]
pub(crate) enum LedgerCounter {
    EventsCreated = 101,

    NewStateLeaves = 201,
    StaleStateLeaves = 202,

    NewStateNodes = 301,
    StaleStateNodes = 302,
}

impl LedgerCounter {
    const STR_EVENTS_CREATED: &'static str = "events_created";
    const STR_NEW_STATE_LEAVES: &'static str = "new_state_leaves";
    const STR_STALE_STATE_LEAVES: &'static str = "stale_state_leaves";
    const STR_NEW_STATE_NODES: &'static str = "new_state_nodes";
    const STR_STALE_STATE_NODES: &'static str = "stale_state_nodes";

    pub fn name(&self) -> &'static str {
        match self {
            Self::EventsCreated=> Self::STR_EVENTS_CREATED,
            Self::NewStateLeaves => Self::STR_NEW_STATE_LEAVES,
            Self::StaleStateLeaves => Self::STR_STALE_STATE_LEAVES,
            Self::NewStateNodes => Self::STR_NEW_STATE_NODES,
            Self::StaleStateNodes => Self::STR_STALE_STATE_NODES,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
struct InnerLedgerCounters {
    counters: BTreeMap<u16, usize>,
}

impl InnerLedgerCounters {
    pub fn new() -> Self {
        Self {
            counters: BTreeMap::new(),
        }
    }

    fn raw_key(counter: LedgerCounter) -> u16 {
        counter
            .to_u16()
            .expect("LedgerCounter should convert to u16.")
    }

    fn get(&self, counter: LedgerCounter) -> usize {
        self.counters
            .get(&Self::raw_key(counter))
            .cloned()
            .unwrap_or(0)
    }

    fn inc(&mut self, counter: LedgerCounter, by: usize) -> &mut Self {
        self.raw_inc(Self::raw_key(counter), by)
    }

    fn raw_inc(&mut self, key: u16, by: usize) -> &mut Self {
        let value = self.counters.entry(key).or_insert(0);
        *value += by;

        self
    }
}

/// Represents `LedgerCounter` bumps yielded by saving a batch of transactions.
pub(crate) struct LedgerCounterBumps {
    bumps: InnerLedgerCounters,
}

impl LedgerCounterBumps {
    /// Construsts an empty set of bumps.
    pub fn new() -> Self {
        Self {
            bumps: InnerLedgerCounters::new(),
        }
    }

    /// Makes the bump of a certain counter bigger.
    ///
    /// If a bump has not already been recorded for the counter, assumes current value of 0.
    pub fn bump(&mut self, counter: LedgerCounter, by: usize) -> &mut Self {
        self.bumps.inc(counter, by);

        self
    }

    /// Get the current value of the bump of `counter`.
    ///
    /// Defaults to 0.
    #[cfg(test)]
    pub fn get(&mut self, counter: LedgerCounter) -> usize {
        self.bumps.get(counter)
    }
}

/// Represents ledger counter values at a certain version.
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub(crate) struct LedgerCounters {
    counters: InnerLedgerCounters,
}

impl LedgerCounters {
    /// Constructs a new empty counter set.
    pub fn new() -> Self {
        Self {
            counters: InnerLedgerCounters::new(),
        }
    }

    /// Bump each counter in `bumps` with the value in `bumps`.
    pub fn bump(&mut self, bumps: LedgerCounterBumps) -> &mut Self {
        for (key, value) in bumps.bumps.counters.into_iter() {
            self.counters.raw_inc(key, value);
        }

        self
    }

    /// Bump Prometheus counters.
    pub fn bump_op_counters(&self) {
        for counter in LedgerCounter::iter() {
            OP_COUNTER.set(counter.name(), self.get(counter));
            LIBRA_STORAGE_LEDGER
                .with_label_values(&[counter.name()])
                .set(self.get(counter) as i64);
        }
    }

    /// Get the value of `counter`.
    pub fn get(&self, counter: LedgerCounter) -> usize {
        self.counters.get(counter)
    }
}

#[cfg(test)]
prop_compose! {
    pub(crate) fn ledger_counters_strategy()(
        counters_map in hash_map(any::<LedgerCounter>(), any::<usize>(), 0..3)
    ) -> LedgerCounters {
        let mut counters = InnerLedgerCounters::new();
        for (counter, value) in counters_map {
            counters.inc(counter, value);
        }

        LedgerCounters { counters }
    }
}

#[cfg(test)]
impl Arbitrary for LedgerCounters {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
        ledger_counters_strategy().boxed()
    }
}

#[cfg(test)]
mod test;
<|endoftext|>


<commit_before>#![cfg(feature = "quickcheck")]
#![cfg(feature = "quickcheck_macros")]

extern crate num_bigint;
extern crate num_integer;
extern crate num_traits;

extern crate quickcheck;
#[macro_use]
extern crate quickcheck_macros;

use num_bigint::{BigInt, BigUint};
use num_traits::{Num, One, Pow, Zero};
use quickcheck::{QuickCheck, StdThreadGen, TestResult};

#[quickcheck]
fn quickcheck_unsigned_eq_reflexive(a: BigUint) -> bool {
    a == a
}

#[quickcheck]
fn quickcheck_signed_eq_reflexive(a: BigInt) -> bool {
    a == a
}

#[quickcheck]
fn quickcheck_unsigned_eq_symmetric(a: BigUint, b: BigUint) -> bool {
    if a == b {
        b == a
    } else {
        b != a
    }
}

#[quickcheck]
fn quickcheck_signed_eq_symmetric(a: BigInt, b: BigInt) -> bool {
    if a == b {
        b == a
    } else {
        b != a
    }
}

#[test]
fn quickcheck_arith_primitive() {
    let gen = StdThreadGen::new(usize::max_value());
    let mut qc = QuickCheck::with_gen(gen);

    fn test_unsigned_add_primitive(a: usize, b: usize) -> TestResult {
        let actual = BigUint::from(a) + BigUint::from(b);
        match a.checked_add(b) {
            None => TestResult::discard(),
            Some(expected) => TestResult::from_bool(BigUint::from(expected) == actual),
        }
    }

    fn test_signed_add_primitive(a: isize, b: isize) -> TestResult {
        let actual = BigInt::from(a) + BigInt::from(b);
        match a.checked_add(b) {
            None => TestResult::discard(),
            Some(expected) => TestResult::from_bool(BigInt::from(expected) == actual),
        }
    }

    fn test_unsigned_mul_primitive(a: u64, b: u64) -> bool {
        //maximum value of u64 means no overflow
        BigUint::from(a as u128 * b as u128) == BigUint::from(a) * BigUint::from(b)
    }

    fn test_signed_mul_primitive(a: i64, b: i64) -> bool {
        //maximum value of i64 means no overflow
        BigInt::from(a as i128 * b as i128) == BigInt::from(a) * BigInt::from(b)
    }

    fn test_unsigned_sub_primitive(a: u128, b: u128) -> bool {
        if b < a {
            BigUint::from(a - b) == BigUint::from(a) - BigUint::from(b)
        } else {
            BigUint::from(b - a) == BigUint::from(b) - BigUint::from(a)
        }
    }

    fn test_signed_sub_primitive(a: i128, b: i128) -> bool {
        if b < a {
            BigInt::from(a - b) == BigInt::from(a) - BigInt::from(b)
        } else {
            BigInt::from(b - a) == BigInt::from(b) - BigInt::from(a)
        }
    }

    fn test_unsigned_div_primitive(a: u128, b: u128) -> TestResult {
        if b == 0 {
            TestResult::discard()
        } else {
            TestResult::from_bool(BigUint::from(a / b) == BigUint::from(a) / BigUint::from(b))
        }
    }

    fn test_signed_div_primitive(a: i128, b: i128) -> TestResult {
        if b == 0 {
            TestResult::discard()
        } else {
            TestResult::from_bool(BigInt::from(a / b) == BigInt::from(a) / BigInt::from(b))
        }
    }

    qc.quickcheck(test_unsigned_add_primitive as fn(usize, usize) -> TestResult);
    qc.quickcheck(test_signed_add_primitive as fn(isize, isize) -> TestResult);
    qc.quickcheck(test_unsigned_mul_primitive as fn(u64, u64) -> bool);
    qc.quickcheck(test_signed_mul_primitive as fn(i64, i64) -> bool);
    qc.quickcheck(test_unsigned_sub_primitive as fn(u128, u128) -> bool);
    qc.quickcheck(test_signed_sub_primitive as fn(i128, i128) -> bool);
    qc.quickcheck(test_unsigned_div_primitive as fn(u128, u128) -> TestResult);
    qc.quickcheck(test_signed_div_primitive as fn(i128, i128) -> TestResult);
}

#[quickcheck]
fn quickcheck_unsigned_add_commutative(a: BigUint, b: BigUint) -> bool {
    a.clone() + b.clone() == b + a
}

#[quickcheck]
fn quickcheck_signed_add_commutative(a: BigInt, b: BigInt) -> bool {
    a.clone() + b.clone() == b + a
}

#[quickcheck]
fn quickcheck_unsigned_add_zero(a: BigUint) -> bool {
    a.clone() == a + BigUint::zero()
}

#[quickcheck]
fn quickcheck_signed_add_zero(a: BigInt) -> bool {
    a.clone() == a + BigInt::zero()
}

#[quickcheck]
fn quickcheck_unsigned_add_associative(a: BigUint, b: BigUint, c: BigUint) -> bool {
    (a.clone() + b.clone()) + c.clone() == a + (b + c)
}

#[quickcheck]
fn quickcheck_signed_add_associative(a: BigInt, b: BigInt, c: BigInt) -> bool {
    (a.clone() + b.clone()) + c.clone() == a + (b + c)
}

#[quickcheck]
fn quickcheck_unsigned_mul_zero(a: BigUint) -> bool {
    a * BigUint::zero() == BigUint::zero()
}

#[quickcheck]
fn quickcheck_signed_mul_zero(a: BigInt) -> bool {
    a * BigInt::zero() == BigInt::zero()
}

#[quickcheck]
fn quickcheck_unsigned_mul_one(a: BigUint) -> bool {
    a.clone() * BigUint::one() == a
}

#[quickcheck]
fn quickcheck_signed_mul_one(a: BigInt) -> bool {
    a.clone() * BigInt::one() == a
}

#[quickcheck]
fn quickcheck_unsigned_mul_commutative(a: BigUint, b: BigUint) -> bool {
    a.clone() * b.clone() == b * a
}

#[quickcheck]
fn quickcheck_signed_mul_commutative(a: BigInt, b: BigInt) -> bool {
    a.clone() * b.clone() == b * a
}

#[quickcheck]
fn quickcheck_unsigned_mul_associative(a: BigUint, b: BigUint, c: BigUint) -> bool {
    (a.clone() * b.clone()) * c.clone() == a * (b * c)
}

#[quickcheck]
fn quickcheck_signed_mul_associative(a: BigInt, b: BigInt, c: BigInt) -> bool {
    (a.clone() * b.clone()) * c.clone() == a * (b * c)
}

#[quickcheck]
fn quickcheck_unsigned_distributive(a: BigUint, b: BigUint, c: BigUint) -> bool {
    a.clone() * (b.clone() + c.clone()) == a.clone() * b + a * c
}

#[quickcheck]
fn quickcheck_signed_distributive(a: BigInt, b: BigInt, c: BigInt) -> bool {
    a.clone() * (b.clone() + c.clone()) == a.clone() * b + a * c
}

#[quickcheck]
///Tests that exactly one of a<b a>b a=b is true
fn quickcheck_unsigned_ge_le_eq_mut_exclusive(a: BigUint, b: BigUint) -> bool {
    let gt_lt_eq = vec![a > b, a < b, a == b];
    gt_lt_eq
        .iter()
        .fold(0, |acc, e| if *e { acc + 1 } else { acc })
        == 1
}

#[quickcheck]
///Tests that exactly one of a<b a>b a=b is true
fn quickcheck_signed_ge_le_eq_mut_exclusive(a: BigInt, b: BigInt) -> bool {
    let gt_lt_eq = vec![a > b, a < b, a == b];
    gt_lt_eq
        .iter()
        .fold(0, |acc, e| if *e { acc + 1 } else { acc })
        == 1
}

#[quickcheck]
/// Tests correctness of subtraction assuming addition is correct
fn quickcheck_unsigned_sub(a: BigUint, b: BigUint) -> bool {
    if b < a {
        a.clone() - b.clone() + b == a
    } else {
        b.clone() - a.clone() + a == b
    }
}

#[quickcheck]
/// Tests correctness of subtraction assuming addition is correct
fn quickcheck_signed_sub(a: BigInt, b: BigInt) -> bool {
    if b < a {
        a.clone() - b.clone() + b == a
    } else {
        b.clone() - a.clone() + a == b
    }
}

#[quickcheck]
fn quickcheck_unsigned_pow_zero(a: BigUint) -> bool {
    a.pow(0_u32) == BigUint::one()
}

#[quickcheck]
fn quickcheck_unsigned_pow_one(a: BigUint) -> bool {
    a.pow(1_u32) == a
}

#[quickcheck]
fn quickcheck_unsigned_sqrt(a: BigUint) -> bool {
    (a.clone() * a.clone()).sqrt() == a
}

#[quickcheck]
fn quickcheck_unsigned_cbrt(a: BigUint) -> bool {
    (a.clone() * a.clone() * a.clone()).cbrt() == a
}

#[quickcheck]
fn quickcheck_signed_cbrt(a: BigInt) -> bool {
    (a.clone() * a.clone() * a.clone()).cbrt() == a
}

#[quickcheck]
fn quickcheck_unsigned_conversion(a: BigUint, radix: u8) -> TestResult {
    let radix = radix as u32;
    if radix > 36 || radix < 2 {
        return TestResult::discard();
    }
    let string = a.to_str_radix(radix);
    TestResult::from_bool(a == BigUint::from_str_radix(&string, radix).unwrap())
}

#[quickcheck]
fn quickcheck_signed_conversion(a: BigInt, radix: u8) -> TestResult {
    let radix = radix as u32;
    if radix > 36 || radix < 2 {
        return TestResult::discard();
    }
    let string = a.to_str_radix(radix);
    TestResult::from_bool(a == BigInt::from_str_radix(&string, radix).unwrap())
}

#[test]
fn quicktest_shift() {
    let gen = StdThreadGen::new(usize::max_value());
    let mut qc = QuickCheck::with_gen(gen);
    fn test_shr_unsigned(a: u64, shift: u8) -> TestResult {
        let shift = (shift % 64) as usize; //shift at most 64 bits
        let big_a = BigUint::from(a);
        TestResult::from_bool(BigUint::from(a >> shift) == big_a >> shift)
    }
    fn test_shr_signed(a: i64, shift: u8) -> TestResult {
        let shift = (shift % 64) as usize; //shift at most 64 bits
        let big_a = BigInt::from(a);
        TestResult::from_bool(BigInt::from(a >> shift) == big_a >> shift)
    }
    fn test_shl_unsigned(a: u32, shift: u8) -> TestResult {
        let shift = (shift % 32) as usize; //shift at most 32 bits
        let a = a as u64; //leave room for the shifted bits
        let big_a = BigUint::from(a);
        TestResult::from_bool(BigUint::from(a >> shift) == big_a >> shift)
    }
    fn test_shl_signed(a: i32, shift: u8) -> TestResult {
        let shift = (shift % 32) as usize;
        let a = a as u64; //leave room for the shifted bits
        let big_a = BigInt::from(a);
        TestResult::from_bool(BigInt::from(a >> shift) == big_a >> shift)
    }

    qc.quickcheck(test_shr_unsigned as fn(u64, u8) -> TestResult);
    qc.quickcheck(test_shr_signed as fn(i64, u8) -> TestResult);
    qc.quickcheck(test_shl_unsigned as fn(u32, u8) -> TestResult);
    qc.quickcheck(test_shl_signed as fn(i32, u8) -> TestResult);
}
<commit_msg>switch quickchecks to using operations by reference<commit_after>#![cfg(feature = "quickcheck")]
#![cfg(feature = "quickcheck_macros")]

extern crate num_bigint;
extern crate num_integer;
extern crate num_traits;

extern crate quickcheck;
#[macro_use]
extern crate quickcheck_macros;

use num_bigint::{BigInt, BigUint};
use num_traits::{Num, One, Pow, Zero};
use quickcheck::{QuickCheck, StdThreadGen, TestResult};

#[quickcheck]
fn quickcheck_unsigned_eq_reflexive(a: BigUint) -> bool {
    a == a
}

#[quickcheck]
fn quickcheck_signed_eq_reflexive(a: BigInt) -> bool {
    a == a
}

#[quickcheck]
fn quickcheck_unsigned_eq_symmetric(a: BigUint, b: BigUint) -> bool {
    if a == b {
        b == a
    } else {
        b != a
    }
}

#[quickcheck]
fn quickcheck_signed_eq_symmetric(a: BigInt, b: BigInt) -> bool {
    if a == b {
        b == a
    } else {
        b != a
    }
}

#[test]
fn quickcheck_arith_primitive() {
    let gen = StdThreadGen::new(usize::max_value());
    let mut qc = QuickCheck::with_gen(gen);

    fn test_unsigned_add_primitive(a: usize, b: usize) -> TestResult {
        let actual = BigUint::from(a) + BigUint::from(b);
        match a.checked_add(b) {
            None => TestResult::discard(),
            Some(expected) => TestResult::from_bool(BigUint::from(expected) == actual),
        }
    }

    fn test_signed_add_primitive(a: isize, b: isize) -> TestResult {
        let actual = BigInt::from(a) + BigInt::from(b);
        match a.checked_add(b) {
            None => TestResult::discard(),
            Some(expected) => TestResult::from_bool(BigInt::from(expected) == actual),
        }
    }

    fn test_unsigned_mul_primitive(a: u64, b: u64) -> bool {
        //maximum value of u64 means no overflow
        BigUint::from(a as u128 * b as u128) == BigUint::from(a) * BigUint::from(b)
    }

    fn test_signed_mul_primitive(a: i64, b: i64) -> bool {
        //maximum value of i64 means no overflow
        BigInt::from(a as i128 * b as i128) == BigInt::from(a) * BigInt::from(b)
    }

    fn test_unsigned_sub_primitive(a: u128, b: u128) -> bool {
        if b < a {
            BigUint::from(a - b) == BigUint::from(a) - BigUint::from(b)
        } else {
            BigUint::from(b - a) == BigUint::from(b) - BigUint::from(a)
        }
    }

    fn test_signed_sub_primitive(a: i128, b: i128) -> bool {
        if b < a {
            BigInt::from(a - b) == BigInt::from(a) - BigInt::from(b)
        } else {
            BigInt::from(b - a) == BigInt::from(b) - BigInt::from(a)
        }
    }

    fn test_unsigned_div_primitive(a: u128, b: u128) -> TestResult {
        if b == 0 {
            TestResult::discard()
        } else {
            TestResult::from_bool(BigUint::from(a / b) == BigUint::from(a) / BigUint::from(b))
        }
    }

    fn test_signed_div_primitive(a: i128, b: i128) -> TestResult {
        if b == 0 {
            TestResult::discard()
        } else {
            TestResult::from_bool(BigInt::from(a / b) == BigInt::from(a) / BigInt::from(b))
        }
    }

    qc.quickcheck(test_unsigned_add_primitive as fn(usize, usize) -> TestResult);
    qc.quickcheck(test_signed_add_primitive as fn(isize, isize) -> TestResult);
    qc.quickcheck(test_unsigned_mul_primitive as fn(u64, u64) -> bool);
    qc.quickcheck(test_signed_mul_primitive as fn(i64, i64) -> bool);
    qc.quickcheck(test_unsigned_sub_primitive as fn(u128, u128) -> bool);
    qc.quickcheck(test_signed_sub_primitive as fn(i128, i128) -> bool);
    qc.quickcheck(test_unsigned_div_primitive as fn(u128, u128) -> TestResult);
    qc.quickcheck(test_signed_div_primitive as fn(i128, i128) -> TestResult);
}

#[quickcheck]
fn quickcheck_unsigned_add_commutative(a: BigUint, b: BigUint) -> bool {
    &a + &b == b + a
}

#[quickcheck]
fn quickcheck_signed_add_commutative(a: BigInt, b: BigInt) -> bool {
    &a + &b == b + a
}

#[quickcheck]
fn quickcheck_unsigned_add_zero(a: BigUint) -> bool {
    a == &a + BigUint::zero()
}

#[quickcheck]
fn quickcheck_signed_add_zero(a: BigInt) -> bool {
    a == &a + BigInt::zero()
}

#[quickcheck]
fn quickcheck_unsigned_add_associative(a: BigUint, b: BigUint, c: BigUint) -> bool {
    (&a + &b) + &c == a + (b + c)
}

#[quickcheck]
fn quickcheck_signed_add_associative(a: BigInt, b: BigInt, c: BigInt) -> bool {
    (&a + &b) + &c == a + (b + c)
}

#[quickcheck]
fn quickcheck_unsigned_mul_zero(a: BigUint) -> bool {
    a * BigUint::zero() == BigUint::zero()
}

#[quickcheck]
fn quickcheck_signed_mul_zero(a: BigInt) -> bool {
    a * BigInt::zero() == BigInt::zero()
}

#[quickcheck]
fn quickcheck_unsigned_mul_one(a: BigUint) -> bool {
    &a * BigUint::one() == a
}

#[quickcheck]
fn quickcheck_signed_mul_one(a: BigInt) -> bool {
    &a * BigInt::one() == a
}

#[quickcheck]
fn quickcheck_unsigned_mul_commutative(a: BigUint, b: BigUint) -> bool {
    &a * &b == b * a
}

#[quickcheck]
fn quickcheck_signed_mul_commutative(a: BigInt, b: BigInt) -> bool {
    &a * &b == b * a
}

#[quickcheck]
fn quickcheck_unsigned_mul_associative(a: BigUint, b: BigUint, c: BigUint) -> bool {
    (&a * &b) * &c == a * (b * c)
}

#[quickcheck]
fn quickcheck_signed_mul_associative(a: BigInt, b: BigInt, c: BigInt) -> bool {
    (&a * &b) * &c == a * (b * c)
}

#[quickcheck]
fn quickcheck_unsigned_distributive(a: BigUint, b: BigUint, c: BigUint) -> bool {
    &a * (&b + &c) == &a * b + a * c
}

#[quickcheck]
fn quickcheck_signed_distributive(a: BigInt, b: BigInt, c: BigInt) -> bool {
    &a * (&b + &c) == &a * b + a * c
}

#[quickcheck]
///Tests that exactly one of a<b a>b a=b is true
fn quickcheck_unsigned_ge_le_eq_mut_exclusive(a: BigUint, b: BigUint) -> bool {
    let gt_lt_eq = vec![a > b, a < b, a == b];
    gt_lt_eq
        .iter()
        .fold(0, |acc, e| if *e { acc + 1 } else { acc })
        == 1
}

#[quickcheck]
///Tests that exactly one of a<b a>b a=b is true
fn quickcheck_signed_ge_le_eq_mut_exclusive(a: BigInt, b: BigInt) -> bool {
    let gt_lt_eq = vec![a > b, a < b, a == b];
    gt_lt_eq
        .iter()
        .fold(0, |acc, e| if *e { acc + 1 } else { acc })
        == 1
}

#[quickcheck]
/// Tests correctness of subtraction assuming addition is correct
fn quickcheck_unsigned_sub(a: BigUint, b: BigUint) -> bool {
    if b < a {
        &a - &b + b == a
    } else {
        &b - &a + a == b
    }
}

#[quickcheck]
/// Tests correctness of subtraction assuming addition is correct
fn quickcheck_signed_sub(a: BigInt, b: BigInt) -> bool {
    if b < a {
        &a - &b + b == a
    } else {
        &b - &a + a == b
    }
}

#[quickcheck]
fn quickcheck_unsigned_pow_zero(a: BigUint) -> bool {
    a.pow(0_u32) == BigUint::one()
}

#[quickcheck]
fn quickcheck_unsigned_pow_one(a: BigUint) -> bool {
    a.pow(1_u32) == a
}

#[quickcheck]
fn quickcheck_unsigned_sqrt(a: BigUint) -> bool {
    (&a * &a).sqrt() == a
}

#[quickcheck]
fn quickcheck_unsigned_cbrt(a: BigUint) -> bool {
    (&a * &a * &a).cbrt() == a
}

#[quickcheck]
fn quickcheck_signed_cbrt(a: BigInt) -> bool {
    (&a * &a * &a).cbrt() == a
}

#[quickcheck]
fn quickcheck_unsigned_conversion(a: BigUint, radix: u8) -> TestResult {
    let radix = radix as u32;
    if radix > 36 || radix < 2 {
        return TestResult::discard();
    }
    let string = a.to_str_radix(radix);
    TestResult::from_bool(a == BigUint::from_str_radix(&string, radix).unwrap())
}

#[quickcheck]
fn quickcheck_signed_conversion(a: BigInt, radix: u8) -> TestResult {
    let radix = radix as u32;
    if radix > 36 || radix < 2 {
        return TestResult::discard();
    }
    let string = a.to_str_radix(radix);
    TestResult::from_bool(a == BigInt::from_str_radix(&string, radix).unwrap())
}

#[test]
fn quicktest_shift() {
    let gen = StdThreadGen::new(usize::max_value());
    let mut qc = QuickCheck::with_gen(gen);

    fn test_shr_unsigned(a: u64, shift: u8) -> TestResult {
        let shift = (shift % 64) as usize; //shift at most 64 bits
        let big_a = BigUint::from(a);
        TestResult::from_bool(BigUint::from(a >> shift) == big_a >> shift)
    }

    fn test_shr_signed(a: i64, shift: u8) -> TestResult {
        let shift = (shift % 64) as usize; //shift at most 64 bits
        let big_a = BigInt::from(a);
        TestResult::from_bool(BigInt::from(a >> shift) == big_a >> shift)
    }

    fn test_shl_unsigned(a: u32, shift: u8) -> TestResult {
        let shift = (shift % 32) as usize; //shift at most 32 bits
        let a = a as u64; //leave room for the shifted bits
        let big_a = BigUint::from(a);
        TestResult::from_bool(BigUint::from(a >> shift) == big_a >> shift)
    }

    fn test_shl_signed(a: i32, shift: u8) -> TestResult {
        let shift = (shift % 32) as usize;
        let a = a as u64; //leave room for the shifted bits
        let big_a = BigInt::from(a);
        TestResult::from_bool(BigInt::from(a >> shift) == big_a >> shift)
    }

    qc.quickcheck(test_shr_unsigned as fn(u64, u8) -> TestResult);
    qc.quickcheck(test_shr_signed as fn(i64, u8) -> TestResult);
    qc.quickcheck(test_shl_unsigned as fn(u32, u8) -> TestResult);
    qc.quickcheck(test_shl_signed as fn(i32, u8) -> TestResult);
}
<|endoftext|>


<commit_before>use std::io::Write;
use std::error::Error;
use std::time::SystemTime;

use backend::Debug;
use data_types::*;
use types::*;

macro_rules! debug_to_sql {
    ($sql_type:ty, $ty:ty) => {
        impl ToSql<$sql_type, Debug> for $ty {
            fn to_sql<W: Write>(&self, _: &mut W) -> Result<IsNull, Box<Error+Send+Sync>> {
                Ok(IsNull::No)
            }
        }
    };
}

debug_to_sql!(Bool, bool);
debug_to_sql!(Timestamp, SystemTime);

#[cfg(feature = "postgres")]
mod pg_impls {
    use super::*;

    debug_to_sql!(Timestamp, PgTimestamp);
    debug_to_sql!(Timestamptz, PgTimestamp);
    debug_to_sql!(Date, PgDate);
    debug_to_sql!(Time, PgTime);
    debug_to_sql!(Interval, PgInterval);
}

#[cfg(feature = "chrono")]
mod chrono_impls {
    extern crate chrono;
    use super::*;
    use chrono::*;

    debug_to_sql!(Timestamp, NaiveDateTime);
    debug_to_sql!(Time, NaiveTime);
    debug_to_sql!(Date, NaiveDate);

    #[cfg(feature = "postgres")]
    impl<TZ: TimeZone> ToSql<Timestamptz, Debug> for DateTime<TZ> {
        fn to_sql<W: Write>(&self, _: &mut W) -> Result<IsNull, Box<Error+Send+Sync>> {
            Ok(IsNull::No)
        }
    }
}
<commit_msg>Fix failing build<commit_after>use std::io::Write;
use std::error::Error;
use std::time::SystemTime;

use backend::Debug;
use types::*;

macro_rules! debug_to_sql {
    ($sql_type:ty, $ty:ty) => {
        impl ToSql<$sql_type, Debug> for $ty {
            fn to_sql<W: Write>(&self, _: &mut W) -> Result<IsNull, Box<Error+Send+Sync>> {
                Ok(IsNull::No)
            }
        }
    };
}

debug_to_sql!(Bool, bool);
debug_to_sql!(Timestamp, SystemTime);

#[cfg(feature = "postgres")]
mod pg_impls {
    use super::*;
    use data_types::*;

    debug_to_sql!(Timestamp, PgTimestamp);
    debug_to_sql!(Timestamptz, PgTimestamp);
    debug_to_sql!(Date, PgDate);
    debug_to_sql!(Time, PgTime);
    debug_to_sql!(Interval, PgInterval);
}

#[cfg(feature = "chrono")]
mod chrono_impls {
    extern crate chrono;
    use super::*;
    use self::chrono::{NaiveDateTime, NaiveTime, NaiveDate};
    #[cfg(feature = "postgres")]
    use self::chrono::{DateTime, TimeZone};

    debug_to_sql!(Timestamp, NaiveDateTime);
    debug_to_sql!(Time, NaiveTime);
    debug_to_sql!(Date, NaiveDate);

    #[cfg(feature = "postgres")]
    impl<TZ: TimeZone> ToSql<Timestamptz, Debug> for DateTime<TZ> {
        fn to_sql<W: Write>(&self, _: &mut W) -> Result<IsNull, Box<Error+Send+Sync>> {
            Ok(IsNull::No)
        }
    }
}
<|endoftext|>


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

/// Test aspects of setting up from metadata.

extern crate devicemapper;
extern crate libstratis;
extern crate nix;
extern crate tempdir;
extern crate uuid;

use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::{Path, PathBuf};

use self::nix::mount::{MsFlags, MNT_DETACH, mount, umount2};
use self::tempdir::TempDir;
use self::uuid::Uuid;

use self::devicemapper::{Device, DM};

use libstratis::engine::{Engine, Pool};
use libstratis::engine::engine::HasUuid;
use libstratis::engine::types::{Redundancy, RenameAction};
use libstratis::engine::strat_engine::blockdevmgr::initialize;
use libstratis::engine::strat_engine::device::resolve_devices;
use libstratis::engine::strat_engine::metadata::MIN_MDA_SECTORS;
use libstratis::engine::strat_engine::pool::StratPool;
use libstratis::engine::strat_engine::serde_structs::Recordable;
use libstratis::engine::strat_engine::setup::{find_all, get_blockdevs, get_metadata};
use libstratis::engine::strat_engine::StratEngine;

/// Verify that find_all function locates and assigns pools appropriately.
/// 1. Split available paths into 2 discrete sets.
/// 2. Initialize the block devices in the first set with a pool uuid.
/// 3. Run find_all() and verify that it has found the initialized devices
/// and no others.
/// 4. Initialize the block devices in the second set with a different pool
/// uuid.
/// 5. Run find_all() again and verify that both sets of devices are found.
/// 6. Verify that get_metadata() return an error. initialize() only
/// initializes block devices, it does not write metadata.
pub fn test_initialize(paths: &[&Path]) -> () {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let unique_devices = resolve_devices(paths1).unwrap();
    let uuid1 = Uuid::new_v4();
    initialize(&uuid1, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 1);
    assert!(pools.contains_key(&uuid1));
    let devices = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices.len() == paths1.len());

    let unique_devices = resolve_devices(paths2).unwrap();
    let uuid2 = Uuid::new_v4();
    initialize(&uuid2, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);

    assert!(pools.contains_key(&uuid1));
    let devices1 = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices1.len() == paths1.len());

    assert!(pools.contains_key(&uuid2));
    let devices2 = pools.get(&uuid2).expect("pools.contains_key() was true");
    assert!(devices2.len() == paths2.len());

    assert!(pools
                .iter()
                .map(|(uuid, devs)| get_metadata(*uuid, devs))
                .all(|x| x.unwrap().is_none()));
}

/// Verify that metadata can be read from pools.
/// 1. Split paths into two separate sets.
/// 2. Create pools from the two sets.
/// 3. Use find_all() to get the devices in the pool.
/// 4. Use get_metadata to find metadata for each pool and verify correctness.
/// 5. Teardown the engine and repeat.
pub fn test_basic_metadata(paths: &[&Path]) {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths1, None, false).unwrap();
    let metadata1 = engine.get_strat_pool(&uuid1).unwrap().record();

    let name2 = "name2";
    let (uuid2, _) = engine.create_pool(&name2, paths2, None, false).unwrap();
    let metadata2 = engine.get_strat_pool(&uuid2).unwrap().record();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    let devnodes1 = pools.get(&uuid1).unwrap();
    let devnodes2 = pools.get(&uuid2).unwrap();
    let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
    let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
    assert!(pool_save1 == metadata1);
    assert!(pool_save2 == metadata2);
    let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
    let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
    assert!(blockdevs1.len() == pool_save1.block_devs.len());
    assert!(blockdevs2.len() == pool_save2.block_devs.len());

    engine.teardown().unwrap();
    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    let devnodes1 = pools.get(&uuid1).unwrap();
    let devnodes2 = pools.get(&uuid2).unwrap();
    let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
    let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
    assert!(pool_save1 == metadata1);
    assert!(pool_save2 == metadata2);
    let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
    let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
    assert!(blockdevs1.len() == pool_save1.block_devs.len());
    assert!(blockdevs2.len() == pool_save2.block_devs.len());
}

/// Test engine setup.
/// 1. Create two pools.
/// 2. Verify that both exist.
/// 3. Teardown the engine.
/// 4. Verify that pools are gone.
/// 5. Initialize the engine.
/// 6. Verify that pools can be found again.
pub fn test_setup(paths: &[&Path]) {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths1, None, false).unwrap();

    let name2 = "name2";
    let (uuid2, _) = engine.create_pool(&name2, paths2, None, false).unwrap();

    assert!(engine.get_pool(&uuid1).is_some());
    assert!(engine.get_pool(&uuid2).is_some());

    engine.teardown().unwrap();

    let engine = StratEngine::initialize().unwrap();

    assert!(engine.get_pool(&uuid1).is_some());
    assert!(engine.get_pool(&uuid2).is_some());

    engine.teardown().unwrap();
}

/// Verify that a pool rename causes the pool metadata to get the new name.
pub fn test_pool_rename(paths: &[&Path]) {
    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths, None, false).unwrap();

    let name2 = "name2";
    let action = engine.rename_pool(&uuid1, name2).unwrap();

    assert_eq!(action, RenameAction::Renamed);
    engine.teardown().unwrap();

    let engine = StratEngine::initialize().unwrap();
    let pool_name: String = engine.get_pool(&uuid1).unwrap().name().into();
    assert_eq!(pool_name, name2);
    engine.teardown().unwrap();
}

/// Verify that setting up a pool when the pool has not been previously torn
/// down does not fail. Clutter the original pool with a filesystem with some
/// data on it.
pub fn test_pool_setup(paths: &[&Path]) {
    let dm = DM::new().unwrap();

    let (mut pool, _) = StratPool::initialize("name", &dm, paths, Redundancy::NONE, false).unwrap();

    let (_, fs_uuid) = pool.create_filesystems(&[("fsname", None)]).unwrap()[0];

    let tmp_dir = TempDir::new("stratis_testing").unwrap();
    let new_file = tmp_dir.path().join("stratis_test.txt");
    {
        let fs = pool.get_filesystem(&fs_uuid).unwrap();
        mount(Some(&fs.devnode()),
              tmp_dir.path(),
              Some("xfs"),
              MsFlags::empty(),
              None as Option<&str>)
                .unwrap();
        writeln!(&OpenOptions::new()
                      .create(true)
                      .write(true)
                      .open(new_file)
                      .unwrap(),
                 "data")
                .unwrap();
    }

    let paths2: HashMap<Device, PathBuf> = resolve_devices(paths)
        .unwrap()
        .into_iter()
        .map(|(d, p)| (d, p.to_owned()))
        .collect();
    let new_pool = StratPool::setup(*pool.uuid(), &paths2).unwrap();

    assert!(new_pool.get_filesystem(&fs_uuid).is_some());

    umount2(tmp_dir.path(), MNT_DETACH).unwrap();
    pool.teardown().unwrap();
}
<commit_msg>Allow setup tests to use as few as two blockdevs<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

/// Test aspects of setting up from metadata.

extern crate devicemapper;
extern crate libstratis;
extern crate nix;
extern crate tempdir;
extern crate uuid;

use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::{Path, PathBuf};

use self::nix::mount::{MsFlags, MNT_DETACH, mount, umount2};
use self::tempdir::TempDir;
use self::uuid::Uuid;

use self::devicemapper::{Device, DM};

use libstratis::engine::{Engine, Pool};
use libstratis::engine::engine::HasUuid;
use libstratis::engine::types::{Redundancy, RenameAction};
use libstratis::engine::strat_engine::blockdevmgr::initialize;
use libstratis::engine::strat_engine::device::resolve_devices;
use libstratis::engine::strat_engine::metadata::MIN_MDA_SECTORS;
use libstratis::engine::strat_engine::pool::StratPool;
use libstratis::engine::strat_engine::serde_structs::Recordable;
use libstratis::engine::strat_engine::setup::{find_all, get_blockdevs, get_metadata};
use libstratis::engine::strat_engine::StratEngine;

/// Verify that find_all function locates and assigns pools appropriately.
/// 1. Split available paths into 2 discrete sets.
/// 2. Initialize the block devices in the first set with a pool uuid.
/// 3. Run find_all() and verify that it has found the initialized devices
/// and no others.
/// 4. Initialize the block devices in the second set with a different pool
/// uuid.
/// 5. Run find_all() again and verify that both sets of devices are found.
/// 6. Verify that get_metadata() return an error. initialize() only
/// initializes block devices, it does not write metadata.
pub fn test_initialize(paths: &[&Path]) -> () {
    assert!(paths.len() > 1);

    let (paths1, paths2) = paths.split_at(paths.len() / 2);

    let unique_devices = resolve_devices(paths1).unwrap();
    let uuid1 = Uuid::new_v4();
    initialize(&uuid1, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 1);
    assert!(pools.contains_key(&uuid1));
    let devices = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices.len() == paths1.len());

    let unique_devices = resolve_devices(paths2).unwrap();
    let uuid2 = Uuid::new_v4();
    initialize(&uuid2, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);

    assert!(pools.contains_key(&uuid1));
    let devices1 = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices1.len() == paths1.len());

    assert!(pools.contains_key(&uuid2));
    let devices2 = pools.get(&uuid2).expect("pools.contains_key() was true");
    assert!(devices2.len() == paths2.len());

    assert!(pools
                .iter()
                .map(|(uuid, devs)| get_metadata(*uuid, devs))
                .all(|x| x.unwrap().is_none()));
}

/// Verify that metadata can be read from pools.
/// 1. Split paths into two separate sets.
/// 2. Create pools from the two sets.
/// 3. Use find_all() to get the devices in the pool.
/// 4. Use get_metadata to find metadata for each pool and verify correctness.
/// 5. Teardown the engine and repeat.
pub fn test_basic_metadata(paths: &[&Path]) {
    assert!(paths.len() > 1);

    let (paths1, paths2) = paths.split_at(paths.len() / 2);

    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths1, None, false).unwrap();
    let metadata1 = engine.get_strat_pool(&uuid1).unwrap().record();

    let name2 = "name2";
    let (uuid2, _) = engine.create_pool(&name2, paths2, None, false).unwrap();
    let metadata2 = engine.get_strat_pool(&uuid2).unwrap().record();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    let devnodes1 = pools.get(&uuid1).unwrap();
    let devnodes2 = pools.get(&uuid2).unwrap();
    let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
    let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
    assert!(pool_save1 == metadata1);
    assert!(pool_save2 == metadata2);
    let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
    let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
    assert!(blockdevs1.len() == pool_save1.block_devs.len());
    assert!(blockdevs2.len() == pool_save2.block_devs.len());

    engine.teardown().unwrap();
    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    let devnodes1 = pools.get(&uuid1).unwrap();
    let devnodes2 = pools.get(&uuid2).unwrap();
    let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
    let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
    assert!(pool_save1 == metadata1);
    assert!(pool_save2 == metadata2);
    let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
    let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
    assert!(blockdevs1.len() == pool_save1.block_devs.len());
    assert!(blockdevs2.len() == pool_save2.block_devs.len());
}

/// Test engine setup.
/// 1. Create two pools.
/// 2. Verify that both exist.
/// 3. Teardown the engine.
/// 4. Verify that pools are gone.
/// 5. Initialize the engine.
/// 6. Verify that pools can be found again.
pub fn test_setup(paths: &[&Path]) {
    assert!(paths.len() > 1);

    let (paths1, paths2) = paths.split_at(paths.len() / 2);

    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths1, None, false).unwrap();

    let name2 = "name2";
    let (uuid2, _) = engine.create_pool(&name2, paths2, None, false).unwrap();

    assert!(engine.get_pool(&uuid1).is_some());
    assert!(engine.get_pool(&uuid2).is_some());

    engine.teardown().unwrap();

    let engine = StratEngine::initialize().unwrap();

    assert!(engine.get_pool(&uuid1).is_some());
    assert!(engine.get_pool(&uuid2).is_some());

    engine.teardown().unwrap();
}

/// Verify that a pool rename causes the pool metadata to get the new name.
pub fn test_pool_rename(paths: &[&Path]) {
    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths, None, false).unwrap();

    let name2 = "name2";
    let action = engine.rename_pool(&uuid1, name2).unwrap();

    assert_eq!(action, RenameAction::Renamed);
    engine.teardown().unwrap();

    let engine = StratEngine::initialize().unwrap();
    let pool_name: String = engine.get_pool(&uuid1).unwrap().name().into();
    assert_eq!(pool_name, name2);
    engine.teardown().unwrap();
}

/// Verify that setting up a pool when the pool has not been previously torn
/// down does not fail. Clutter the original pool with a filesystem with some
/// data on it.
pub fn test_pool_setup(paths: &[&Path]) {
    let dm = DM::new().unwrap();

    let (mut pool, _) = StratPool::initialize("name", &dm, paths, Redundancy::NONE, false).unwrap();

    let (_, fs_uuid) = pool.create_filesystems(&[("fsname", None)]).unwrap()[0];

    let tmp_dir = TempDir::new("stratis_testing").unwrap();
    let new_file = tmp_dir.path().join("stratis_test.txt");
    {
        let fs = pool.get_filesystem(&fs_uuid).unwrap();
        mount(Some(&fs.devnode()),
              tmp_dir.path(),
              Some("xfs"),
              MsFlags::empty(),
              None as Option<&str>)
                .unwrap();
        writeln!(&OpenOptions::new()
                      .create(true)
                      .write(true)
                      .open(new_file)
                      .unwrap(),
                 "data")
                .unwrap();
    }

    let paths2: HashMap<Device, PathBuf> = resolve_devices(paths)
        .unwrap()
        .into_iter()
        .map(|(d, p)| (d, p.to_owned()))
        .collect();
    let new_pool = StratPool::setup(*pool.uuid(), &paths2).unwrap();

    assert!(new_pool.get_filesystem(&fs_uuid).is_some());

    umount2(tmp_dir.path(), MNT_DETACH).unwrap();
    pool.teardown().unwrap();
}
<|endoftext|>


<commit_before>use super::{AddressNotFoundError, InvalidDatabaseError, Reader, Decoder, IoError};
use std::io::net::ip::IpAddr;
use std::from_str::FromStr;
use serialize::Decodable;

#[test]
fn test_decoder() {

    #[deriving(Decodable, Show, Eq)]
    struct MapXType {
        arrayX: ~[uint],
        utf8_stringX: ~str
    };

    #[deriving(Decodable, Show, Eq)]
    struct MapType {
        mapX: MapXType
    };

    #[deriving(Decodable, Show)]
    struct TestType {
        array:       ~[uint],
        boolean:     bool,
        bytes:       ~[u8],
        double:      f64,
        float:       f32,
        int32:       i32,
        map:         MapType,
        uint16:      u16,
        uint32:      u32,
        uint64:      u64,
        uint128:     ~[u8],
        utf8_string: ~str
    }

    let r = Reader::open("test-data/test-data/MaxMind-DB-test-decoder.mmdb").unwrap();
    let ip: IpAddr = FromStr::from_str("::1.1.1.0").unwrap();
    let raw_data = r.lookup(ip);

    let mut decoder = Decoder::new(raw_data.unwrap());
    let result: TestType = match Decodable::decode(&mut decoder) {
        Ok(v) => v,
        Err(e) => fail!("Decoding error: {}", e)
    };

    assert_eq!(result.array, ~[ 1u, 2u, 3u ]);
    assert_eq!(result.boolean, true);
    assert_eq!(result.bytes, ~[0u8, 0u8, 0u8, 42u8])
    assert_eq!(result.double, 42.123456);
    assert_eq!(result.float, 1.1);
    assert_eq!(result.int32, -268435456);

    assert_eq!(result.map, MapType{ mapX: MapXType{ arrayX: ~[7,8,9], utf8_stringX: "hello".to_owned()}});

    assert_eq!(result.uint16, 100);
    assert_eq!(result.uint32, 268435456);
    assert_eq!(result.uint64, 1152921504606846976);
    assert_eq!(result.uint128, ~[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

    assert_eq!(result.utf8_string,  "unicode! ☯ - ♫".to_owned());
}

#[test]
fn test_broken_database() {
    let r = Reader::open("test-data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb").unwrap();
    let ip: IpAddr = FromStr::from_str("2001:220::").unwrap();
    let result = r.lookup(ip);
    assert_eq!(result, Err(InvalidDatabaseError("double of size 2".to_owned())));
}

#[test]
fn test_missing_database() {
    let r = Reader::open("file-does-not-exist.mmdb");
    match r {
        Ok(_) => fail!("Received Reader when opening non-existent file"),
        Err(IoError(_)) => assert!(true),
        Err(_) => assert!(false)
    }
}

#[test]
fn test_non_database() {
    let r = Reader::open("README.md");
    match r {
        Ok(_) => fail!("Received Reader when opening a non-MMDB file"),
        Err(e) => assert_eq!(e, InvalidDatabaseError("Could not find MaxMind DB metadata in file.".to_owned()))

    }
}

#[test]
fn test_reader() {
    let sizes = [24u, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4u, 6];
        for ip_version in versions.iter() {
            let filename = format!("test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size);
            print!("{}\n", filename);
            let reader = Reader::open(filename).unwrap();

            check_metadata(&reader, *ip_version, *record_size);

             if *ip_version == 6u {
            //     check_ip_v4(reader)
            // } else {
                check_ipv6(&reader);
            }
        }
    }
}

fn check_metadata(reader: &Reader, ip_version: uint, record_size: uint) {
    let metadata = &reader.metadata;

    assert_eq!(metadata.binary_format_major_version,  2u16)

    assert_eq!(metadata.binary_format_minor_version,  0u16)
    assert!(metadata.build_epoch >= 1397457605)
    assert_eq!(metadata.database_type, "Test".to_owned())

    assert_eq!(*metadata.description.find(&"en".to_owned()).unwrap(),
                   "Test Database".to_owned());
    assert_eq!(*metadata.description.find(&"zh".to_owned()).unwrap(),
                   "Test Database Chinese".to_owned());

    assert_eq!(metadata.ip_version,  ip_version as u16)
    assert_eq!(metadata.languages, ~["en".to_owned(), "zh".to_owned()])

    if ip_version == 4 {
        assert_eq!(metadata.node_count,  37)
    } else {
        assert_eq!(metadata.node_count,  160)
    }

    assert_eq!(metadata.record_size,  record_size as u16)
}

fn check_ipv6(reader: &Reader) {

    let subnets =
        [["::1:ffff:ffff", "::1:ffff:ffff"],
        ["::2:0:0",  "::2:0:0"],
        ["::2:0:1",  "::2:0:0"],
        ["::2:0:33", "::2:0:0"],
        ["::2:0:39", "::2:0:0"],
        ["::2:0:40", "::2:0:40"],
        ["::2:0:41", "::2:0:40"],
        ["::2:0:49", "::2:0:40"],
        ["::2:0:50", "::2:0:50"],
        ["::2:0:52", "::2:0:50"],
        ["::2:0:57", "::2:0:50"],
        ["::2:0:58", "::2:0:58"],
        ["::2:0:59", "::2:0:58"]];

    #[deriving(Decodable, Show)]
    struct IpType  {
         ip: ~str,
    }

    for values in subnets.iter() {
        let ip: IpAddr = FromStr::from_str(values[0]).unwrap();
        let res = reader.lookup(ip).unwrap();

        let mut decoder = Decoder::new(res);
        let value: IpType =  match Decodable::decode(&mut decoder) {
            Ok(v) => v,
            Err(e) => fail!("Decoding error: {}", e)
        };
        assert_eq!(value.ip, values[1].to_owned());
    }

    let noRecord =  ["1.1.1.33", "255.254.253.123", "89fa::"];

    for &address in noRecord.iter() {
        let ip: IpAddr = FromStr::from_str(address).unwrap();
        match reader.lookup(ip) {
            Ok(v) => fail!("received an unexpected value: {}", v),
            Err(e) => assert_eq!(e, AddressNotFoundError("Address not found in database".to_owned()))
        }
    }
}
<commit_msg>Added IPv4 DB tests<commit_after>use super::{AddressNotFoundError, Decoder, InvalidDatabaseError, IoError,
            Reader};

use std::io::net::ip::IpAddr;
use std::from_str::FromStr;
use serialize::Decodable;

#[test]
fn test_decoder() {

    #[deriving(Decodable, Show, Eq)]
    struct MapXType {
        arrayX: ~[uint],
        utf8_stringX: ~str
    };

    #[deriving(Decodable, Show, Eq)]
    struct MapType {
        mapX: MapXType
    };

    #[deriving(Decodable, Show)]
    struct TestType {
        array:       ~[uint],
        boolean:     bool,
        bytes:       ~[u8],
        double:      f64,
        float:       f32,
        int32:       i32,
        map:         MapType,
        uint16:      u16,
        uint32:      u32,
        uint64:      u64,
        uint128:     ~[u8],
        utf8_string: ~str
    }

    let r = Reader::open("test-data/test-data/MaxMind-DB-test-decoder.mmdb").unwrap();
    let ip: IpAddr = FromStr::from_str("::1.1.1.0").unwrap();
    let raw_data = r.lookup(ip);

    let mut decoder = Decoder::new(raw_data.unwrap());
    let result: TestType = match Decodable::decode(&mut decoder) {
        Ok(v) => v,
        Err(e) => fail!("Decoding error: {}", e)
    };

    assert_eq!(result.array, ~[ 1u, 2u, 3u ]);
    assert_eq!(result.boolean, true);
    assert_eq!(result.bytes, ~[0u8, 0u8, 0u8, 42u8])
    assert_eq!(result.double, 42.123456);
    assert_eq!(result.float, 1.1);
    assert_eq!(result.int32, -268435456);

    assert_eq!(result.map, MapType{ mapX: MapXType{ arrayX: ~[7,8,9], utf8_stringX: "hello".to_owned()}});

    assert_eq!(result.uint16, 100);
    assert_eq!(result.uint32, 268435456);
    assert_eq!(result.uint64, 1152921504606846976);
    assert_eq!(result.uint128, ~[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

    assert_eq!(result.utf8_string,  "unicode! ☯ - ♫".to_owned());
}

#[test]
fn test_broken_database() {
    let r = Reader::open("test-data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb").unwrap();
    let ip: IpAddr = FromStr::from_str("2001:220::").unwrap();
    let result = r.lookup(ip);
    assert_eq!(result, Err(InvalidDatabaseError("double of size 2".to_owned())));
}

#[test]
fn test_missing_database() {
    let r = Reader::open("file-does-not-exist.mmdb");
    match r {
        Ok(_) => fail!("Received Reader when opening non-existent file"),
        Err(IoError(_)) => assert!(true),
        Err(_) => assert!(false)
    }
}

#[test]
fn test_non_database() {
    let r = Reader::open("README.md");
    match r {
        Ok(_) => fail!("Received Reader when opening a non-MMDB file"),
        Err(e) => assert_eq!(e, InvalidDatabaseError("Could not find MaxMind DB metadata in file.".to_owned()))

    }
}

#[test]
fn test_reader() {
    let sizes = [24u, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4u, 6];
        for ip_version in versions.iter() {
            let filename = format!("test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size);
            let reader = Reader::open(filename).unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

fn check_metadata(reader: &Reader, ip_version: uint, record_size: uint) {
    let metadata = &reader.metadata;

    assert_eq!(metadata.binary_format_major_version,  2u16)

    assert_eq!(metadata.binary_format_minor_version,  0u16)
    assert!(metadata.build_epoch >= 1397457605)
    assert_eq!(metadata.database_type, "Test".to_owned())

    assert_eq!(*metadata.description.find(&"en".to_owned()).unwrap(),
                   "Test Database".to_owned());
    assert_eq!(*metadata.description.find(&"zh".to_owned()).unwrap(),
                   "Test Database Chinese".to_owned());

    assert_eq!(metadata.ip_version,  ip_version as u16)
    assert_eq!(metadata.languages, ~["en".to_owned(), "zh".to_owned()])

    if ip_version == 4 {
        assert_eq!(metadata.node_count,  37)
    } else {
        assert_eq!(metadata.node_count,  160)
    }

    assert_eq!(metadata.record_size,  record_size as u16)
}

fn check_ip(reader: &Reader, ip_version: uint) {

    let subnets = match ip_version {
        6 =>   [["::1:ffff:ffff", "::1:ffff:ffff"],
                ["::2:0:0",  "::2:0:0"],
                ["::2:0:1",  "::2:0:0"],
                ["::2:0:33", "::2:0:0"],
                ["::2:0:39", "::2:0:0"],
                ["::2:0:40", "::2:0:40"],
                ["::2:0:41", "::2:0:40"],
                ["::2:0:49", "::2:0:40"],
                ["::2:0:50", "::2:0:50"],
                ["::2:0:52", "::2:0:50"],
                ["::2:0:57", "::2:0:50"],
                ["::2:0:58", "::2:0:58"],
                ["::2:0:59", "::2:0:58"]],
        _ =>   [["1.1.1.1", "1.1.1.1"],
                ["1.1.1.2", "1.1.1.2"],
                ["1.1.1.3", "1.1.1.2"],
                ["1.1.1.4", "1.1.1.4"],
                ["1.1.1.5",  "1.1.1.4"],
                ["1.1.1.6",  "1.1.1.4"],
                ["1.1.1.7",  "1.1.1.4"],
                ["1.1.1.8",  "1.1.1.8"],
                ["1.1.1.9",  "1.1.1.8"],
                ["1.1.1.15",  "1.1.1.8"],
                ["1.1.1.16",  "1.1.1.16"],
                ["1.1.1.17",  "1.1.1.16"],
                ["1.1.1.31",  "1.1.1.16"]]
    };

    #[deriving(Decodable, Show)]
    struct IpType  {
         ip: ~str,
    }

    for values in subnets.iter() {
        let ip: IpAddr = FromStr::from_str(values[0]).unwrap();
        let res = reader.lookup(ip).unwrap();

        let mut decoder = Decoder::new(res);
        let value: IpType =  match Decodable::decode(&mut decoder) {
            Ok(v) => v,
            Err(e) => fail!("Decoding error: {}", e)
        };
        assert_eq!(value.ip, values[1].to_owned());
    }

    let noRecord =  ["1.1.1.33", "255.254.253.123", "89fa::"];

    for &address in noRecord.iter() {
        let ip: IpAddr = FromStr::from_str(address).unwrap();
        match reader.lookup(ip) {
            Ok(v) => fail!("received an unexpected value: {}", v),
            Err(e) => assert_eq!(e, AddressNotFoundError("Address not found in database".to_owned()))
        }
    }
}
<|endoftext|>


<commit_before>use super::{MaxMindDBError, Reader};

use std::net::IpAddr;
use std::str::FromStr;

#[allow(clippy::float_cmp)]
#[test]
fn test_decoder() {
    let _ = env_logger::try_init();

    #[allow(non_snake_case)]
    #[derive(Deserialize, Debug, Eq, PartialEq)]
    struct MapXType {
        arrayX: Vec<u32>,
        utf8_stringX: String,
    };

    #[allow(non_snake_case)]
    #[derive(Deserialize, Debug, Eq, PartialEq)]
    struct MapType {
        mapX: MapXType,
    };

    #[derive(Deserialize, Debug)]
    struct TestType {
        array: Vec<u32>,
        boolean: bool,
        bytes: Vec<u8>,
        double: f64,
        float: f32,
        int32: i32,
        map: MapType,
        uint16: u16,
        uint32: u32,
        uint64: u64,
        uint128: Vec<u8>,
        utf8_string: String,
    }

    let r = Reader::open_readfile("test-data/test-data/MaxMind-DB-test-decoder.mmdb");
    if let Err(err) = r {
        panic!(format!("error opening mmdb: {:?}", err));
    }
    let r = r.unwrap();
    let ip: IpAddr = FromStr::from_str("1.1.1.0").unwrap();
    let result: TestType = r.lookup(ip).unwrap();

    assert_eq!(result.array, vec![1u32, 2u32, 3u32]);
    assert_eq!(result.boolean, true);
    assert_eq!(result.bytes, vec![0u8, 0u8, 0u8, 42u8]);
    assert_eq!(result.double, 42.123_456);
    assert_eq!(result.float, 1.1);
    assert_eq!(result.int32, -268_435_456);

    assert_eq!(
        result.map,
        MapType {
            mapX: MapXType {
                arrayX: vec![7, 8, 9],
                utf8_stringX: "hello".to_string(),
            },
        }
    );

    assert_eq!(result.uint16, 100);
    assert_eq!(result.uint32, 268_435_456);
    assert_eq!(result.uint64, 1_152_921_504_606_846_976);
    assert_eq!(
        result.uint128,
        vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    );

    assert_eq!(result.utf8_string, "unicode! ☯ - ♫".to_string());
}

#[test]
fn test_broken_database() {
    let _ = env_logger::try_init();

    let r = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb")
        .ok()
        .unwrap();
    let ip: IpAddr = FromStr::from_str("2001:220::").unwrap();

    #[derive(Deserialize, Debug)]
    struct TestType;
    match r.lookup::<TestType>(ip) {
        Err(e) => assert_eq!(
            e,
            MaxMindDBError::InvalidDatabaseError("double of size 2".to_string())
        ),
        Ok(_) => panic!("Error expected"),
    }
}

#[test]
fn test_missing_database() {
    let _ = env_logger::try_init();

    let r = Reader::open_readfile("file-does-not-exist.mmdb");
    match r {
        Ok(_) => panic!("Received Reader when opening non-existent file"),
        Err(e) => assert_eq!(
            e,
            MaxMindDBError::IoError("No such file or directory (os error 2)".to_string())
        ),
    }
}

#[test]
fn test_non_database() {
    let _ = env_logger::try_init();

    let r = Reader::open_readfile("README.md");
    match r {
        Ok(_) => panic!("Received Reader when opening a non-MMDB file"),
        Err(e) => assert_eq!(
            e,
            MaxMindDBError::InvalidDatabaseError(
                "Could not find MaxMind DB metadata \
                 in file."
                    .to_string(),
            )
        ),
    }
}

#[test]
fn test_reader() {
    let _ = env_logger::try_init();

    let sizes = [24usize, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4usize, 6];
        for ip_version in versions.iter() {
            let filename = format!(
                "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size
            );
            let reader = Reader::open_readfile(filename).ok().unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

/// Create Reader by explicitly reading the entire file into a buffer.
#[test]
fn test_reader_readfile() {
    let _ = env_logger::try_init();

    let sizes = [24usize, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4usize, 6];
        for ip_version in versions.iter() {
            let filename = format!(
                "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size
            );
            let reader = Reader::open_readfile(filename).ok().unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

#[test]
#[cfg(feature = "mmap")]
fn test_reader_mmap() {
    let _ = env_logger::try_init();

    let sizes = [24usize, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4usize, 6];
        for ip_version in versions.iter() {
            let filename = format!(
                "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size
            );
            let reader = Reader::open_mmap(filename).ok().unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

#[test]
fn test_lookup_city() {
    use super::geoip2::City;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-City-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap();
    let city: City = reader.lookup(ip).unwrap();

    let iso_code = city.country.and_then(|cy| cy.iso_code);

    assert_eq!(iso_code, Some("SE"));
}

#[test]
fn test_lookup_country() {
    use super::geoip2::Country;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Country-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap();
    let country: Country = reader.lookup(ip).unwrap();
    let country: super::geoip2::model::Country = country.country.unwrap();

    assert_eq!(country.iso_code, Some("SE"));
    assert_eq!(country.is_in_european_union, Some(true));
}

#[test]
fn test_lookup_connection_type() {
    use super::geoip2::ConnectionType;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Connection-Type-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("96.1.20.112").unwrap();
    let connection_type: ConnectionType = reader.lookup(ip).unwrap();

    assert_eq!(connection_type.connection_type, Some("Cable/DSL"));
}

#[test]
fn test_lookup_annonymous_ip() {
    use super::geoip2::AnonymousIp;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Anonymous-IP-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("81.2.69.123").unwrap();
    let anonymous_ip: AnonymousIp = reader.lookup(ip).unwrap();

    assert_eq!(anonymous_ip.is_anonymous, Some(true));
    assert_eq!(anonymous_ip.is_public_proxy, Some(true));
    assert_eq!(anonymous_ip.is_anonymous_vpn, Some(true));
    assert_eq!(anonymous_ip.is_hosting_provider, Some(true));
    assert_eq!(anonymous_ip.is_tor_exit_node, Some(true))
}

#[test]
fn test_lookup_density_income() {
    use super::geoip2::DensityIncome;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-DensityIncome-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("5.83.124.123").unwrap();
    let density_income: DensityIncome = reader.lookup(ip).unwrap();

    assert_eq!(density_income.average_income, Some(32323));
    assert_eq!(density_income.population_density, Some(1232))
}

#[test]
fn test_lookup_domain() {
    use super::geoip2::Domain;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Domain-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("66.92.80.123").unwrap();
    let domain: Domain = reader.lookup(ip).unwrap();

    assert_eq!(domain.domain, Some("speakeasy.net"));
}

#[test]
fn test_lookup_isp() {
    use super::geoip2::Isp;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("12.87.118.123").unwrap();
    let isp: Isp = reader.lookup(ip).unwrap();

    assert_eq!(isp.autonomous_system_number, Some(7018));
    assert_eq!(isp.isp, Some("AT&T Services"));
    assert_eq!(isp.organization, Some("AT&T Worldnet Services"));
}

#[test]
fn test_lookup_asn() {
    use super::geoip2::Asn;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("1.128.0.123").unwrap();
    let asn: Asn = reader.lookup(ip).unwrap();

    assert_eq!(asn.autonomous_system_number, Some(1221));
    assert_eq!(asn.autonomous_system_organization, Some("Telstra Pty Ltd"));
}

fn check_metadata<T: AsRef<[u8]>>(reader: &Reader<T>, ip_version: usize, record_size: usize) {
    let metadata = &reader.metadata;

    assert_eq!(metadata.binary_format_major_version, 2u16);

    assert_eq!(metadata.binary_format_minor_version, 0u16);
    assert!(metadata.build_epoch >= 1_397_457_605);
    assert_eq!(metadata.database_type, "Test".to_string());

    assert_eq!(
        *metadata.description[&"en".to_string()],
        "Test Database".to_string()
    );
    assert_eq!(
        *metadata.description[&"zh".to_string()],
        "Test Database Chinese".to_string()
    );

    assert_eq!(metadata.ip_version, ip_version as u16);
    assert_eq!(metadata.languages, vec!["en".to_string(), "zh".to_string()]);

    if ip_version == 4 {
        assert_eq!(metadata.node_count, 164)
    } else {
        assert_eq!(metadata.node_count, 416)
    }

    assert_eq!(metadata.record_size, record_size as u16)
}

fn check_ip<T: AsRef<[u8]>>(reader: &Reader<T>, ip_version: usize) {
    let subnets = match ip_version {
        6 => [
            "::1:ffff:ffff",
            "::2:0:0",
            "::2:0:0",
            "::2:0:0",
            "::2:0:0",
            "::2:0:40",
            "::2:0:40",
            "::2:0:40",
            "::2:0:50",
            "::2:0:50",
            "::2:0:50",
            "::2:0:58",
            "::2:0:58",
        ],
        _ => [
            "1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.4", "1.1.1.4", "1.1.1.4", "1.1.1.4", "1.1.1.8",
            "1.1.1.8", "1.1.1.8", "1.1.1.16", "1.1.1.16", "1.1.1.16",
        ],
    };

    #[derive(Deserialize, Debug)]
    struct IpType {
        ip: String,
    }

    for subnet in subnets.iter() {
        let ip: IpAddr = FromStr::from_str(&subnet).unwrap();
        let value: IpType = reader.lookup(ip).unwrap();

        assert_eq!(value.ip, *subnet);
    }

    let no_record = ["1.1.1.33", "255.254.253.123", "89fa::"];

    for &address in no_record.iter() {
        let ip: IpAddr = FromStr::from_str(address).unwrap();
        match reader.lookup::<IpType>(ip) {
            Ok(v) => panic!("received an unexpected value: {:?}", v),
            Err(e) => assert_eq!(
                e,
                MaxMindDBError::AddressNotFoundError("Address not found in database".to_string())
            ),
        }
    }
}
<commit_msg>Quick fix for test on Windows<commit_after>use super::{MaxMindDBError, Reader};

use std::net::IpAddr;
use std::str::FromStr;

#[allow(clippy::float_cmp)]
#[test]
fn test_decoder() {
    let _ = env_logger::try_init();

    #[allow(non_snake_case)]
    #[derive(Deserialize, Debug, Eq, PartialEq)]
    struct MapXType {
        arrayX: Vec<u32>,
        utf8_stringX: String,
    };

    #[allow(non_snake_case)]
    #[derive(Deserialize, Debug, Eq, PartialEq)]
    struct MapType {
        mapX: MapXType,
    };

    #[derive(Deserialize, Debug)]
    struct TestType {
        array: Vec<u32>,
        boolean: bool,
        bytes: Vec<u8>,
        double: f64,
        float: f32,
        int32: i32,
        map: MapType,
        uint16: u16,
        uint32: u32,
        uint64: u64,
        uint128: Vec<u8>,
        utf8_string: String,
    }

    let r = Reader::open_readfile("test-data/test-data/MaxMind-DB-test-decoder.mmdb");
    if let Err(err) = r {
        panic!(format!("error opening mmdb: {:?}", err));
    }
    let r = r.unwrap();
    let ip: IpAddr = FromStr::from_str("1.1.1.0").unwrap();
    let result: TestType = r.lookup(ip).unwrap();

    assert_eq!(result.array, vec![1u32, 2u32, 3u32]);
    assert_eq!(result.boolean, true);
    assert_eq!(result.bytes, vec![0u8, 0u8, 0u8, 42u8]);
    assert_eq!(result.double, 42.123_456);
    assert_eq!(result.float, 1.1);
    assert_eq!(result.int32, -268_435_456);

    assert_eq!(
        result.map,
        MapType {
            mapX: MapXType {
                arrayX: vec![7, 8, 9],
                utf8_stringX: "hello".to_string(),
            },
        }
    );

    assert_eq!(result.uint16, 100);
    assert_eq!(result.uint32, 268_435_456);
    assert_eq!(result.uint64, 1_152_921_504_606_846_976);
    assert_eq!(
        result.uint128,
        vec![1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    );

    assert_eq!(result.utf8_string, "unicode! ☯ - ♫".to_string());
}

#[test]
fn test_broken_database() {
    let _ = env_logger::try_init();

    let r = Reader::open_readfile("test-data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb")
        .ok()
        .unwrap();
    let ip: IpAddr = FromStr::from_str("2001:220::").unwrap();

    #[derive(Deserialize, Debug)]
    struct TestType;
    match r.lookup::<TestType>(ip) {
        Err(e) => assert_eq!(
            e,
            MaxMindDBError::InvalidDatabaseError("double of size 2".to_string())
        ),
        Ok(_) => panic!("Error expected"),
    }
}

#[test]
fn test_missing_database() {
    let _ = env_logger::try_init();

    let r = Reader::open_readfile("file-does-not-exist.mmdb");
    match r {
        Ok(_) => panic!("Received Reader when opening non-existent file"),
        Err(e) => assert!(
            e == MaxMindDBError::IoError(
                "The system cannot find the file specified. (os error 2)".to_string()
            ) || e == MaxMindDBError::IoError("No such file or directory (os error 2)".to_string())
        ),
    }
}

#[test]
fn test_non_database() {
    let _ = env_logger::try_init();

    let r = Reader::open_readfile("README.md");
    match r {
        Ok(_) => panic!("Received Reader when opening a non-MMDB file"),
        Err(e) => assert_eq!(
            e,
            MaxMindDBError::InvalidDatabaseError(
                "Could not find MaxMind DB metadata \
                 in file."
                    .to_string(),
            )
        ),
    }
}

#[test]
fn test_reader() {
    let _ = env_logger::try_init();

    let sizes = [24usize, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4usize, 6];
        for ip_version in versions.iter() {
            let filename = format!(
                "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size
            );
            let reader = Reader::open_readfile(filename).ok().unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

/// Create Reader by explicitly reading the entire file into a buffer.
#[test]
fn test_reader_readfile() {
    let _ = env_logger::try_init();

    let sizes = [24usize, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4usize, 6];
        for ip_version in versions.iter() {
            let filename = format!(
                "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size
            );
            let reader = Reader::open_readfile(filename).ok().unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

#[test]
#[cfg(feature = "mmap")]
fn test_reader_mmap() {
    let _ = env_logger::try_init();

    let sizes = [24usize, 28, 32];
    for record_size in sizes.iter() {
        let versions = [4usize, 6];
        for ip_version in versions.iter() {
            let filename = format!(
                "test-data/test-data/MaxMind-DB-test-ipv{}-{}.mmdb",
                ip_version, record_size
            );
            let reader = Reader::open_mmap(filename).ok().unwrap();

            check_metadata(&reader, *ip_version, *record_size);
            check_ip(&reader, *ip_version);
        }
    }
}

#[test]
fn test_lookup_city() {
    use super::geoip2::City;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-City-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap();
    let city: City = reader.lookup(ip).unwrap();

    let iso_code = city.country.and_then(|cy| cy.iso_code);

    assert_eq!(iso_code, Some("SE"));
}

#[test]
fn test_lookup_country() {
    use super::geoip2::Country;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Country-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("89.160.20.112").unwrap();
    let country: Country = reader.lookup(ip).unwrap();
    let country: super::geoip2::model::Country = country.country.unwrap();

    assert_eq!(country.iso_code, Some("SE"));
    assert_eq!(country.is_in_european_union, Some(true));
}

#[test]
fn test_lookup_connection_type() {
    use super::geoip2::ConnectionType;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Connection-Type-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("96.1.20.112").unwrap();
    let connection_type: ConnectionType = reader.lookup(ip).unwrap();

    assert_eq!(connection_type.connection_type, Some("Cable/DSL"));
}

#[test]
fn test_lookup_annonymous_ip() {
    use super::geoip2::AnonymousIp;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Anonymous-IP-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("81.2.69.123").unwrap();
    let anonymous_ip: AnonymousIp = reader.lookup(ip).unwrap();

    assert_eq!(anonymous_ip.is_anonymous, Some(true));
    assert_eq!(anonymous_ip.is_public_proxy, Some(true));
    assert_eq!(anonymous_ip.is_anonymous_vpn, Some(true));
    assert_eq!(anonymous_ip.is_hosting_provider, Some(true));
    assert_eq!(anonymous_ip.is_tor_exit_node, Some(true))
}

#[test]
fn test_lookup_density_income() {
    use super::geoip2::DensityIncome;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-DensityIncome-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("5.83.124.123").unwrap();
    let density_income: DensityIncome = reader.lookup(ip).unwrap();

    assert_eq!(density_income.average_income, Some(32323));
    assert_eq!(density_income.population_density, Some(1232))
}

#[test]
fn test_lookup_domain() {
    use super::geoip2::Domain;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-Domain-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("66.92.80.123").unwrap();
    let domain: Domain = reader.lookup(ip).unwrap();

    assert_eq!(domain.domain, Some("speakeasy.net"));
}

#[test]
fn test_lookup_isp() {
    use super::geoip2::Isp;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("12.87.118.123").unwrap();
    let isp: Isp = reader.lookup(ip).unwrap();

    assert_eq!(isp.autonomous_system_number, Some(7018));
    assert_eq!(isp.isp, Some("AT&T Services"));
    assert_eq!(isp.organization, Some("AT&T Worldnet Services"));
}

#[test]
fn test_lookup_asn() {
    use super::geoip2::Asn;
    let _ = env_logger::try_init();

    let filename = "test-data/test-data/GeoIP2-ISP-Test.mmdb";

    let reader = Reader::open_readfile(filename).unwrap();

    let ip: IpAddr = FromStr::from_str("1.128.0.123").unwrap();
    let asn: Asn = reader.lookup(ip).unwrap();

    assert_eq!(asn.autonomous_system_number, Some(1221));
    assert_eq!(asn.autonomous_system_organization, Some("Telstra Pty Ltd"));
}

fn check_metadata<T: AsRef<[u8]>>(reader: &Reader<T>, ip_version: usize, record_size: usize) {
    let metadata = &reader.metadata;

    assert_eq!(metadata.binary_format_major_version, 2u16);

    assert_eq!(metadata.binary_format_minor_version, 0u16);
    assert!(metadata.build_epoch >= 1_397_457_605);
    assert_eq!(metadata.database_type, "Test".to_string());

    assert_eq!(
        *metadata.description[&"en".to_string()],
        "Test Database".to_string()
    );
    assert_eq!(
        *metadata.description[&"zh".to_string()],
        "Test Database Chinese".to_string()
    );

    assert_eq!(metadata.ip_version, ip_version as u16);
    assert_eq!(metadata.languages, vec!["en".to_string(), "zh".to_string()]);

    if ip_version == 4 {
        assert_eq!(metadata.node_count, 164)
    } else {
        assert_eq!(metadata.node_count, 416)
    }

    assert_eq!(metadata.record_size, record_size as u16)
}

fn check_ip<T: AsRef<[u8]>>(reader: &Reader<T>, ip_version: usize) {
    let subnets = match ip_version {
        6 => [
            "::1:ffff:ffff",
            "::2:0:0",
            "::2:0:0",
            "::2:0:0",
            "::2:0:0",
            "::2:0:40",
            "::2:0:40",
            "::2:0:40",
            "::2:0:50",
            "::2:0:50",
            "::2:0:50",
            "::2:0:58",
            "::2:0:58",
        ],
        _ => [
            "1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.4", "1.1.1.4", "1.1.1.4", "1.1.1.4", "1.1.1.8",
            "1.1.1.8", "1.1.1.8", "1.1.1.16", "1.1.1.16", "1.1.1.16",
        ],
    };

    #[derive(Deserialize, Debug)]
    struct IpType {
        ip: String,
    }

    for subnet in subnets.iter() {
        let ip: IpAddr = FromStr::from_str(&subnet).unwrap();
        let value: IpType = reader.lookup(ip).unwrap();

        assert_eq!(value.ip, *subnet);
    }

    let no_record = ["1.1.1.33", "255.254.253.123", "89fa::"];

    for &address in no_record.iter() {
        let ip: IpAddr = FromStr::from_str(address).unwrap();
        match reader.lookup::<IpType>(ip) {
            Ok(v) => panic!("received an unexpected value: {:?}", v),
            Err(e) => assert_eq!(
                e,
                MaxMindDBError::AddressNotFoundError("Address not found in database".to_string())
            ),
        }
    }
}
<|endoftext|>