<commit_before>//! A module, providing way to parse Bencoded files (e.g. torrent files).
//!
//! Contains all types of elements (`BNumber`, `BList`, `BDictionary`, `BString`), which
//! have trait `BElement` with decoding functionality.

use std::fmt::Formatter;
use std::fmt::Debug;
use std::fmt::Error;

/// Trait for all bencode elements.
///
/// Provides way to decode some type of element `T`, which must have trait `BElement`, from
/// array of bytes
///
/// # Examples
///
/// Simple implementation.
///
/// ```ignore
/// struct BExample {
///     e: i8,
/// }
///
/// impl BElement<BExample> for BExample {
///     //Implementation
/// }
/// ```
pub trait BElement<T> where T: BElement<T> {
    /// Decodes element from array of bytes.
    ///
    /// Returns `Ok((position of last used byte in array of bytes, parsed element))` or
    /// `Err` if parse has failed.
    fn decode(encoded: &[u8]) -> Result<(usize, T), &'static str>;
}

/// Struct for representing numbers in Bencode format.
pub struct BNumber {
    /// Real number, represented by `BNumber`.
    number: i64,
}

impl BNumber {
    /// Simple constructor from one `i64`.
    pub fn new(number: i64) -> BNumber {
        BNumber { number: number }
    }
}

/// Basic equivalence relation.
///
/// Checks for equality simply using `BNumber`'s `number` field. Works exactly
/// like equivalence in i64.
impl PartialEq for BNumber {
    fn eq(&self, other: &Self) -> bool {
        self.number == other.number
    }

    fn ne(&self, other: &Self) -> bool {
        self.number != other.number
    }
}

/// Guarantees to be reflexive.
impl Eq for BNumber {

}

/// Simple `Debug` implementation.
///
/// Works just like `i64::fmt`.
impl Debug for BNumber {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        self.number.fmt(f)
    }
}

impl BElement<BNumber> for BNumber {

    /// Decodes BNumber from array of bytes.
    ///
    /// Returnes `Ok((position of next token in bencode, parsed BNumber))`
    /// or `Error` if couldn't parse BNumber.
    /// # Examples
    ///
    /// BNumber must begin with 'i' char and end with 'e' char.
    ///
    /// ```
    /// use bencode::BElement;
    /// use bencode::BNumber;
    /// assert_eq!((4, BNumber::new(300)),
    ///                BNumber::decode("i300e".as_bytes()).ok().expect("invalid"));
    /// ```
    ///
    /// If it's not, then error is generated.
    ///
    /// ```
    /// use bencode::BElement;
    /// use bencode::BNumber;
    /// assert!(BNumber::decode("l300e".as_bytes()).is_err());
    /// ```
    fn decode(encoded: &[u8]) -> Result<(usize, BNumber), &'static str> {
        match encoded[0] as char {
            'i' => {
                let mut i : usize = 1;
                while i < encoded.len() && (encoded[i] as char).is_digit(10) {
                    i += 1;
                }
                if i == encoded.len() {
                    return Err("expected 'e' after number");
                }
                let number: &[u8] = &encoded[1..i];
                match encoded[i] as char {
                    'e' => {
                        let str_number: String = String::from_utf8_lossy(number).into_owned();
                        if let Ok(x) = str_number.parse::<i64>() {
                            Ok((i, BNumber::new(x)))
                        } else {
                            Err("expected correct i64 number")
                        }
                    },
                    _ => Err("expected 'e' after number")
                }
            },
            _ => Err("expected 'i' before number")
        }
    }
}

#[test]
fn test1_bnumber_parse() {
    let (index, number) = BNumber::decode("i300e".as_bytes()).ok().expect("invalid");
    assert_eq!(300, number.number);
    assert_eq!(4, index);
}
<commit_msg>Fixed BNumber, added BString implementation.<commit_after>//! A module, providing way to parse Bencoded files (e.g. torrent files).
//!
//! Contains all types of elements (`BNumber`, `BList`, `BDictionary`, `BString`), which
//! have trait `BElement` with decoding functionality.

use std::fmt::Formatter;
use std::fmt::Debug;
use std::fmt::Error;

/// Trait for all bencode elements.
///
/// Provides way to decode some type of element `T`, which must have trait `BElement`, from
/// array of bytes
///
/// # Examples
///
/// Simple implementation.
///
/// ```ignore
/// struct BExample {
///     e: i8,
/// }
///
/// impl BElement<BExample> for BExample {
///     //Implementation
/// }
/// ```
pub trait BElement<T> where T: BElement<T> {
    /// Decodes element from array of bytes.
    ///
    /// Returns `Ok((position of last used byte in array of bytes, parsed element))` or
    /// `Err` if parse has failed.
    fn decode(encoded: &[u8]) -> Result<(usize, T), &'static str>;
}

/// Struct for representing numbers in Bencode format.
pub struct BNumber {
    /// Real number, represented by `BNumber`.
    number: i64,
}

impl BNumber {
    /// Simple constructor from one `i64`.
    pub fn new(number: i64) -> BNumber {
        BNumber { number: number }
    }
}

/// Basic equivalence relation.
///
/// Checks for equality simply using `BNumber`'s `number` field. Works exactly
/// like equivalence in i64.
impl PartialEq for BNumber {
    fn eq(&self, other: &Self) -> bool {
        self.number == other.number
    }

    fn ne(&self, other: &Self) -> bool {
        self.number != other.number
    }
}

/// Guarantees to be reflexive.
impl Eq for BNumber {

}

/// Simple `Debug` implementation.
///
/// Works just like `i64::fmt`.
impl Debug for BNumber {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        self.number.fmt(f)
    }
}

impl BElement<BNumber> for BNumber {

    /// Decodes `BNumber` from array of bytes.
    ///
    /// Returnes `Ok((position of last used byte in passed array, parsed BNumber))`
    /// or `Err` if couldn't parse BNumber correctly.
    /// # Examples
    ///
    /// BNumber must begin with 'i' char and end with 'e' char.
    ///
    /// ```
    /// use bencode::BElement;
    /// use bencode::BNumber;
    /// assert_eq!((4, BNumber::new(300)),
    ///                BNumber::decode("i300e".as_bytes()).ok().expect("invalid"));
    /// assert_eq!((5, BNumber::new(-204)),
    ///                BNumber::decode("i-204e".as_bytes()).ok().expect("invalid"));
    /// ```
    ///
    /// If it's not, then error is generated.
    ///
    /// ```
    /// use bencode::BElement;
    /// use bencode::BNumber;
    /// assert!(BNumber::decode("l300e".as_bytes()).is_err());
    /// ```
    ///
    /// Also error is generated, when number is too big for `i64`.
    ///
    /// ```
    /// use bencode::BElement;
    /// use bencode::BNumber;
    /// assert!(BNumber::decode("i400000000000000000000000000000000000000000000e".as_bytes()).is_err());
    /// ```
    fn decode(encoded: &[u8]) -> Result<(usize, BNumber), &'static str> {
        match encoded[0] as char {
            'i' => {
                let mut i : usize = 1;
                while i < encoded.len() && encoded[i] as char != 'e' {
                    i += 1;
                }
                if i == encoded.len() {
                    return Err("expected 'e' after number");
                }
                let number: &[u8] = &encoded[1..i];
                let str_number: String = String::from_utf8_lossy(number).into_owned();
                if let Ok(x) = str_number.parse::<i64>() {
                    Ok((i, BNumber::new(x)))
                } else {
                    Err("expected correct i64 number")
                }
            },
            _ => Err("expected 'i' before number")
        }
    }
}

/// Struct for representing string (byte sequence) in Bencode format.
pub struct BString {
    /// Sequence of bytes, contained in this `BString`.
    data: String,
}

/// Basic equivalence relation.
///
/// Checks for equality simply using `BString`'s `data` field. Works exactly
/// like equivalence in &[u8].
impl PartialEq for BString {
    fn eq(&self, other: &Self) -> bool {
        self.data == other.data
    }

    fn ne(&self, other: &Self) -> bool {
        self.data != other.data
    }
}

/// Guarantees to be reflexive.
impl Eq for BString {

}

/// Simple `Debug` implementation.
///
/// Works just like `[u8]::fmt`.
impl Debug for BString{
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        self.data.fmt(f)
    }
}

impl BString {
    /// Simple constructor from array of bytes.
    pub fn new(data: &[u8]) -> BString {
        BString { data: String::from_utf8_lossy(data).into_owned() }
    }
}

impl BElement<BString> for BString {
    /// Decodes `BString` from array of bytes.
    ///
    /// Returns `Ok((position of last used byte in passed array, parsed BString))`
    /// or `Err` if couldn't parse `BString` correctly.
    /// # Examples
    ///
    /// `BString` must have following structure: <length>:<data>, where data - sequence
    /// of bytes with corresponding length.
    ///
    /// ```
    /// use bencode::BElement;
    /// use bencode::BString;
    ///
    /// assert_eq!((4, BString::new("abc".as_bytes())),
    ///            BString::decode("3:abc".as_bytes()).ok().expect("invalid"));
    /// ```
    fn decode(encoded: &[u8]) -> Result<(usize, BString), &'static str> {
        let mut i: usize = 0;
        while i < encoded.len() && encoded[i] as char != ':' {
            i += 1;
        }
        if i == encoded.len() {
            Err("expected :, but end was found")
        } else {
            let length: &[u8] = &encoded[0..i];
            if let Ok(x) = String::from_utf8_lossy(length).into_owned().parse::<usize>() {
                let value: &[u8] = &encoded[i + 1..i + x + 1];
                Ok((i + x, BString::new(value)))
            } else {
                Err("expected correct usize number, representing length")
            }
        }
    }
}

#[test]
fn test1_bnumber_parse() {
    let (index, number) = BNumber::decode("i300e".as_bytes()).ok().expect("invalid");
    assert_eq!(300, number.number);
    assert_eq!(4, index);
}
<|endoftext|>


<commit_before>//! Openapi provides structures and support for serializing and deserializing [openapi](https://github.com/OAI/OpenAPI-Specification) specifications
//!
//! # Examples
//!
//! Typical use deserialing an existing to a persisted spec to rust form or
//! visa versa
//!
//! The hyper client should be configured with tls.
//!
//! ```no_run
//! extern crate openapi;
//!
//! fn main() {
//!   match openapi::from_path("path/to/openapi.yaml") {
//!     Ok(spec) => println!("spec: {:?}", spec),
//!     Err(err) => println!("error: {}", err)
//!   }
//! }
//! ```
//!
//! # Errors
//!
//! Operations typically result in a `openapi::Result` Type which is an alias
//! for Rust's
//! built-in Result with the Err Type fixed to the
//! [openapi::errors::Error](errors/struct.Error.html) enum type. These are
//! provided
//! using [error_chain](https://github.com/brson/error-chain) crate so their
//! shape and behavior should be consistent and familiar to existing
//! error_chain users.
//!
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
extern crate url;
extern crate url_serde;
extern crate semver;

use std::fs;
use std::io::Read;
use std::path::Path;

pub mod v2;
pub mod v3_0;

const MINIMUM_OPENAPI30_VERSION: &str = ">= 3.0";

/// errors that openapi functions may return
pub mod errors {
    error_chain!{
        foreign_links {
            Io(::std::io::Error);
            Yaml(::serde_yaml::Error);
            Serialize(::serde_json::Error);
            SemVerError(::semver::SemVerError);
        }

        errors {
            UnsupportedSpecFileVersion(version: ::semver::Version) {
                description("Unsupported spec file version")
                display("Unsupported spec file version ({}). Expected {}", version, ::MINIMUM_OPENAPI30_VERSION)
            }
        }
    }
}
pub use errors::{Result, ResultExt};

/// Supported versions of the OpenApi.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum OpenApi {
    /// Version 2.0 of the OpenApi specification.
    ///
    /// Refer to the official
    /// [specification](https://github.com/OAI/OpenAPI-Specification/blob/0dd79f6/versions/2.0.md)
    /// for more information.
    V2(v2::Spec),

    /// Version 3.0.1 of the OpenApi specification.
    ///
    /// Refer to the official
    /// [specification](https://github.com/OAI/OpenAPI-Specification/blob/0dd79f6/versions/3.0.1.md)
    /// for more information.
    #[allow(non_camel_case_types)]
    V3_0(v3_0::Spec),
}

/// deserialize an open api spec from a path
pub fn from_path<P>(path: P) -> errors::Result<OpenApi>
where
    P: AsRef<Path>,
{
    from_reader(fs::File::open(path)?)
}

/// deserialize an open api spec from type which implements Read
pub fn from_reader<R>(read: R) -> errors::Result<OpenApi>
where
    R: Read,
{
    Ok(serde_yaml::from_reader::<R, OpenApi>(read)?)
}

/// serialize to a yaml string
pub fn to_yaml(spec: &OpenApi) -> errors::Result<String> {
    Ok(serde_yaml::to_string(spec)?)
}

/// serialize to a json string
pub fn to_json(spec: &OpenApi) -> errors::Result<String> {
    Ok(serde_json::to_string_pretty(spec)?)
}

#[cfg(test)]
mod tests {
    use super::*;

    // Just tests if the deserialization does not blow up. But does not test correctness
    #[test]
    fn can_deserialize() {
        for entry in fs::read_dir("data/v2").unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            // cargo test -- --nocapture to see this message
            println!("Testing if {:?} is deserializable", path);
            from_path(path).unwrap();
        }
    }
}
<commit_msg>Add two tests for JSON conversion validation (both v2 and v3.0)<commit_after>//! Openapi provides structures and support for serializing and deserializing [openapi](https://github.com/OAI/OpenAPI-Specification) specifications
//!
//! # Examples
//!
//! Typical use deserialing an existing to a persisted spec to rust form or
//! visa versa
//!
//! The hyper client should be configured with tls.
//!
//! ```no_run
//! extern crate openapi;
//!
//! fn main() {
//!   match openapi::from_path("path/to/openapi.yaml") {
//!     Ok(spec) => println!("spec: {:?}", spec),
//!     Err(err) => println!("error: {}", err)
//!   }
//! }
//! ```
//!
//! # Errors
//!
//! Operations typically result in a `openapi::Result` Type which is an alias
//! for Rust's
//! built-in Result with the Err Type fixed to the
//! [openapi::errors::Error](errors/struct.Error.html) enum type. These are
//! provided
//! using [error_chain](https://github.com/brson/error-chain) crate so their
//! shape and behavior should be consistent and familiar to existing
//! error_chain users.
//!
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
extern crate url;
extern crate url_serde;
extern crate semver;

use std::fs;
use std::io::Read;
use std::path::Path;

pub mod v2;
pub mod v3_0;

const MINIMUM_OPENAPI30_VERSION: &str = ">= 3.0";

/// errors that openapi functions may return
pub mod errors {
    error_chain!{
        foreign_links {
            Io(::std::io::Error);
            Yaml(::serde_yaml::Error);
            Serialize(::serde_json::Error);
            SemVerError(::semver::SemVerError);
        }

        errors {
            UnsupportedSpecFileVersion(version: ::semver::Version) {
                description("Unsupported spec file version")
                display("Unsupported spec file version ({}). Expected {}", version, ::MINIMUM_OPENAPI30_VERSION)
            }
        }
    }
}
pub use errors::{Result, ResultExt};

/// Supported versions of the OpenApi.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum OpenApi {
    /// Version 2.0 of the OpenApi specification.
    ///
    /// Refer to the official
    /// [specification](https://github.com/OAI/OpenAPI-Specification/blob/0dd79f6/versions/2.0.md)
    /// for more information.
    V2(v2::Spec),

    /// Version 3.0.1 of the OpenApi specification.
    ///
    /// Refer to the official
    /// [specification](https://github.com/OAI/OpenAPI-Specification/blob/0dd79f6/versions/3.0.1.md)
    /// for more information.
    #[allow(non_camel_case_types)]
    V3_0(v3_0::Spec),
}

/// deserialize an open api spec from a path
pub fn from_path<P>(path: P) -> errors::Result<OpenApi>
where
    P: AsRef<Path>,
{
    from_reader(fs::File::open(path)?)
}

/// deserialize an open api spec from type which implements Read
pub fn from_reader<R>(read: R) -> errors::Result<OpenApi>
where
    R: Read,
{
    Ok(serde_yaml::from_reader::<R, OpenApi>(read)?)
}

/// serialize to a yaml string
pub fn to_yaml(spec: &OpenApi) -> errors::Result<String> {
    Ok(serde_yaml::to_string(spec)?)
}

/// serialize to a json string
pub fn to_json(spec: &OpenApi) -> errors::Result<String> {
    Ok(serde_json::to_string_pretty(spec)?)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;
    use std::io::Write;

    /// Helper function for reading a file to string.
    fn read_file<P>(path: P) -> String
    where
        P: AsRef<Path>,
    {
        let mut f = File::open(path).unwrap();
        let mut content = String::new();
        f.read_to_string(&mut content).unwrap();
        content
    }

    /// Helper function to write string to file.
    fn write_to_file<P>(path: P, filename: &str, data: &str)
    where
        P: AsRef<Path> + std::fmt::Debug,
    {
        println!("    Saving string to {:?}...", path);
        std::fs::create_dir_all(&path).unwrap();
        let full_filename = path.as_ref().to_path_buf().join(filename);
        let mut f = File::create(&full_filename).unwrap();
        f.write_all(data.as_bytes()).unwrap();
    }

    /// Convert a YAML `&str` to a JSON `String`.
    fn convert_yaml_str_to_json(yaml_str: &str) -> String {
        let yaml: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap();
        let json: serde_json::Value = serde_yaml::from_value(yaml).unwrap();
        serde_json::to_string_pretty(&json).unwrap()
    }

    /// Deserialize and re-serialize the input file to a JSON string through two different
    /// paths, comparing the result.
    /// 1. File -> `String` -> `serde_yaml::Value` -> `serde_json::Value` -> `String`
    /// 2. File -> `Spec` -> `serde_json::Value` -> `String`
    /// Both conversion of `serde_json::Value` -> `String` are done
    /// using `serde_json::to_string_pretty`.
    /// Since the first conversion is independant of the current crate (and only
    /// uses serde's json and yaml support), no information should be lost in the final
    /// JSON string. The second conversion goes through our `OpenApi`, so the final JSON
    /// string is a representation of _our_ implementation.
    /// By comparing those two JSON conversions, we can validate our implementation.
    fn compare_spec_through_json(input_file: &Path, save_path_base: &Path) -> (String, String, String) {
        // First conversion:
        //     File -> `String` -> `serde_yaml::Value` -> `serde_json::Value` -> `String`

        // Read the original file to string
        let spec_yaml_str = read_file(&input_file);
        // Convert YAML string to JSON string
        let spec_json_str = convert_yaml_str_to_json(&spec_yaml_str);

        // Second conversion:
        //     File -> `Spec` -> `serde_json::Value` -> `String`

        // Parse the input file
        let parsed_spec = from_path(&input_file).unwrap();
        // Convert to serde_json::Value
        let parsed_spec_json: serde_json::Value = serde_json::to_value(parsed_spec).unwrap();
        // Convert to a JSON string
        let parsed_spec_json_str: String = serde_json::to_string_pretty(&parsed_spec_json).unwrap();

        // Save JSON strings to file
        let api_filename = input_file.file_name()
            .unwrap()
            .to_str()
            .unwrap()
            .replace(".yaml", ".json");

        let mut save_path = save_path_base.to_path_buf();
        save_path.push("yaml_to_json");
        write_to_file(&save_path, &api_filename, &spec_json_str);

        let mut save_path = save_path_base.to_path_buf();
        save_path.push("yaml_to_spec_to_json");
        write_to_file(&save_path, &api_filename, &parsed_spec_json_str);

        // Return the JSON filename and the two JSON strings
        (api_filename, parsed_spec_json_str, spec_json_str)
    }

    // Just tests if the deserialization does not blow up. But does not test correctness
    #[test]
    fn can_deserialize() {
        for entry in fs::read_dir("data/v2").unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            // cargo test -- --nocapture to see this message
            println!("Testing if {:?} is deserializable", path);
            from_path(path).unwrap();
        }
    }

    #[test]
    fn can_deserialize_and_reserialize_v2() {
        let save_path_base: std::path::PathBuf = [
            "target",
            "tests",
            "can_deserialize_and_reserialize_v2",
        ].iter()
            .collect();
        let mut invalid_diffs = Vec::new();

        for entry in fs::read_dir("data/v2").unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();

            println!("Testing if {:?} is deserializable", path);

            let (api_filename, parsed_spec_json_str, spec_json_str) =
                compare_spec_through_json(&path, &save_path_base);

            if parsed_spec_json_str != spec_json_str {
                invalid_diffs.push((api_filename, parsed_spec_json_str, spec_json_str));
            }
        }

        for invalid_diff in &invalid_diffs {
            println!("File {} failed JSON comparison!", invalid_diff.0);
        }
        assert!(invalid_diffs.len() == 0);
    }

    #[test]
    fn can_deserialize_and_reserialize_v3() {
        let save_path_base: std::path::PathBuf = [
            "target",
            "tests",
            "can_deserialize_and_reserialize_v3",
        ].iter()
            .collect();
        let mut invalid_diffs = Vec::new();

        for entry in fs::read_dir("data/v3.0").unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();

            println!("Testing if {:?} is deserializable", path);

            let (api_filename, parsed_spec_json_str, spec_json_str) =
                compare_spec_through_json(&path, &save_path_base);

            if parsed_spec_json_str != spec_json_str {
                invalid_diffs.push((api_filename, parsed_spec_json_str, spec_json_str));
            }
        }

        for invalid_diff in &invalid_diffs {
            println!("File {} failed JSON comparison!", invalid_diff.0);
        }
        assert!(invalid_diffs.len() == 0);
    }
}
<|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/. */

extern crate gfx_traits;
extern crate ipc_channel;
#[macro_use]
extern crate log;
extern crate malloc_size_of;
#[macro_use]
extern crate malloc_size_of_derive;
extern crate msg;
extern crate profile_traits;
extern crate script_traits;
extern crate servo_config;
extern crate servo_url;
extern crate time;

use gfx_traits::{Epoch, DisplayList};
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use profile_traits::time::{ProfilerChan, ProfilerCategory, send_profile_data};
use profile_traits::time::TimerMetadata;
use script_traits::{ConstellationControlMsg, LayoutMsg, ProgressiveWebMetricType};
use servo_config::opts;
use servo_url::ServoUrl;
use std::cell::{Cell, RefCell};
use std::cmp::Ordering;
use std::collections::HashMap;
use time::precise_time_ns;

pub trait ProfilerMetadataFactory {
    fn new_metadata(&self) -> Option<TimerMetadata>;
}

pub trait ProgressiveWebMetric {
    fn get_navigation_start(&self) -> Option<u64>;
    fn set_navigation_start(&mut self, time: u64);
    fn get_time_profiler_chan(&self) -> &ProfilerChan;
    fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64);
    fn get_url(&self) -> &ServoUrl;
}

/// TODO make this configurable
/// maximum task time is 50ms (in ns)
pub const MAX_TASK_NS: u64 = 50000000;
/// 10 second window (in ns)
const INTERACTIVE_WINDOW_SECONDS_IN_NS: u64 = 10000000000;

pub trait ToMs<T> {
    fn to_ms(&self) -> T;
}

impl ToMs<f64> for u64 {
    fn to_ms(&self) -> f64 {
        *self as f64 / 1000000.
    }
}

fn set_metric<U: ProgressiveWebMetric>(
    pwm: &U,
    metadata: Option<TimerMetadata>,
    metric_type: ProgressiveWebMetricType,
    category: ProfilerCategory,
    attr: &Cell<Option<u64>>,
    metric_time: Option<u64>,
    url: &ServoUrl)
{
    let navigation_start = match pwm.get_navigation_start() {
        Some(time) => time,
        None => {
            warn!("Trying to set metric before navigation start");
            return;
        }
    };
    let now = match metric_time {
        Some(time) => time,
        None => precise_time_ns(),
    };
    let time = now - navigation_start;
    attr.set(Some(time));

    // Queue performance observer notification.
    pwm.send_queued_constellation_msg(metric_type, time);

    // Send the metric to the time profiler.
    send_profile_data(
        category,
        metadata,
        &pwm.get_time_profiler_chan(),
        time,
        time,
        0,
        0,
    );

    // Print the metric to console if the print-pwm option was given.
    if opts::get().print_pwm {
        println!("Navigation start: {}", pwm.get_navigation_start().unwrap().to_ms());
        println!("{:?} {:?} {:?}", url, metric_type, time.to_ms());
    }

}

// spec: https://github.com/WICG/time-to-interactive
// https://github.com/GoogleChrome/lighthouse/issues/27
// we can look at three different metrics here:
// navigation start -> visually ready (dom content loaded)
// navigation start -> thread ready (main thread available)
// visually ready -> thread ready
#[derive(MallocSizeOf)]
pub struct InteractiveMetrics {
    /// when we navigated to the page
    navigation_start: Option<u64>,
    /// indicates if the page is visually ready
    dom_content_loaded: Cell<Option<u64>>,
    /// main thread is available -- there's been a 10s window with no tasks longer than 50ms
    main_thread_available: Cell<Option<u64>>,
    // max(main_thread_available, dom_content_loaded)
    time_to_interactive: Cell<Option<u64>>,
    #[ignore_malloc_size_of = "can't measure channels"]
    time_profiler_chan: ProfilerChan,
    url: ServoUrl
}

#[derive(Clone, Copy, Debug, MallocSizeOf)]
pub struct InteractiveWindow {
    start: u64,
}

impl InteractiveWindow {
    pub fn new() -> InteractiveWindow {
        InteractiveWindow {
            start: precise_time_ns(),
        }
    }

    // We need to either start or restart the 10s window
    //   start: we've added a new document
    //   restart: there was a task > 50ms
    //   not all documents are interactive
    pub fn start_window(&mut self) {
        self.start = precise_time_ns();
    }

    /// check if 10s has elapsed since start
    pub fn needs_check(&self) -> bool {
        precise_time_ns() - self.start >= INTERACTIVE_WINDOW_SECONDS_IN_NS
    }

    pub fn get_start(&self) -> u64 {
        self.start
    }
}

#[derive(Debug)]
pub enum InteractiveFlag {
    DOMContentLoaded,
    TimeToInteractive(u64),
}

impl InteractiveMetrics {
    pub fn new(time_profiler_chan: ProfilerChan, url: ServoUrl) -> InteractiveMetrics {
        InteractiveMetrics {
            navigation_start: None,
            dom_content_loaded: Cell::new(None),
            main_thread_available: Cell::new(None),
            time_to_interactive: Cell::new(None),
            time_profiler_chan: time_profiler_chan,
            url,
        }
    }

    pub fn set_dom_content_loaded(&self) {
        if self.dom_content_loaded.get().is_none() {
            self.dom_content_loaded.set(Some(precise_time_ns()));
        }
    }

    pub fn set_main_thread_available(&self, time: u64) {
        if self.main_thread_available.get().is_none() {
            self.main_thread_available.set(Some(time));
        }
    }

    pub fn get_dom_content_loaded(&self) -> Option<u64> {
        self.dom_content_loaded.get()
    }

    pub fn get_main_thread_available(&self) -> Option<u64> {
        self.main_thread_available.get()
    }

    // can set either dlc or tti first, but both must be set to actually calc metric
    // when the second is set, set_tti is called with appropriate time
    pub fn maybe_set_tti<T>(
        &self,
        profiler_metadata_factory: &T,
        metric: InteractiveFlag,
    ) where
        T: ProfilerMetadataFactory,
    {
        if self.get_tti().is_some() {
            return;
        }
        match metric {
            InteractiveFlag::DOMContentLoaded => self.set_dom_content_loaded(),
            InteractiveFlag::TimeToInteractive(time) => self.set_main_thread_available(time),
        }

        let dcl = self.dom_content_loaded.get();
        let mta = self.main_thread_available.get();
        let (dcl, mta) = match (dcl, mta) {
        (Some(dcl), Some(mta)) => (dcl, mta),
            _ => return,
        };
        let metric_time = match dcl.partial_cmp(&mta) {
            Some(Ordering::Less) => mta,
            Some(_) => dcl,
            None => panic!("no ordering possible. something bad happened"),
        };
        set_metric(
            self,
            profiler_metadata_factory.new_metadata(),
            ProgressiveWebMetricType::TimeToInteractive,
            ProfilerCategory::TimeToInteractive,
            &self.time_to_interactive,
            Some(metric_time),
            &self.url,
        );
    }

    pub fn get_tti(&self) -> Option<u64> {
        self.time_to_interactive.get()
    }

    pub fn needs_tti(&self) -> bool {
        self.get_tti().is_none()
    }
}

impl ProgressiveWebMetric for InteractiveMetrics {
    fn get_navigation_start(&self) -> Option<u64> {
        self.navigation_start
    }

    fn set_navigation_start(&mut self, time: u64) {
        self.navigation_start = Some(time);
    }

    fn send_queued_constellation_msg(&self, _name: ProgressiveWebMetricType, _time: u64) { }

    fn get_time_profiler_chan(&self) -> &ProfilerChan {
        &self.time_profiler_chan
    }

    fn get_url(&self) -> &ServoUrl {
        &self.url
    }
}

// https://w3c.github.io/paint-timing/
pub struct PaintTimeMetrics {
    pending_metrics: RefCell<HashMap<Epoch, (Option<TimerMetadata>, bool)>>,
    navigation_start: Option<u64>,
    first_paint: Cell<Option<u64>>,
    first_contentful_paint: Cell<Option<u64>>,
    pipeline_id: PipelineId,
    time_profiler_chan: ProfilerChan,
    constellation_chan: IpcSender<LayoutMsg>,
    script_chan: IpcSender<ConstellationControlMsg>,
    url: ServoUrl,
}

impl PaintTimeMetrics {
    pub fn new(
        pipeline_id: PipelineId,
        time_profiler_chan: ProfilerChan,
        constellation_chan: IpcSender<LayoutMsg>,
        script_chan: IpcSender<ConstellationControlMsg>,
        url: ServoUrl)
            -> PaintTimeMetrics {
        PaintTimeMetrics {
            pending_metrics: RefCell::new(HashMap::new()),
            navigation_start: None,
            first_paint: Cell::new(None),
            first_contentful_paint: Cell::new(None),
            pipeline_id,
            time_profiler_chan,
            constellation_chan,
            script_chan,
            url,
        }
    }

    pub fn maybe_set_first_paint<T>(&self, profiler_metadata_factory: &T)
    where
        T: ProfilerMetadataFactory,
    {
        if self.first_paint.get().is_some() {
            return;
        }

        set_metric(
            self,
            profiler_metadata_factory.new_metadata(),
            ProgressiveWebMetricType::FirstPaint,
            ProfilerCategory::TimeToFirstPaint,
            &self.first_paint,
            None,
            &self.url,
        );
    }

    pub fn maybe_observe_paint_time<T>(
        &self,
        profiler_metadata_factory: &T,
        epoch: Epoch,
        display_list: &DisplayList)
    where T: ProfilerMetadataFactory {
        if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() {
            // If we already set all paint metrics, we just bail out.
            return;
        }

        self.pending_metrics.borrow_mut().insert(epoch, (
            profiler_metadata_factory.new_metadata(),
            display_list.is_contentful(),
        ));

        // Send the pending metric information to the compositor thread.
        // The compositor will record the current time after painting the
        // frame with the given ID and will send the metric back to us.
        let msg = LayoutMsg::PendingPaintMetric(self.pipeline_id, epoch);
        if let Err(e) = self.constellation_chan.send(msg) {
            warn!("Failed to send PendingPaintMetric {:?}", e);
        }
    }

    pub fn maybe_set_metric(&self, epoch: Epoch, paint_time: u64) {
        if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() ||
            self.navigation_start.is_none() {
            // If we already set all paint metrics or we have not set navigation start yet,
            // we just bail out.
            return;
        }

        if let Some(pending_metric) = self.pending_metrics.borrow_mut().remove(&epoch) {
            let profiler_metadata = pending_metric.0;
            set_metric(
                self,
                profiler_metadata.clone(),
                ProgressiveWebMetricType::FirstPaint,
                ProfilerCategory::TimeToFirstPaint,
                &self.first_paint,
                Some(paint_time),
                &self.url,
            );

            if pending_metric.1 {
                set_metric(
                    self,
                    profiler_metadata,
                    ProgressiveWebMetricType::FirstContentfulPaint,
                    ProfilerCategory::TimeToFirstContentfulPaint,
                    &self.first_contentful_paint,
                    Some(paint_time),
                    &self.url,
                );
            }
        }
    }

    pub fn get_first_paint(&self) -> Option<u64> {
        self.first_paint.get()
    }

    pub fn get_first_contentful_paint(&self) -> Option<u64> {
        self.first_contentful_paint.get()
    }
}

impl ProgressiveWebMetric for PaintTimeMetrics {
    fn get_navigation_start(&self) -> Option<u64> {
        self.navigation_start
    }

    fn set_navigation_start(&mut self, time: u64) {
        self.navigation_start = Some(time);
    }

    fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64) {
        let msg = ConstellationControlMsg::PaintMetric(self.pipeline_id, name, time);
        if let Err(e) = self.script_chan.send(msg) {
            warn!("Sending metric to script thread failed ({}).", e);
        }
    }

    fn get_time_profiler_chan(&self) -> &ProfilerChan {
        &self.time_profiler_chan
    }

    fn get_url(&self) -> &ServoUrl {
        &self.url
    }
}
<commit_msg>Format metrics #21373<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/. */

extern crate gfx_traits;
extern crate ipc_channel;
#[macro_use]
extern crate log;
extern crate malloc_size_of;
#[macro_use]
extern crate malloc_size_of_derive;
extern crate msg;
extern crate profile_traits;
extern crate script_traits;
extern crate servo_config;
extern crate servo_url;
extern crate time;

use gfx_traits::{Epoch, DisplayList};
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use profile_traits::time::{ProfilerChan, ProfilerCategory, send_profile_data};
use profile_traits::time::TimerMetadata;
use script_traits::{ConstellationControlMsg, LayoutMsg, ProgressiveWebMetricType};
use servo_config::opts;
use servo_url::ServoUrl;
use std::cell::{Cell, RefCell};
use std::cmp::Ordering;
use std::collections::HashMap;
use time::precise_time_ns;

pub trait ProfilerMetadataFactory {
    fn new_metadata(&self) -> Option<TimerMetadata>;
}

pub trait ProgressiveWebMetric {
    fn get_navigation_start(&self) -> Option<u64>;
    fn set_navigation_start(&mut self, time: u64);
    fn get_time_profiler_chan(&self) -> &ProfilerChan;
    fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64);
    fn get_url(&self) -> &ServoUrl;
}

/// TODO make this configurable
/// maximum task time is 50ms (in ns)
pub const MAX_TASK_NS: u64 = 50000000;
/// 10 second window (in ns)
const INTERACTIVE_WINDOW_SECONDS_IN_NS: u64 = 10000000000;

pub trait ToMs<T> {
    fn to_ms(&self) -> T;
}

impl ToMs<f64> for u64 {
    fn to_ms(&self) -> f64 {
        *self as f64 / 1000000.
    }
}

fn set_metric<U: ProgressiveWebMetric>(
    pwm: &U,
    metadata: Option<TimerMetadata>,
    metric_type: ProgressiveWebMetricType,
    category: ProfilerCategory,
    attr: &Cell<Option<u64>>,
    metric_time: Option<u64>,
    url: &ServoUrl,
) {
    let navigation_start = match pwm.get_navigation_start() {
        Some(time) => time,
        None => {
            warn!("Trying to set metric before navigation start");
            return;
        },
    };
    let now = match metric_time {
        Some(time) => time,
        None => precise_time_ns(),
    };
    let time = now - navigation_start;
    attr.set(Some(time));

    // Queue performance observer notification.
    pwm.send_queued_constellation_msg(metric_type, time);

    // Send the metric to the time profiler.
    send_profile_data(
        category,
        metadata,
        &pwm.get_time_profiler_chan(),
        time,
        time,
        0,
        0,
    );

    // Print the metric to console if the print-pwm option was given.
    if opts::get().print_pwm {
        println!(
            "Navigation start: {}",
            pwm.get_navigation_start().unwrap().to_ms()
        );
        println!("{:?} {:?} {:?}", url, metric_type, time.to_ms());
    }
}

// spec: https://github.com/WICG/time-to-interactive
// https://github.com/GoogleChrome/lighthouse/issues/27
// we can look at three different metrics here:
// navigation start -> visually ready (dom content loaded)
// navigation start -> thread ready (main thread available)
// visually ready -> thread ready
#[derive(MallocSizeOf)]
pub struct InteractiveMetrics {
    /// when we navigated to the page
    navigation_start: Option<u64>,
    /// indicates if the page is visually ready
    dom_content_loaded: Cell<Option<u64>>,
    /// main thread is available -- there's been a 10s window with no tasks longer than 50ms
    main_thread_available: Cell<Option<u64>>,
    // max(main_thread_available, dom_content_loaded)
    time_to_interactive: Cell<Option<u64>>,
    #[ignore_malloc_size_of = "can't measure channels"]
    time_profiler_chan: ProfilerChan,
    url: ServoUrl,
}

#[derive(Clone, Copy, Debug, MallocSizeOf)]
pub struct InteractiveWindow {
    start: u64,
}

impl InteractiveWindow {
    pub fn new() -> InteractiveWindow {
        InteractiveWindow {
            start: precise_time_ns(),
        }
    }

    // We need to either start or restart the 10s window
    //   start: we've added a new document
    //   restart: there was a task > 50ms
    //   not all documents are interactive
    pub fn start_window(&mut self) {
        self.start = precise_time_ns();
    }

    /// check if 10s has elapsed since start
    pub fn needs_check(&self) -> bool {
        precise_time_ns() - self.start >= INTERACTIVE_WINDOW_SECONDS_IN_NS
    }

    pub fn get_start(&self) -> u64 {
        self.start
    }
}

#[derive(Debug)]
pub enum InteractiveFlag {
    DOMContentLoaded,
    TimeToInteractive(u64),
}

impl InteractiveMetrics {
    pub fn new(time_profiler_chan: ProfilerChan, url: ServoUrl) -> InteractiveMetrics {
        InteractiveMetrics {
            navigation_start: None,
            dom_content_loaded: Cell::new(None),
            main_thread_available: Cell::new(None),
            time_to_interactive: Cell::new(None),
            time_profiler_chan: time_profiler_chan,
            url,
        }
    }

    pub fn set_dom_content_loaded(&self) {
        if self.dom_content_loaded.get().is_none() {
            self.dom_content_loaded.set(Some(precise_time_ns()));
        }
    }

    pub fn set_main_thread_available(&self, time: u64) {
        if self.main_thread_available.get().is_none() {
            self.main_thread_available.set(Some(time));
        }
    }

    pub fn get_dom_content_loaded(&self) -> Option<u64> {
        self.dom_content_loaded.get()
    }

    pub fn get_main_thread_available(&self) -> Option<u64> {
        self.main_thread_available.get()
    }

    // can set either dlc or tti first, but both must be set to actually calc metric
    // when the second is set, set_tti is called with appropriate time
    pub fn maybe_set_tti<T>(&self, profiler_metadata_factory: &T, metric: InteractiveFlag)
    where
        T: ProfilerMetadataFactory,
    {
        if self.get_tti().is_some() {
            return;
        }
        match metric {
            InteractiveFlag::DOMContentLoaded => self.set_dom_content_loaded(),
            InteractiveFlag::TimeToInteractive(time) => self.set_main_thread_available(time),
        }

        let dcl = self.dom_content_loaded.get();
        let mta = self.main_thread_available.get();
        let (dcl, mta) = match (dcl, mta) {
            (Some(dcl), Some(mta)) => (dcl, mta),
            _ => return,
        };
        let metric_time = match dcl.partial_cmp(&mta) {
            Some(Ordering::Less) => mta,
            Some(_) => dcl,
            None => panic!("no ordering possible. something bad happened"),
        };
        set_metric(
            self,
            profiler_metadata_factory.new_metadata(),
            ProgressiveWebMetricType::TimeToInteractive,
            ProfilerCategory::TimeToInteractive,
            &self.time_to_interactive,
            Some(metric_time),
            &self.url,
        );
    }

    pub fn get_tti(&self) -> Option<u64> {
        self.time_to_interactive.get()
    }

    pub fn needs_tti(&self) -> bool {
        self.get_tti().is_none()
    }
}

impl ProgressiveWebMetric for InteractiveMetrics {
    fn get_navigation_start(&self) -> Option<u64> {
        self.navigation_start
    }

    fn set_navigation_start(&mut self, time: u64) {
        self.navigation_start = Some(time);
    }

    fn send_queued_constellation_msg(&self, _name: ProgressiveWebMetricType, _time: u64) {}

    fn get_time_profiler_chan(&self) -> &ProfilerChan {
        &self.time_profiler_chan
    }

    fn get_url(&self) -> &ServoUrl {
        &self.url
    }
}

// https://w3c.github.io/paint-timing/
pub struct PaintTimeMetrics {
    pending_metrics: RefCell<HashMap<Epoch, (Option<TimerMetadata>, bool)>>,
    navigation_start: Option<u64>,
    first_paint: Cell<Option<u64>>,
    first_contentful_paint: Cell<Option<u64>>,
    pipeline_id: PipelineId,
    time_profiler_chan: ProfilerChan,
    constellation_chan: IpcSender<LayoutMsg>,
    script_chan: IpcSender<ConstellationControlMsg>,
    url: ServoUrl,
}

impl PaintTimeMetrics {
    pub fn new(
        pipeline_id: PipelineId,
        time_profiler_chan: ProfilerChan,
        constellation_chan: IpcSender<LayoutMsg>,
        script_chan: IpcSender<ConstellationControlMsg>,
        url: ServoUrl,
    ) -> PaintTimeMetrics {
        PaintTimeMetrics {
            pending_metrics: RefCell::new(HashMap::new()),
            navigation_start: None,
            first_paint: Cell::new(None),
            first_contentful_paint: Cell::new(None),
            pipeline_id,
            time_profiler_chan,
            constellation_chan,
            script_chan,
            url,
        }
    }

    pub fn maybe_set_first_paint<T>(&self, profiler_metadata_factory: &T)
    where
        T: ProfilerMetadataFactory,
    {
        if self.first_paint.get().is_some() {
            return;
        }

        set_metric(
            self,
            profiler_metadata_factory.new_metadata(),
            ProgressiveWebMetricType::FirstPaint,
            ProfilerCategory::TimeToFirstPaint,
            &self.first_paint,
            None,
            &self.url,
        );
    }

    pub fn maybe_observe_paint_time<T>(
        &self,
        profiler_metadata_factory: &T,
        epoch: Epoch,
        display_list: &DisplayList,
    ) where
        T: ProfilerMetadataFactory,
    {
        if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() {
            // If we already set all paint metrics, we just bail out.
            return;
        }

        self.pending_metrics.borrow_mut().insert(
            epoch,
            (
                profiler_metadata_factory.new_metadata(),
                display_list.is_contentful(),
            ),
        );

        // Send the pending metric information to the compositor thread.
        // The compositor will record the current time after painting the
        // frame with the given ID and will send the metric back to us.
        let msg = LayoutMsg::PendingPaintMetric(self.pipeline_id, epoch);
        if let Err(e) = self.constellation_chan.send(msg) {
            warn!("Failed to send PendingPaintMetric {:?}", e);
        }
    }

    pub fn maybe_set_metric(&self, epoch: Epoch, paint_time: u64) {
        if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() ||
            self.navigation_start.is_none()
        {
            // If we already set all paint metrics or we have not set navigation start yet,
            // we just bail out.
            return;
        }

        if let Some(pending_metric) = self.pending_metrics.borrow_mut().remove(&epoch) {
            let profiler_metadata = pending_metric.0;
            set_metric(
                self,
                profiler_metadata.clone(),
                ProgressiveWebMetricType::FirstPaint,
                ProfilerCategory::TimeToFirstPaint,
                &self.first_paint,
                Some(paint_time),
                &self.url,
            );

            if pending_metric.1 {
                set_metric(
                    self,
                    profiler_metadata,
                    ProgressiveWebMetricType::FirstContentfulPaint,
                    ProfilerCategory::TimeToFirstContentfulPaint,
                    &self.first_contentful_paint,
                    Some(paint_time),
                    &self.url,
                );
            }
        }
    }

    pub fn get_first_paint(&self) -> Option<u64> {
        self.first_paint.get()
    }

    pub fn get_first_contentful_paint(&self) -> Option<u64> {
        self.first_contentful_paint.get()
    }
}

impl ProgressiveWebMetric for PaintTimeMetrics {
    fn get_navigation_start(&self) -> Option<u64> {
        self.navigation_start
    }

    fn set_navigation_start(&mut self, time: u64) {
        self.navigation_start = Some(time);
    }

    fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64) {
        let msg = ConstellationControlMsg::PaintMetric(self.pipeline_id, name, time);
        if let Err(e) = self.script_chan.send(msg) {
            warn!("Sending metric to script thread failed ({}).", e);
        }
    }

    fn get_time_profiler_chan(&self) -> &ProfilerChan {
        &self.time_profiler_chan
    }

    fn get_url(&self) -> &ServoUrl {
        &self.url
    }
}
<|endoftext|>


<commit_before>#![no_std]

#[doc(hidden)]
pub extern crate core as _core;

/// Asserts at compile-time that the types have equal sizes.
///
/// # Example
///
/// ```
/// #[macro_use]
/// extern crate static_assertions;
///
/// struct Byte(u8);
///
/// fn main() {
///     assert_eq_size!(Byte, u8);
///     // assert_eq_size!(Byte, u16); // Fails to compile
///
///     // Supports unlimited arguments:
///     assert_eq_size!([Byte; 4], [u16; 2], u32);
/// }
/// ```
#[macro_export]
macro_rules! assert_eq_size {
    ($x:ty, $y:ty) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::mem::{forget, transmute, uninitialized};
            forget::<$y>(transmute(uninitialized::<$x>()));
        }
    };
    ($x:ty, $y:ty, $($rest:ty),+) => {
        assert_eq_size!($x, $y);
        assert_eq_size!($y, $($rest),+);
    };
}

/// Asserts at compile-time that the values have equal sizes.
#[macro_export]
macro_rules! assert_eq_size_val {
    ($x:expr, $y:expr) => {
        #[allow(unused_unsafe)]
        match (&$x, &$y) {
            (x, y) => unsafe {
                use $crate::_core::{mem, ptr};
                let mut copy = ptr::read(x);
                ptr::write(&mut copy, mem::transmute(ptr::read(y)));
                mem::forget(copy);
            }
        }
    };
    ($x:expr, $y:expr, $($rest:expr),+) => {
        assert_eq_size_val!($x, $y);
        assert_eq_size_val!($y, $($rest),+);
    };
}
<commit_msg>Make assert_eq_size_val iterative<commit_after>#![no_std]

#[doc(hidden)]
pub extern crate core as _core;

/// Asserts at compile-time that the types have equal sizes.
///
/// # Example
///
/// ```
/// #[macro_use]
/// extern crate static_assertions;
///
/// struct Byte(u8);
///
/// fn main() {
///     assert_eq_size!(Byte, u8);
///     // assert_eq_size!(Byte, u16); // Fails to compile
///
///     // Supports unlimited arguments:
///     assert_eq_size!([Byte; 4], [u16; 2], u32);
/// }
/// ```
#[macro_export]
macro_rules! assert_eq_size {
    ($x:ty, $y:ty) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::mem::{forget, transmute, uninitialized};
            forget::<$y>(transmute(uninitialized::<$x>()));
        }
    };
    ($x:ty, $y:ty, $($rest:ty),+) => {
        assert_eq_size!($x, $y);
        assert_eq_size!($y, $($rest),+);
    };
}

/// Asserts at compile-time that the values have equal sizes.
#[macro_export]
macro_rules! assert_eq_size_val {
    ($x:expr, $($xs:expr),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            use $crate::_core::{mem, ptr};
            let mut copy = ptr::read(&$x);
            $(ptr::write(&mut copy, mem::transmute(ptr::read(&$xs)));)+
            mem::forget(copy);
        }
    }
}
<|endoftext|>


<commit_before>
pub fn edit_distance(a: &str, b: &str) -> i32 {

    let row: Vec<i32> = vec![0; b.len() + 1];
    let mut matrix: Vec<Vec<i32>> = vec![row; a.len() + 1];

    // initialize string a
    for i in (0..a.len()) {
        matrix[i+1][0] = matrix[i][0] + 1;
    }

    // initialize string b
    for i in (0..b.len()) {
        matrix[0][i+1] = matrix[0][i] + 1;
    }

    // calculate matrix
    for (i, ca) in a.char_indices() {
        for (j, cb) in b.char_indices() {

            let mut alternatives = [
                // deletion
                matrix[i][j+1] + 1,
                // insertion
                matrix[i+1][j] + 1,
                // match or substituion
                matrix[i][j] + if ca == cb { 0 } else { 1 }];
            alternatives.sort();
            matrix[i+1][j+1] = *alternatives.first().unwrap();
        }
    }

    return matrix[a.len()][b.len()];
}

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

    #[test]
    fn simple() {
        assert_eq!(edit_distance("kitten", "sitting"), 3);
        assert_eq!(edit_distance("Tier", "Tor"), 2);
    }
}
<commit_msg>Use last expression instead of return<commit_after>
pub fn edit_distance(a: &str, b: &str) -> i32 {

    let row: Vec<i32> = vec![0; b.len() + 1];
    let mut matrix: Vec<Vec<i32>> = vec![row; a.len() + 1];

    // initialize string a
    for i in (0..a.len()) {
        matrix[i+1][0] = matrix[i][0] + 1;
    }

    // initialize string b
    for i in (0..b.len()) {
        matrix[0][i+1] = matrix[0][i] + 1;
    }

    // calculate matrix
    for (i, ca) in a.char_indices() {
        for (j, cb) in b.char_indices() {

            let mut alternatives = [
                // deletion
                matrix[i][j+1] + 1,
                // insertion
                matrix[i+1][j] + 1,
                // match or substituion
                matrix[i][j] + if ca == cb { 0 } else { 1 }];
            alternatives.sort();
            matrix[i+1][j+1] = *alternatives.first().unwrap();
        }
    }

    matrix[a.len()][b.len()]
}

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

    #[test]
    fn simple() {
        assert_eq!(edit_distance("kitten", "sitting"), 3);
        assert_eq!(edit_distance("Tier", "Tor"), 2);
    }
}
<|endoftext|>


<commit_before>//! A crate for My Number functions.
//!
//! # Example
//!
//! ```
//! extern crate mynumber;
//!
//! match mynumber::verify("123456789018") {
//!     Ok(()) => println!("valid"),
//!     Err(e) => println!("invalid: {:?}", e),
//! }
//! ```

#[derive(Debug)]
pub enum VerifyError {
    InvalidNumberLength,
    InvalidType,
    InvalidCheckDigit
}

pub mod individual;
pub mod corporate;

pub fn verify(mynumber: &str) -> Result<(), VerifyError> {
    individual::verify(mynumber)
}
<commit_msg>Remove unused error<commit_after>//! A crate for My Number functions.
//!
//! # Example
//!
//! ```
//! extern crate mynumber;
//!
//! match mynumber::verify("123456789018") {
//!     Ok(()) => println!("valid"),
//!     Err(e) => println!("invalid: {:?}", e),
//! }
//! ```

#[derive(Debug)]
pub enum VerifyError {
    InvalidNumberLength,
    InvalidCheckDigit
}

pub mod individual;
pub mod corporate;

pub fn verify(mynumber: &str) -> Result<(), VerifyError> {
    individual::verify(mynumber)
}
<|endoftext|>


<commit_before>#[macro_use] extern crate nom;

use std::str::FromStr;

use self::nom::{is_digit, space, newline};

#[cfg(test)]
mod tests {
  use nom;

  #[test]
  fn it_parses_a_u32() {
    let input = b"12345";
    let result = super::take_u32(input);
    match result {
      nom::IResult::Done(_, f) => assert_eq!(f, 12345u32),
      _ => unreachable!(),
    }

  }

  #[test]
  fn it_parses_extent_allocation() {
    let example_output = b"extent_alloc 4260849 125170297 4618726 131131897";
    match super::extent_alloc(example_output) {
      nom::IResult::Done(_, result) => {
        assert_eq!(result.allocated_extents, 4260849);
        assert_eq!(result.freed_blocks, 131131897);
      },
      _ => unreachable!(),
    }
  }

  #[test]
  fn it_parses_allocation_btree() {
    let example_output = b"abt 29491162 337391304 11257328 11133039";
    match super::abt(example_output) {
      nom::IResult::Done(_, result) => {
        assert_eq!(result.inserts, 11257328);
      },
      _ => unreachable!(),
    }
  }

  #[test]
  fn it_parses_example() {
    let example_output = b"extent_alloc 4260849 125170297 4618726 131131897
abt 29491162 337391304 11257328 11133039
blk_map 381213360 115456141 10903633 69612322 7448401 507596777 0
bmbt 771328 6236258 602114 86646
dir 21253907 6921870 6969079 779205554
trans 126946406 38184616 6342392
ig 17754368 2019571 102 15734797 0 15672217 3962470
log 129491915 3992515264 458018 153771989 127040250
push_ail 171473415 0 6896837 3324292 8069877 65884 1289485 0 22535 7337
xstrat 4140059 0
rw 1595677950 1046884251
attr 194724197 0 7 0
icluster 20772185 2488203 13909520
vnodes 62578 15959666 0 0 15897088 15897088 15897088 0
buf 2090581631 1972536890 118044776 225145 9486625 0 0 2000152616 809762
xpc 6908312903680 67735504884757 19760115252482
debug 0";
    let result = super::parse(example_output).unwrap();

    assert_eq!(result.extent_allocation.freed_extents, 4618726);
    assert_eq!(result.allocation_btree.lookups, 29491162);
  }
}

pub struct XfsStat {
  extent_allocation: ExtentAllocation,
  allocation_btree: AllocationBTree
}

pub struct ExtentAllocation {
  pub allocated_extents: u32,
  pub allocated_blocks: u32,
  pub freed_extents: u32,
  pub freed_blocks: u32,
}

pub struct AllocationBTree {
  pub lookups: u32,
  pub compares: u32,
  pub inserts: u32,
  pub deletes: u32,
}

pub fn parse(input: &[u8]) -> Option<XfsStat> {
  match xfs_stat(input) {
    nom::IResult::Done(_, stat) =>{
      Some(stat)
    },
    _ => None
  }
}

named!(xfs_stat <XfsStat>,
  chain!(
    extent_alloc: extent_alloc ~
    newline ~
    abt: abt,
    || {
      XfsStat {
        extent_allocation: extent_alloc,
        allocation_btree: abt,
      }
    }
  )
);

named!(take_u32 <u32>,
  chain!(
    uint_slice: take_while!(is_digit),
    || {
      let int_str = String::from_utf8_lossy(uint_slice);
      u32::from_str(&int_str[..]).unwrap()
    }
  )
);

named!(extent_alloc <ExtentAllocation>,
  chain!(
    tag!("extent_alloc") ~
    space ~
    allocx: take_u32 ~
    space ~
    allocb: take_u32 ~
    space ~
    freex: take_u32 ~
    space ~
    freeb: take_u32,
    || {
      ExtentAllocation {
        allocated_extents: allocx,
        allocated_blocks: allocb,
        freed_extents: freex,
        freed_blocks: freeb,
      }
    }
  )
);

named!(abt <AllocationBTree>,
  chain!(
    tag!("abt") ~
    space ~
    lookups: take_u32 ~
    space ~
    compares: take_u32 ~
    space ~
    inserts: take_u32 ~
    space ~
    deletes: take_u32,
    || {
      AllocationBTree {
        lookups: lookups,
        compares: compares,
        inserts: inserts,
        deletes: deletes,
      }
    }
  )
);<commit_msg>more fields<commit_after>#[macro_use] extern crate nom;

use std::str::FromStr;

use self::nom::{is_digit, space, newline};

#[cfg(test)]
mod tests {
  use nom;

  #[test]
  fn it_parses_a_u32() {
    let input = b"12345";
    let result = super::take_u32(input);
    match result {
      nom::IResult::Done(_, f) => assert_eq!(f, 12345u32),
      _ => unreachable!(),
    }

  }

  #[test]
  fn it_parses_extent_allocation() {
    let example_output = b"extent_alloc 4260849 125170297 4618726 131131897";
    match super::extent_alloc(example_output) {
      nom::IResult::Done(_, result) => {
        assert_eq!(result.allocated_extents, 4260849);
        assert_eq!(result.freed_blocks, 131131897);
      },
      _ => unreachable!(),
    }
  }

  #[test]
  fn it_parses_allocation_btree() {
    let example_output = b"abt 29491162 337391304 11257328 11133039";
    match super::abt(example_output) {
      nom::IResult::Done(_, result) => {
        assert_eq!(result.inserts, 11257328);
      },
      _ => unreachable!(),
    }
  }

  #[test]
  fn it_parses_block_mapping() {
    let example_output = b"blk_map 381213360 115456141 10903633 69612322 7448401 507596777 0";
    match super::blk_map(example_output) {
      nom::IResult::Done(_, result) => {
        assert_eq!(result.list_delete, 7448401);
      },
      _ => unreachable!(),
    }
  }

  #[test]
  fn it_parses_block_map_btree() {
    let example_output = b"bmbt 771328 6236258 602114 86646";
    match super::bmbt(example_output) {
      nom::IResult::Done(_, result) => {
        assert_eq!(result.deletes, 86646);
      },
      _ => unreachable!(),
    }
  }

  #[test]
  fn it_parses_example() {
    let example_output = b"extent_alloc 4260849 125170297 4618726 131131897
abt 29491162 337391304 11257328 11133039
blk_map 381213360 115456141 10903633 69612322 7448401 507596777 0
bmbt 771328 6236258 602114 86646
dir 21253907 6921870 6969079 779205554
trans 126946406 38184616 6342392
ig 17754368 2019571 102 15734797 0 15672217 3962470
log 129491915 3992515264 458018 153771989 127040250
push_ail 171473415 0 6896837 3324292 8069877 65884 1289485 0 22535 7337
xstrat 4140059 0
rw 1595677950 1046884251
attr 194724197 0 7 0
icluster 20772185 2488203 13909520
vnodes 62578 15959666 0 0 15897088 15897088 15897088 0
buf 2090581631 1972536890 118044776 225145 9486625 0 0 2000152616 809762
xpc 6908312903680 67735504884757 19760115252482
debug 0";
    let result = super::parse(example_output).unwrap();

    assert_eq!(result.extent_allocation.freed_extents, 4618726);
    assert_eq!(result.allocation_btree.lookups, 29491162);
    assert_eq!(result.block_mapping.unmap, 10903633);
    assert_eq!(result.block_map_btree.inserts, 602114);
  }
}

pub struct XfsStat {
  pub extent_allocation: ExtentAllocation,
  pub allocation_btree: AllocationBTree,
  pub block_mapping: BlockMapping,
  pub block_map_btree: BlockMapBTree
}

pub struct ExtentAllocation {
  // Number of file system extents allocated over all XFS filesystems.
  pub allocated_extents: u32,
  // Number of file system blocks allocated over all XFS filesystems.
  pub allocated_blocks: u32,
  // Number of file system extents freed over all XFS filesystems.
  pub freed_extents: u32,
  // Number of file system blocks freed over all XFS filesystems.
  pub freed_blocks: u32,
}

pub struct AllocationBTree {
  // Number of lookup operations in XFS filesystem allocation btrees.
  pub lookups: u32,
  // Number of compares in XFS filesystem allocation btree lookups.
  pub compares: u32,
  // Number of extent records inserted into XFS filesystem allocation btrees.
  pub inserts: u32,
  // Number of extent records deleted from XFS filesystem allocation btrees.
  pub deletes: u32,
}

pub struct BlockMapping {
  // Number of block map for read operations performed on XFS files.
  pub map_read: u32,
  // Number of block map for write operations performed on XFS files.
  pub map_write: u32,
  // Number of block unmap (delete) operations performed on XFS files.
  pub unmap: u32,
  // Number of extent list insertion operations for XFS files.
  pub list_insert: u32,
  // Number of extent list deletion operations for XFS files.
  pub list_delete: u32,
  // Number of extent list lookup operations for XFS files.
  pub list_lookup: u32,
  // Number of extent list comparisons in XFS extent list lookups.
  pub list_compare: u32,
}

pub struct BlockMapBTree {
  // Number of block map btree lookup operations on XFS files.
  pub lookups: u32,
  // Number of block map btree compare operations in XFS block map lookups.
  pub compares: u32,
  // Number of block map btree records inserted for XFS files.
  pub inserts: u32,
  // Number of block map btree records deleted for XFS files.
  pub deletes: u32,
}

pub fn parse(input: &[u8]) -> Option<XfsStat> {
  match xfs_stat(input) {
    nom::IResult::Done(_, stat) =>{
      Some(stat)
    },
    _ => None
  }
}

named!(xfs_stat <XfsStat>,
  chain!(
    extent_alloc: extent_alloc ~
    newline ~
    abt: abt ~
    newline ~
    blk_map: blk_map ~
    newline ~
    block_map_btree: bmbt,
    || {
      XfsStat {
        extent_allocation: extent_alloc,
        allocation_btree: abt,
        block_mapping: blk_map,
        block_map_btree: block_map_btree,
      }
    }
  )
);

named!(take_u32 <u32>,
  chain!(
    uint_slice: take_while!(is_digit),
    || {
      let int_str = String::from_utf8_lossy(uint_slice);
      u32::from_str(&int_str[..]).unwrap()
    }
  )
);

named!(extent_alloc <ExtentAllocation>,
  chain!(
    tag!("extent_alloc") ~
    space ~
    allocx: take_u32 ~
    space ~
    allocb: take_u32 ~
    space ~
    freex: take_u32 ~
    space ~
    freeb: take_u32,
    || {
      ExtentAllocation {
        allocated_extents: allocx,
        allocated_blocks: allocb,
        freed_extents: freex,
        freed_blocks: freeb,
      }
    }
  )
);

named!(abt <AllocationBTree>,
  chain!(
    tag!("abt") ~
    space ~
    lookups: take_u32 ~
    space ~
    compares: take_u32 ~
    space ~
    inserts: take_u32 ~
    space ~
    deletes: take_u32,
    || {
      AllocationBTree {
        lookups: lookups,
        compares: compares,
        inserts: inserts,
        deletes: deletes,
      }
    }
  )
);

named!(blk_map <BlockMapping>,
  chain!(
    tag!("blk_map") ~
    space ~
    map_read: take_u32 ~
    space ~
    map_write: take_u32 ~
    space ~
    unmap: take_u32 ~
    space ~
    list_insert: take_u32 ~
    space ~
    list_delete: take_u32 ~
    space ~
    list_lookup: take_u32 ~
    space ~
    list_compare: take_u32,
    ||{
      BlockMapping {
        map_read: map_read,
        map_write: map_write,
        unmap: unmap,
        list_insert: list_insert,
        list_delete: list_delete,
        list_lookup: list_lookup,
        list_compare: list_compare,
      }
    }
  )
);

named!(bmbt <BlockMapBTree>,
  chain!(
    tag!("bmbt") ~
    space ~
    lookup: take_u32 ~
    space ~
    compare: take_u32 ~
    space ~
    insrec: take_u32 ~
    space ~
    delrec: take_u32,
    || {
      BlockMapBTree {
        lookups: lookup,
        compares: compare,
        inserts: insrec,
        deletes: delrec,
      }
    }
  )
);<|endoftext|>


<commit_before>extern crate time;

mod task;
mod time_source;
mod scheduler;
mod steady_time_source;
#[cfg(test)]
mod test_helpers;

pub use time::Duration;
pub use steady_time_source::*;
pub use scheduler::*;

<commit_msg>exproting time_source traits<commit_after>extern crate time;

mod task;
mod time_source;
mod scheduler;
mod steady_time_source;
#[cfg(test)]
mod test_helpers;

pub use time::Duration;
pub use time_source::*;
pub use steady_time_source::*;
pub use scheduler::*;

<|endoftext|>


<commit_before>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(all(feature = "unstable", test), feature(specialization))]

//! An implementation of the Rust channel API over process boundaries. Under the
//! hood, this API uses Mach ports on Mac and file descriptor passing over Unix
//! sockets on Linux. The serde library is used to serialize values for transport
//! over the wire.
//!
//! # Features
//! ## `force-inprocess`
//!
//! Force the `inprocess` backend to be used instead of the OS specific backend.
//! The `inprocess` backend is a dummy back-end, that behaves like the real ones,
//! but doesn't actually work between processes.
//!
//! ## `memfd`
//!
//! Use [memfd_create] to back [OsIpcSharedMemory] on Linux. [memfd_create] was
//! introduced in version 3.17. __WARNING:__ Enabling this feature with kernel
//! version less than 3.17 will cause panics on any use of [IpcSharedMemory].
//!
//! ## `unstable`
//!
//! [IpcReceiver]: ipc/struct.IpcReceiver.html
//! [IpcSender]: ipc/struct.IpcSender.html
//! [IpcReceiverSet]: ipc/struct.IpcReceiverSet.html
//! [IpcSharedMemory]: ipc/struct.IpcSharedMemory.html
//! [OsIpcSharedMemory]: platform/struct.OsIpcSharedMemory.html
//! [memfd_create]: http://man7.org/linux/man-pages/man2/memfd_create.2.html

extern crate bincode;
extern crate crossbeam_channel;

#[macro_use]
extern crate lazy_static;
#[cfg(all(
    not(feature = "force-inprocess"),
    any(target_os = "linux", target_os = "openbsd", target_os = "freebsd")
))]
extern crate fnv;
extern crate libc;
#[cfg(all(
    not(feature = "force-inprocess"),
    any(target_os = "linux", target_os = "openbsd", target_os = "freebsd")
))]
extern crate mio;
extern crate rand;
extern crate serde;
extern crate tempfile;
#[cfg(any(
    feature = "force-inprocess",
    target_os = "windows",
    target_os = "android",
    target_os = "ios"
))]
extern crate uuid;
#[cfg(all(
    feature = "memfd",
    not(feature = "force-inprocess"),
    target_os = "linux"
))]
#[macro_use]
extern crate sc;

#[cfg(feature = "async")]
extern crate futures;

#[cfg(all(feature = "async", test))]
extern crate futures_test;

#[cfg(feature = "async")]
pub mod asynch;

pub mod ipc;
pub mod platform;
pub mod router;

#[cfg(test)]
mod test;

pub use bincode::{Error, ErrorKind};
<commit_msg>Auto merge of #269 - RalfJung:RalfJung-patch-1, r=jdm<commit_after>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(all(feature = "unstable", test), feature(specialization))]

//! An implementation of the Rust channel API over process boundaries. Under the
//! hood, this API uses Mach ports on Mac and file descriptor passing over Unix
//! sockets on Linux. The serde library is used to serialize values for transport
//! over the wire.
//!
//! # Features
//! ## `force-inprocess`
//!
//! Force the `inprocess` backend to be used instead of the OS specific backend.
//! The `inprocess` backend is a dummy back-end, that behaves like the real ones,
//! but doesn't actually work between processes.
//!
//! ## `memfd`
//!
//! Use [memfd_create] to back [OsIpcSharedMemory] on Linux. [memfd_create] was
//! introduced in kernel version 3.17. __WARNING:__ Enabling this feature with kernel
//! version less than 3.17 will cause panics on any use of [IpcSharedMemory].
//!
//! ## `unstable`
//!
//! [IpcReceiver]: ipc/struct.IpcReceiver.html
//! [IpcSender]: ipc/struct.IpcSender.html
//! [IpcReceiverSet]: ipc/struct.IpcReceiverSet.html
//! [IpcSharedMemory]: ipc/struct.IpcSharedMemory.html
//! [OsIpcSharedMemory]: platform/struct.OsIpcSharedMemory.html
//! [memfd_create]: http://man7.org/linux/man-pages/man2/memfd_create.2.html

extern crate bincode;
extern crate crossbeam_channel;

#[macro_use]
extern crate lazy_static;
#[cfg(all(
    not(feature = "force-inprocess"),
    any(target_os = "linux", target_os = "openbsd", target_os = "freebsd")
))]
extern crate fnv;
extern crate libc;
#[cfg(all(
    not(feature = "force-inprocess"),
    any(target_os = "linux", target_os = "openbsd", target_os = "freebsd")
))]
extern crate mio;
extern crate rand;
extern crate serde;
extern crate tempfile;
#[cfg(any(
    feature = "force-inprocess",
    target_os = "windows",
    target_os = "android",
    target_os = "ios"
))]
extern crate uuid;
#[cfg(all(
    feature = "memfd",
    not(feature = "force-inprocess"),
    target_os = "linux"
))]
#[macro_use]
extern crate sc;

#[cfg(feature = "async")]
extern crate futures;

#[cfg(all(feature = "async", test))]
extern crate futures_test;

#[cfg(feature = "async")]
pub mod asynch;

pub mod ipc;
pub mod platform;
pub mod router;

#[cfg(test)]
mod test;

pub use bincode::{Error, ErrorKind};
<|endoftext|>


<commit_before>extern crate time;

use std::fmt;
use std::io;

pub enum TestResult {
    Pass,
    Fail,
    Error,
    Skipped
}

impl fmt::Display for TestResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let rep = match *self {
            TestResult::Pass => ".",
            TestResult::Fail => "F",
            TestResult::Error => "E",
            TestResult::Skipped => "S"
        };
        write!(f, "{}", rep)
    }
}

pub trait Runnable {
    fn run(&self) -> TestResult;
}

struct TestTimings {
    time_s: f64,
    runs_per_s: f64
}

impl fmt::Display for TestTimings {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Finished in {}s, {} runs/s.", self.time_s, self.runs_per_s)
    }
}

struct TestStats {
    runs: usize,
    failures: usize,
    errors: usize,
    skips: usize,
}

impl TestStats {
    fn new() -> TestStats {
        TestStats {
            runs: 0,
            failures: 0,
            errors: 0,
            skips: 0
        }
    }

    fn create(result: &TestResult) -> TestStats {
        TestStats {
            runs: 1,
            failures: match *result { TestResult::Fail => 1, _ => 0 },
            errors: match *result { TestResult::Error => 1, _ => 0 },
            skips: match *result { TestResult::Skipped => 1, _ => 0 },
        }
    }

    fn combine(a: TestStats, b: TestStats) -> TestStats {
        TestStats {
            runs: a.runs + b.runs,
            failures: a.failures + b.failures,
            errors: a.errors + b.errors,
            skips: a.skips + b.skips
        }
    }
}

impl fmt::Display for TestStats {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} runs, {} failures, {} errors, {} skips.", self.runs, self.failures, self.errors, self.skips)
    }
}

trait Reporter {
    fn start(&mut self);
    fn record(&mut self, result: &TestResult);
    fn report(&mut self);
}

struct ProgressReporter {
    output: io::Stdout
}

impl ProgressReporter {
    fn new() -> ProgressReporter {
        ProgressReporter {
            output: io::stdout()
        }
    }
}

impl Reporter for ProgressReporter  {
    fn start(&mut self) {}
    fn record(&mut self, result: &TestResult) {
        use std::io::{Write};
        match write!(&mut self.output, "{}", result) {
            Ok(_) => match self.output.flush() {
                Ok(_) => {},
                _ => panic!("Unable to flush test result")
            },
            _ => panic!("Unable to write test result")
        };
    }
    fn report(&mut self) {}
}

pub struct TestRunner;

impl TestRunner {
    pub fn run(&self, tests: &Vec<Box<Runnable>>) {
        println!("# Running.\n");

        let mut reporter = ProgressReporter::new();
        let start_time = time::precise_time_ns();

        let results: Vec<TestResult> = tests
            .iter()
            .map(|test| test.run())
            .map(|result| {
                reporter.record(&result);
                result
            })
            .collect();

        let end_time = time::precise_time_ns();
        let time_ms = end_time.wrapping_sub(start_time) as i64 / 1000000;
        let time_s = time_ms as f64 / 1000f64;
        let timings = TestTimings {
            time_s: time_s,
            runs_per_s: results.len() as f64 / time_s
        };

        println!("\n\n{}", timings);

        let stats = results
            .iter()
            .map(TestStats::create)
            .fold(TestStats::new(), TestStats::combine);

        println!("\n{}", stats);

        if stats.skips > 0 {
            println!("\nYou have skipped tests. Run with --verbose for details.");
        }
    }
}
<commit_msg>Create a statistics reporter.<commit_after>extern crate time;

use std::fmt;
use std::io;

#[derive(Clone)]
pub enum TestResult {
    Pass,
    Fail,
    Error,
    Skipped
}

impl fmt::Display for TestResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let rep = match *self {
            TestResult::Pass => ".",
            TestResult::Fail => "F",
            TestResult::Error => "E",
            TestResult::Skipped => "S"
        };
        write!(f, "{}", rep)
    }
}

pub trait Runnable {
    fn run(&self) -> TestResult;
}

struct TestTimings {
    time_s: f64,
    runs_per_s: f64
}

impl fmt::Display for TestTimings {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Finished in {}s, {} runs/s.", self.time_s, self.runs_per_s)
    }
}

struct TestStats {
    runs: usize,
    failures: usize,
    errors: usize,
    skips: usize,
}

impl TestStats {
    fn new() -> TestStats {
        TestStats {
            runs: 0,
            failures: 0,
            errors: 0,
            skips: 0
        }
    }

    fn create(result: &TestResult) -> TestStats {
        TestStats {
            runs: 1,
            failures: match *result { TestResult::Fail => 1, _ => 0 },
            errors: match *result { TestResult::Error => 1, _ => 0 },
            skips: match *result { TestResult::Skipped => 1, _ => 0 },
        }
    }

    fn combine(a: TestStats, b: TestStats) -> TestStats {
        TestStats {
            runs: a.runs + b.runs,
            failures: a.failures + b.failures,
            errors: a.errors + b.errors,
            skips: a.skips + b.skips
        }
    }
}

impl fmt::Display for TestStats {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} runs, {} failures, {} errors, {} skips.", self.runs, self.failures, self.errors, self.skips)
    }
}

trait Reporter {
    fn start(&mut self);
    fn record(&mut self, result: &TestResult);
    fn report(&mut self);
}

struct ProgressReporter {
    output: io::Stdout
}

impl ProgressReporter {
    fn new() -> ProgressReporter {
        ProgressReporter {
            output: io::stdout()
        }
    }
}

impl Reporter for ProgressReporter {
    fn start(&mut self) {}
    fn record(&mut self, result: &TestResult) {
        use std::io::{Write};
        match write!(&mut self.output, "{}", result) {
            Ok(_) => match self.output.flush() {
                Ok(_) => {},
                _ => panic!("Unable to flush test result")
            },
            _ => panic!("Unable to write test result")
        };
    }
    fn report(&mut self) {}
}

struct StatisticsReporter {
    start_time: u64,
    end_time: u64,
    results: Vec<TestResult>
}

impl StatisticsReporter {
    fn new() -> StatisticsReporter {
        StatisticsReporter {
            start_time: 0,
            end_time: 0,
            results: Vec::new()
        }
    }
}

impl Reporter for StatisticsReporter {
    fn start(&mut self) {
        println!("# Running.\n");
        self.start_time = time::precise_time_ns();
    }
    fn record(&mut self, result: &TestResult) {
        self.results.push(result.clone());
    }
    fn report(&mut self) {
        self.end_time = time::precise_time_ns();

        let time_ms = self.end_time.wrapping_sub(self.start_time) as i64 / 1000000;
        let time_s = time_ms as f64 / 1000f64;
        let timings = TestTimings {
            time_s: time_s,
            runs_per_s: self.results.len() as f64 / time_s
        };

        println!("\n\n{}", timings);

        let stats = self.results
            .iter()
            .map(TestStats::create)
            .fold(TestStats::new(), TestStats::combine);

        println!("\n{}", stats);

        if stats.skips > 0 {
            println!("\nYou have skipped tests. Run with --verbose for details.");
        }
    }
}

pub struct TestRunner;

impl TestRunner {
    pub fn run(&self, tests: &Vec<Box<Runnable>>) {
        let mut reporter = StatisticsReporter::new();
        reporter.start();

        let results: Vec<TestResult> = tests
            .iter()
            .map(|test| test.run())
            .map(|result| {
                reporter.record(&result);
                result
            })
            .collect();

        reporter.report();
    }
}
<|endoftext|>