<commit_before>// VslIdent (vxid) is encoded as 30 bit long u where 31'st and 32'nd bits are used to tell
// client and backend requests apart; value of 0 is reserved for non-request related
// records.
// VslIdent is assigned in VXID_Get from per worker pool of 32768 IDs. When worker runs out
// additional set of IDs is allocated to it.
// So in practice we have 1Gi IDs available per client/backend request before they wrap
// around.
//
// We need to constrain memory usage + make sure that super old records are not keept
// forever if this constraing is not reached.
//
// To constrain memory we just set a limit of how many slots are available at any given
// time. To constraint time we can count VSL records in u64 or smaller wrapping integer
// so we can tell if two VslIdent's are from two different times.

use linked_hash_map::{self, LinkedHashMap};
use std::cmp::min;
use std::num::Wrapping;
use std::fmt::Debug;
use fnv::FnvHasher;
use std::hash::BuildHasherDefault;

use vsl::record::VslIdent;

pub type Epoch = usize;

// How many VslIdent recorts to keep in the store
const MAX_SLOTS: usize = 4_000;
// How many inserts old a record can be before we drop it on remove
const MAX_EPOCH_DIFF: usize = 100_000;
// How many objects to nuke/expire when store is full or we are expiring as factor of MAX_SLOTS
const EVICT_FACTOR: f32 = 0.01;

pub struct Config {
    max_slots: usize,
    max_epoch_diff: usize,
    evict_count: usize,
}

impl Default for Config {
    fn default() -> Config {
        Config::new(MAX_SLOTS, MAX_EPOCH_DIFF, EVICT_FACTOR).unwrap()
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum ConfigError {
        InvalidMaxSlots {
            description("Max slots must be greater than 0")
        }
        InvalidMaxEpochDiff {
            description("Max epoch diff must be greater then 0")
        }
        InvalidEvictFactor {
            description("Evict factor must yield evict count greater then 0")
        }
    }
}

impl Config {
    pub fn new(max_slots: usize, max_epoch_diff: usize, evict_factor: f32) -> Result<Config, ConfigError> {
        let evict_count = (max_slots as f32 * evict_factor).ceil() as usize;

        if !(max_slots > 0) {
            return Err(ConfigError::InvalidMaxSlots)
        }
        if !(max_epoch_diff > 0) {
            return Err(ConfigError::InvalidMaxEpochDiff)
        }
        if !(evict_count > 0) {
            return Err(ConfigError::InvalidEvictFactor)
        }

        Ok(Config {
            max_slots: max_slots,
            max_epoch_diff: max_epoch_diff,
            evict_count: evict_count,
        })
    }
}

#[derive(Debug)]
pub struct VslStore<T> {
    name: &'static str,
    // in order of insertion, oldest (lowest epoch) records are at the front
    store: LinkedHashMap<VslIdent, (Wrapping<Epoch>, T), BuildHasherDefault<FnvHasher>>,
    slots_free: usize,
    expire_count: usize,
    nuke_count: usize,
    epoch: Wrapping<Epoch>,
    max_epoch_diff: Wrapping<Epoch>,
}

impl<T> VslStore<T> {
    pub fn new(name: &'static str) -> VslStore<T> {
        VslStore::with_config(name, &Default::default())
    }

    pub fn with_config(name: &'static str, config: &Config) -> VslStore<T> {
        VslStore {
            name: name,
            store: LinkedHashMap::default(),
            slots_free: config.max_slots,
            expire_count: config.evict_count,
            nuke_count: config.evict_count,
            epoch: Wrapping(0),
            max_epoch_diff: Wrapping(config.max_epoch_diff),
        }
    }

    pub fn insert(&mut self, ident: VslIdent, value: T) where T: Debug {
        // increase the epoch before we expire to make space for new insert
        self.epoch = self.epoch + Wrapping(1);
        self.expire();

        if self.slots_free < 1 {
            self.nuke();
        }
        assert!(self.slots_free >= 1);

        if self.store.insert(ident, (self.epoch, value)).is_none() {
            self.slots_free -= 1;
        }
    }

    pub fn get_mut(&mut self, ident: &VslIdent) -> Option<&mut T> {
        self.store.get_mut(ident).map(|&mut (_epoch, ref mut t)| t)
    }

    pub fn get(&self, ident: &VslIdent) -> Option<&T> {
        self.store.get(ident).map(|&(_epoch, ref t)| t)
    }

    pub fn contains_key(&self, ident: &VslIdent) -> bool {
        self.store.contains_key(ident)
    }

    pub fn remove(&mut self, ident: &VslIdent) -> Option<T> {
        let val = self.store.remove(ident).map(|(_epoch, t)| t);
        if val.is_some() {
            self.slots_free += 1;
        }
        val
    }

    pub fn values(&self) -> Values<T> {
        Values(self.store.values())
    }

    fn expire(&mut self) where T: Debug {
        let to_expire = self.store.values()
            .take(self.expire_count)
            .take_while(|&&(epoch, _)| self.epoch - epoch >= self.max_epoch_diff)
            .count();
        //trace!("to_expire: {} expire_count: {} store:\n{:#?}", to_expire, self.expire_count, self);

        if to_expire == 0 {
            return
        }

        warn!("VslStore[{}]: Expiring {} records", &self.name, to_expire);
        for _ in 0..to_expire {
            let (ident, (epoch, record)) = self.store.pop_front().unwrap();
            self.slots_free += 1;
            info!("VslStore[{}]: Removed expired record from store: current epoch {}, record epoch {}, ident: {}:\n{:#?}", &self.name, self.epoch, epoch, ident, record);
        }
    }

    fn nuke(&mut self) where T: Debug {
        let to_nuke: usize = min(self.nuke_count, self.store.len());

        warn!("VslStore[{}]: Nuking {} oldest records", &self.name, to_nuke);
        for _ in 0..to_nuke {
            let (ident, (epoch, record)) = self.store.pop_front().unwrap();
            self.slots_free += 1;
            info!("VslStore[{}]: Nuked record from store: current epoch {}, record epoch {}, ident: {}:\n{:#?}", &self.name, self.epoch, epoch, ident, record);
        }
    }
}

pub struct Values<'a, T: 'a>(linked_hash_map::Values<'a, VslIdent, (Wrapping<Epoch>, T)>);

impl<'a, T> Iterator for Values<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|v| &v.1)
    }
}

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

    use vsl::record::VslIdent;
    impl<T> VslStore<T> {
        pub fn oldest(&self) -> Option<(&VslIdent, &T)> {
            self.store.front().map(|(i, v)| (i, &v.1))
        }
    }

    #[test]
    fn nuking() {
        let mut s = VslStore::with_config("foo", &Config::new(10, 200, 0.1).unwrap());
        for i in 0..130 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 130 - 10);
    }

    #[test]
    fn slot_count() {
        let mut s = VslStore::with_config("foo", &Config::new(10, 100, 0.1).unwrap());

        for i in 0..10 {
            s.insert(i, i);
        }

        for i in 0..2 {
            s.remove(&i);
        }

        s.remove(&100);

        for i in 10..13 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 13 - 10);
    }

    #[test]
    fn expire() {
        let mut s = VslStore::with_config("foo", &Config::new(200, 10, 0.1).unwrap());

        for i in 0..130 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 130 - 10);
    }
}
<commit_msg>bulk up expiring; log expirations and nukes on warning level<commit_after>// VslIdent (vxid) is encoded as 30 bit long u where 31'st and 32'nd bits are used to tell
// client and backend requests apart; value of 0 is reserved for non-request related
// records.
// VslIdent is assigned in VXID_Get from per worker pool of 32768 IDs. When worker runs out
// additional set of IDs is allocated to it.
// So in practice we have 1Gi IDs available per client/backend request before they wrap
// around.
//
// We need to constrain memory usage + make sure that super old records are not keept
// forever if this constraing is not reached.
//
// To constrain memory we just set a limit of how many slots are available at any given
// time. To constraint time we can count VSL records in u64 or smaller wrapping integer
// so we can tell if two VslIdent's are from two different times.

use linked_hash_map::{self, LinkedHashMap};
use std::cmp::min;
use std::num::Wrapping;
use std::fmt::Debug;
use fnv::FnvHasher;
use std::hash::BuildHasherDefault;

use vsl::record::VslIdent;

pub type Epoch = usize;

// How many VslIdent recorts to keep in the store
const MAX_SLOTS: usize = 4_000;
// How many inserts old a record can be before we drop it on remove
const MAX_EPOCH_DIFF: usize = 100_000;
// How many objects to nuke/expire when store is full or we are expiring as factor of MAX_SLOTS
const EVICT_FACTOR: f32 = 0.01;

pub struct Config {
    max_slots: usize,
    max_epoch_diff: usize,
    evict_count: usize,
}

impl Default for Config {
    fn default() -> Config {
        Config::new(MAX_SLOTS, MAX_EPOCH_DIFF, EVICT_FACTOR).unwrap()
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum ConfigError {
        InvalidMaxSlots {
            description("Max slots must be greater than 0")
        }
        InvalidMaxEpochDiff {
            description("Max epoch diff must be greater then 0")
        }
        InvalidEvictFactor {
            description("Evict factor must yield evict count greater then 0")
        }
    }
}

impl Config {
    pub fn new(max_slots: usize, max_epoch_diff: usize, evict_factor: f32) -> Result<Config, ConfigError> {
        let evict_count = (max_slots as f32 * evict_factor).ceil() as usize;

        if !(max_slots > 0) {
            return Err(ConfigError::InvalidMaxSlots)
        }
        if !(max_epoch_diff > 0) {
            return Err(ConfigError::InvalidMaxEpochDiff)
        }
        if !(evict_count > 0) {
            return Err(ConfigError::InvalidEvictFactor)
        }

        Ok(Config {
            max_slots: max_slots,
            max_epoch_diff: max_epoch_diff,
            evict_count: evict_count,
        })
    }
}

#[derive(Debug)]
pub struct VslStore<T> {
    name: &'static str,
    // in order of insertion, oldest (lowest epoch) records are at the front
    store: LinkedHashMap<VslIdent, (Wrapping<Epoch>, T), BuildHasherDefault<FnvHasher>>,
    slots_free: usize,
    expire_count: usize,
    nuke_count: usize,
    epoch: Wrapping<Epoch>,
    max_epoch_diff: Wrapping<Epoch>,
}

impl<T> VslStore<T> {
    pub fn new(name: &'static str) -> VslStore<T> {
        VslStore::with_config(name, &Default::default())
    }

    pub fn with_config(name: &'static str, config: &Config) -> VslStore<T> {
        VslStore {
            name: name,
            store: LinkedHashMap::default(),
            slots_free: config.max_slots,
            expire_count: config.evict_count,
            nuke_count: config.evict_count,
            epoch: Wrapping(0),
            max_epoch_diff: Wrapping(config.max_epoch_diff),
        }
    }

    pub fn insert(&mut self, ident: VslIdent, value: T) where T: Debug {
        // increase the epoch before we expire to make space for new insert
        self.epoch = self.epoch + Wrapping(1);

        if self.epoch % Wrapping(self.expire_count) == Wrapping(0) || self.slots_free < 1 {
            // expire every expire_count insert to bulk it up
            // try to expire if storage is full before nuking
            self.expire();
        }

        if self.slots_free < 1 {
            self.nuke();
        }

        assert!(self.slots_free >= 1);

        if self.store.insert(ident, (self.epoch, value)).is_none() {
            self.slots_free -= 1;
        }
    }

    pub fn get_mut(&mut self, ident: &VslIdent) -> Option<&mut T> {
        self.store.get_mut(ident).map(|&mut (_epoch, ref mut t)| t)
    }

    pub fn get(&self, ident: &VslIdent) -> Option<&T> {
        self.store.get(ident).map(|&(_epoch, ref t)| t)
    }

    pub fn contains_key(&self, ident: &VslIdent) -> bool {
        self.store.contains_key(ident)
    }

    pub fn remove(&mut self, ident: &VslIdent) -> Option<T> {
        let val = self.store.remove(ident).map(|(_epoch, t)| t);
        if val.is_some() {
            self.slots_free += 1;
        }
        val
    }

    pub fn values(&self) -> Values<T> {
        Values(self.store.values())
    }

    fn expire(&mut self) where T: Debug {
        let to_expire = self.store.values()
            .take(self.expire_count)
            .take_while(|&&(epoch, _)| self.epoch - epoch >= self.max_epoch_diff)
            .count();

        if to_expire == 0 {
            return
        }

        for _ in 0..to_expire {
            let (ident, (epoch, record)) = self.store.pop_front().unwrap();
            self.slots_free += 1;
            warn!("VslStore[{}]: Removed expired record from store: current epoch {}, record epoch {}, ident: {}:\n{:#?}", &self.name, self.epoch, epoch, ident, record);
        }
    }

    fn nuke(&mut self) where T: Debug {
        let to_nuke: usize = min(self.nuke_count, self.store.len());

        for _ in 0..to_nuke {
            let (ident, (epoch, record)) = self.store.pop_front().unwrap();
            self.slots_free += 1;
            warn!("VslStore[{}]: Nuked record from store: current epoch {}, record epoch {}, ident: {}:\n{:#?}", &self.name, self.epoch, epoch, ident, record);
        }
    }
}

pub struct Values<'a, T: 'a>(linked_hash_map::Values<'a, VslIdent, (Wrapping<Epoch>, T)>);

impl<'a, T> Iterator for Values<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|v| &v.1)
    }
}

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

    use vsl::record::VslIdent;
    impl<T> VslStore<T> {
        pub fn oldest(&self) -> Option<(&VslIdent, &T)> {
            self.store.front().map(|(i, v)| (i, &v.1))
        }
    }

    #[test]
    fn nuking() {
        let mut s = VslStore::with_config("foo", &Config::new(10, 200, 0.1).unwrap());
        for i in 0..130 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 130 - 10);
    }

    #[test]
    fn slot_count() {
        let mut s = VslStore::with_config("foo", &Config::new(10, 100, 0.1).unwrap());

        for i in 0..10 {
            s.insert(i, i);
        }

        for i in 0..2 {
            s.remove(&i);
        }

        s.remove(&100);

        for i in 10..13 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 13 - 10);
    }

    #[test]
    fn expire() {
        let mut s = VslStore::with_config("foo", &Config::new(200, 10, 0.1).unwrap());

        // will expire every 20 records (200 * 0.1)
        for i in 0..140 {
            s.insert(i, i);
        }

        assert_eq!(*s.oldest().unwrap().0, 140 - 10);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

#[derive(PartialEq,Eq,Debug,Hash,Clone)]
pub enum BasicValue {
    Byte(u8),
    Boolean(bool),
    Int16(i16),
    Uint16(u16),
    Int32(i32),
    Uint32(u32),
    Int64(i64),
    Uint64(u64),
    String(String),
    ObjectPath(Path),
    Signature(Signature),
}

#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub struct Path(pub String);

#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub struct Signature(pub String);

#[derive(PartialEq,Debug,Clone)]
pub struct Struct {
    pub objects: Vec<Value>,
    pub signature: Signature
}

#[derive(PartialEq,Debug,Clone)]
pub struct Variant {
    pub object: Box<Value>,
    pub signature: Signature
}

pub type Dictionary = HashMap<BasicValue,Value>;

#[derive(PartialEq,Debug,Clone)]
pub enum Value {
    BasicValue(BasicValue),
    Double(f64),
    Array(Vec<Value>),
    Variant(Variant),
    Struct(Struct),
    Dictionary(Dictionary)
}

impl<'a> From<&'a Value> for u8 {
    fn from(v: &'a Value) -> u8 {
        match v {
            &Value::BasicValue(BasicValue::Byte(x)) => x,
            _ => panic!("Not a byte")
        }
    }
}

impl<'a> From<&'a Value> for i16 {
    fn from(v: &'a Value) -> i16 {
        match v {
            &Value::BasicValue(BasicValue::Int16(x)) => x,
            _ => panic!("Not an Int16")
        }
    }
}

impl<'a> From<&'a Value> for i32 {
    fn from(v: &'a Value) -> i32 {
        match v {
            &Value::BasicValue(BasicValue::Int32(x)) => x,
            _ => panic!("Not an Int32")
        }
    }
}

impl<'a> From<&'a Value> for i64 {
    fn from(v: &'a Value) -> i64 {
        match v {
            &Value::BasicValue(BasicValue::Int64(x)) => x,
            _ => panic!("Not an Int64")
        }
    }
}

impl<'a> From<&'a Value> for u16 {
    fn from(v: &'a Value) -> u16 {
        match v {
            &Value::BasicValue(BasicValue::Uint16(x)) => x,
            _ => panic!("Not a Uint16")
        }
    }
}

impl<'a> From<&'a Value> for u32 {
    fn from(v: &'a Value) -> u32 {
        match v {
            &Value::BasicValue(BasicValue::Uint32(x)) => x,
            _ => panic!("Not a Uint32")
        }
    }
}

impl<'a> From<&'a Value> for u64 {
    fn from(v: &'a Value) -> u64 {
        match v {
            &Value::BasicValue(BasicValue::Uint64(x)) => x,
            _ => panic!("Not a Uint32")
        }
    }
}

impl<'a> From<&'a Value> for String {
    fn from(v: &'a Value) -> String {
        match v {
            &Value::BasicValue(BasicValue::String(ref x)) => x.to_string(),
            &Value::BasicValue(BasicValue::ObjectPath(Path(ref x))) => x.to_string(),
            &Value::BasicValue(BasicValue::Signature(Signature(ref x))) => x.to_string(),
            _ => panic!("Not a string type")
        }
    }
}

<commit_msg>types.rs: add constructor for Variant<commit_after>use std::collections::HashMap;

#[derive(PartialEq,Eq,Debug,Hash,Clone)]
pub enum BasicValue {
    Byte(u8),
    Boolean(bool),
    Int16(i16),
    Uint16(u16),
    Int32(i32),
    Uint32(u32),
    Int64(i64),
    Uint64(u64),
    String(String),
    ObjectPath(Path),
    Signature(Signature),
}

#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub struct Path(pub String);

#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub struct Signature(pub String);

#[derive(PartialEq,Debug,Clone)]
pub struct Struct {
    pub objects: Vec<Value>,
    pub signature: Signature
}

#[derive(PartialEq,Debug,Clone)]
pub struct Variant {
    pub object: Box<Value>,
    pub signature: Signature
}

impl Variant {
    pub fn new (v: Value, s: &str) -> Variant {
        Variant {
            object: Box::new(v),
            signature: Signature(s.to_string())
        }
    }
}

pub type Dictionary = HashMap<BasicValue,Value>;

#[derive(PartialEq,Debug,Clone)]
pub enum Value {
    BasicValue(BasicValue),
    Double(f64),
    Array(Vec<Value>),
    Variant(Variant),
    Struct(Struct),
    Dictionary(Dictionary)
}

impl<'a> From<&'a Value> for u8 {
    fn from(v: &'a Value) -> u8 {
        match v {
            &Value::BasicValue(BasicValue::Byte(x)) => x,
            _ => panic!("Not a byte")
        }
    }
}

impl<'a> From<&'a Value> for i16 {
    fn from(v: &'a Value) -> i16 {
        match v {
            &Value::BasicValue(BasicValue::Int16(x)) => x,
            _ => panic!("Not an Int16")
        }
    }
}

impl<'a> From<&'a Value> for i32 {
    fn from(v: &'a Value) -> i32 {
        match v {
            &Value::BasicValue(BasicValue::Int32(x)) => x,
            _ => panic!("Not an Int32")
        }
    }
}

impl<'a> From<&'a Value> for i64 {
    fn from(v: &'a Value) -> i64 {
        match v {
            &Value::BasicValue(BasicValue::Int64(x)) => x,
            _ => panic!("Not an Int64")
        }
    }
}

impl<'a> From<&'a Value> for u16 {
    fn from(v: &'a Value) -> u16 {
        match v {
            &Value::BasicValue(BasicValue::Uint16(x)) => x,
            _ => panic!("Not a Uint16")
        }
    }
}

impl<'a> From<&'a Value> for u32 {
    fn from(v: &'a Value) -> u32 {
        match v {
            &Value::BasicValue(BasicValue::Uint32(x)) => x,
            _ => panic!("Not a Uint32")
        }
    }
}

impl<'a> From<&'a Value> for u64 {
    fn from(v: &'a Value) -> u64 {
        match v {
            &Value::BasicValue(BasicValue::Uint64(x)) => x,
            _ => panic!("Not a Uint32")
        }
    }
}

impl<'a> From<&'a Value> for String {
    fn from(v: &'a Value) -> String {
        match v {
            &Value::BasicValue(BasicValue::String(ref x)) => x.to_string(),
            &Value::BasicValue(BasicValue::ObjectPath(Path(ref x))) => x.to_string(),
            &Value::BasicValue(BasicValue::Signature(Signature(ref x))) => x.to_string(),
            _ => panic!("Not a string type")
        }
    }
}

<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::time::{Duration, Instant};

use regex::{Captures, Regex};

use console::{measure_text_width, Style};

pub fn duration_to_secs(d: Duration) -> f64 {
    d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64
}

pub fn secs_to_duration(s: f64) -> Duration {
    let secs = s.trunc() as u64;
    let nanos = (s.fract() * 1_000_000_000f64) as u32;
    Duration::new(secs, nanos)
}

pub struct Estimate {
    buf: Vec<f64>,
    buf_cap: usize,
    last_idx: usize,
    started: Option<(Instant, u64)>,
}

impl Estimate {
    pub fn new() -> Estimate {
        Estimate {
            buf: vec![],
            buf_cap: 10,
            last_idx: 0,
            started: Some((<Instant>::now(), 0)),
        }
    }

    pub fn reset(&mut self) {
        self.buf.clear();
        self.last_idx = 0;
        self.started = None;
    }

    pub fn record_step(&mut self, value: u64) {
        // record initial position
        let (started_time, started_value) = match self.started {
            None => {
                let rv = (Instant::now(), value);
                self.started = Some(rv);
                rv
            }
            Some(value) => value,
        };

        let item = if value == 0 {
            0.0
        } else {
            let divisor = value.saturating_sub(started_value) as f64;
            if divisor == 0.0 {
                0.0
            } else {
                duration_to_secs(started_time.elapsed()) / divisor
            }
        };
        if self.buf.len() >= self.buf_cap {
            let idx = self.last_idx % self.buf.len();
            self.buf[idx] = item;
        } else {
            self.buf.push(item);
        }
        self.last_idx += 1;
    }

    pub fn time_per_step(&self) -> Duration {
        if self.buf.is_empty() {
            Duration::new(0, 0)
        } else {
            secs_to_duration(self.buf.iter().sum::<f64>() / self.buf.len() as f64)
        }
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

#[derive(Debug)]
pub struct TemplateVar<'a> {
    pub key: &'a str,
    pub align: Alignment,
    pub truncate: bool,
    pub width: Option<usize>,
    pub style: Option<Style>,
    pub alt_style: Option<Style>,
    pub last_element: bool,
}

impl<'a> TemplateVar<'a> {
    pub fn duplicate_for_key<'b>(&self, key: &'b str) -> TemplateVar<'b> {
        TemplateVar {
            key,
            align: self.align,
            truncate: self.truncate,
            width: self.width,
            style: self.style.clone(),
            alt_style: self.alt_style.clone(),
            last_element: self.last_element,
        }
    }
}

pub fn expand_template<F: FnMut(&TemplateVar<'_>) -> String>(s: &str, mut f: F) -> Cow<'_, str> {
    lazy_static::lazy_static! {
        static ref VAR_RE: Regex = Regex::new(r"(\}\})|\{(\{|[^{}}]+\})").unwrap();
        static ref KEY_RE: Regex = Regex::new(
            r"(?x)
                ([^:]+)
                (?:
                    :
                    ([<^>])?
                    ([0-9]+)?
                    (!)?
                    (?:\.([a-z_]+(?:\.[a-z_]+)*))?
                    (?:/([a-z_]+(?:\.[a-z_]+)*))?
                )?
            "
        )
        .unwrap();
    }
    VAR_RE.replace_all(s, |caps: &Captures<'_>| {
        if caps.get(1).is_some() {
            return "}".into();
        }
        let key = &caps[2];
        if key == "{" {
            return "{".into();
        }
        let mut var = TemplateVar {
            key,
            align: Alignment::Left,
            truncate: false,
            width: None,
            style: None,
            alt_style: None,
            last_element: caps.get(0).unwrap().end() >= s.len(),
        };
        if let Some(opt_caps) = KEY_RE.captures(&key[..key.len() - 1]) {
            if let Some(short_key) = opt_caps.get(1) {
                var.key = short_key.as_str();
            }
            var.align = match opt_caps.get(2).map(|x| x.as_str()) {
                Some("<") => Alignment::Left,
                Some("^") => Alignment::Center,
                Some(">") => Alignment::Right,
                _ => Alignment::Left,
            };
            if let Some(width) = opt_caps.get(3) {
                var.width = Some(width.as_str().parse().unwrap());
            }
            if opt_caps.get(4).is_some() {
                var.truncate = true;
            }
            if let Some(style) = opt_caps.get(5) {
                var.style = Some(Style::from_dotted_str(style.as_str()));
            }
            if let Some(alt_style) = opt_caps.get(6) {
                var.alt_style = Some(Style::from_dotted_str(alt_style.as_str()));
            }
        }
        let mut rv = f(&var);
        if let Some(width) = var.width {
            rv = pad_str(&rv, width, var.align, var.truncate).to_string()
        }
        if let Some(s) = var.style {
            rv = s.apply_to(rv).to_string();
        }
        rv
    })
}

pub fn pad_str(s: &str, width: usize, align: Alignment, truncate: bool) -> Cow<'_, str> {
    let cols = measure_text_width(s);

    if cols >= width {
        return if truncate {
            Cow::Borrowed(s.get(..width).unwrap_or(s))
        } else {
            Cow::Borrowed(s)
        };
    }

    let diff = width.saturating_sub(cols);

    let (left_pad, right_pad) = match align {
        Alignment::Left => (0, diff),
        Alignment::Right => (diff, 0),
        Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
    };

    let mut rv = String::new();
    for _ in 0..left_pad {
        rv.push(' ');
    }
    rv.push_str(s);
    for _ in 0..right_pad {
        rv.push(' ');
    }
    Cow::Owned(rv)
}

#[test]
fn test_expand_template() {
    let rv = expand_template("{{ {foo} {bar} }}", |var| var.key.to_uppercase());
    assert_eq!(&rv, "{ FOO BAR }");
    let rv = expand_template(r#"{ "foo": "{foo}", "bar": {bar} }"#, |var| {
        var.key.to_uppercase()
    });
    assert_eq!(&rv, r#"{ "foo": "FOO", "bar": BAR }"#);
}

#[test]
fn test_expand_template_flags() {
    use console::set_colors_enabled;
    set_colors_enabled(true);

    let rv = expand_template("{foo:5}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        "XXX".into()
    });
    assert_eq!(&rv, "XXX  ");

    let rv = expand_template("{foo:.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, None);
        assert_eq!(var.align, Alignment::Left);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue/green.on_cyan}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        assert_eq!(var.alt_style, Some(Style::new().green().on_cyan()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
}

#[test]
fn test_duration_stuff() {
    let duration = Duration::new(42, 100_000_000);
    let secs = duration_to_secs(duration);
    assert_eq!(secs_to_duration(secs), duration);
}
<commit_msg>Compress Estimate struct<commit_after>use std::borrow::Cow;
use std::time::{Duration, Instant};

use regex::{Captures, Regex};

use console::{measure_text_width, Style};

pub fn duration_to_secs(d: Duration) -> f64 {
    d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64
}

pub fn secs_to_duration(s: f64) -> Duration {
    let secs = s.trunc() as u64;
    let nanos = (s.fract() * 1_000_000_000f64) as u32;
    Duration::new(secs, nanos)
}

pub struct Estimate {
    buf: Box<[f64; 15]>,
    data: u8,
    started: Option<(Instant, u64)>,
}

impl Estimate {
    fn len(&self) -> u8 {
        self.data & 0x0F
    }

    fn set_len(&mut self, len: u8) {
        // Sanity check to make sure math is correct as otherwise it could result in unexpected bugs
        debug_assert!(len < 16);
        self.data = (self.data & 0xF0) | len;
    }

    fn last_idx(&self) -> u8 {
        (self.data & 0xF0) >> 4
    }

    fn set_last_idx(&mut self, last_idx: u8) {
        // This will wrap last_idx on overflow (setting to 16 will result in 0); this is fine
        // because Estimate::buf is 15 elements long
        self.data = ((last_idx & 0x0F) << 4) | (self.data & 0x0F);
    }

    pub fn new() -> Self {
        let this = Self {
            buf: Box::new([0.0; 15]),
            data: 0,
            started: Some((<Instant>::now(), 0)),
        };
        // Make sure not to break anything accidentally as self.data can't handle bufs longer than
        // 15 elements
        debug_assert!(this.buf.len() < 16);
        this
    }

    pub fn reset(&mut self) {
        self.started = None;
        self.data = 0;
    }

    pub fn record_step(&mut self, value: u64) {
        // record initial position
        let (started_time, started_value) =
            *self.started.get_or_insert_with(|| (Instant::now(), value));

        let item = {
            let divisor = value.saturating_sub(started_value) as f64;
            if divisor == 0.0 {
                0.0
            } else {
                duration_to_secs(started_time.elapsed()) / divisor
            }
        };
        let len = self.len();
        let last_idx = self.last_idx();
        if self.buf.len() <= usize::from(len) {
            let idx = last_idx % len;
            self.buf[usize::from(idx)] = item;
        } else {
            self.set_len(len + 1);
            self.buf[usize::from(last_idx)] = item;
        }
        self.set_last_idx(last_idx + 1);
    }

    pub fn time_per_step(&self) -> Duration {
        let len = self.len();
        secs_to_duration(self.buf[0..usize::from(len)].iter().sum::<f64>() / f64::from(len))
    }
}

#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum Alignment {
    Left,
    Center,
    Right,
}

#[derive(Debug)]
pub struct TemplateVar<'a> {
    pub key: &'a str,
    pub align: Alignment,
    pub truncate: bool,
    pub width: Option<usize>,
    pub style: Option<Style>,
    pub alt_style: Option<Style>,
    pub last_element: bool,
}

impl<'a> TemplateVar<'a> {
    pub fn duplicate_for_key<'b>(&self, key: &'b str) -> TemplateVar<'b> {
        TemplateVar {
            key,
            align: self.align,
            truncate: self.truncate,
            width: self.width,
            style: self.style.clone(),
            alt_style: self.alt_style.clone(),
            last_element: self.last_element,
        }
    }
}

pub fn expand_template<F: FnMut(&TemplateVar<'_>) -> String>(s: &str, mut f: F) -> Cow<'_, str> {
    lazy_static::lazy_static! {
        static ref VAR_RE: Regex = Regex::new(r"(\}\})|\{(\{|[^{}}]+\})").unwrap();
        static ref KEY_RE: Regex = Regex::new(
            r"(?x)
                ([^:]+)
                (?:
                    :
                    ([<^>])?
                    ([0-9]+)?
                    (!)?
                    (?:\.([a-z_]+(?:\.[a-z_]+)*))?
                    (?:/([a-z_]+(?:\.[a-z_]+)*))?
                )?
            "
        )
        .unwrap();
    }
    VAR_RE.replace_all(s, |caps: &Captures<'_>| {
        if caps.get(1).is_some() {
            return "}".into();
        }
        let key = &caps[2];
        if key == "{" {
            return "{".into();
        }
        let mut var = TemplateVar {
            key,
            align: Alignment::Left,
            truncate: false,
            width: None,
            style: None,
            alt_style: None,
            last_element: caps.get(0).unwrap().end() >= s.len(),
        };
        if let Some(opt_caps) = KEY_RE.captures(&key[..key.len() - 1]) {
            if let Some(short_key) = opt_caps.get(1) {
                var.key = short_key.as_str();
            }
            var.align = match opt_caps.get(2).map(|x| x.as_str()) {
                Some("<") => Alignment::Left,
                Some("^") => Alignment::Center,
                Some(">") => Alignment::Right,
                _ => Alignment::Left,
            };
            if let Some(width) = opt_caps.get(3) {
                var.width = Some(width.as_str().parse().unwrap());
            }
            if opt_caps.get(4).is_some() {
                var.truncate = true;
            }
            if let Some(style) = opt_caps.get(5) {
                var.style = Some(Style::from_dotted_str(style.as_str()));
            }
            if let Some(alt_style) = opt_caps.get(6) {
                var.alt_style = Some(Style::from_dotted_str(alt_style.as_str()));
            }
        }
        let mut rv = f(&var);
        if let Some(width) = var.width {
            rv = pad_str(&rv, width, var.align, var.truncate).to_string()
        }
        if let Some(s) = var.style {
            rv = s.apply_to(rv).to_string();
        }
        rv
    })
}

pub fn pad_str(s: &str, width: usize, align: Alignment, truncate: bool) -> Cow<'_, str> {
    let cols = measure_text_width(s);

    if cols >= width {
        return if truncate {
            Cow::Borrowed(s.get(..width).unwrap_or(s))
        } else {
            Cow::Borrowed(s)
        };
    }

    let diff = width.saturating_sub(cols);

    let (left_pad, right_pad) = match align {
        Alignment::Left => (0, diff),
        Alignment::Right => (diff, 0),
        Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
    };

    let mut rv = String::new();
    for _ in 0..left_pad {
        rv.push(' ');
    }
    rv.push_str(s);
    for _ in 0..right_pad {
        rv.push(' ');
    }
    Cow::Owned(rv)
}

#[test]
fn test_expand_template() {
    let rv = expand_template("{{ {foo} {bar} }}", |var| var.key.to_uppercase());
    assert_eq!(&rv, "{ FOO BAR }");
    let rv = expand_template(r#"{ "foo": "{foo}", "bar": {bar} }"#, |var| {
        var.key.to_uppercase()
    });
    assert_eq!(&rv, r#"{ "foo": "FOO", "bar": BAR }"#);
}

#[test]
fn test_expand_template_flags() {
    use console::set_colors_enabled;
    set_colors_enabled(true);

    let rv = expand_template("{foo:5}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        "XXX".into()
    });
    assert_eq!(&rv, "XXX  ");

    let rv = expand_template("{foo:.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, None);
        assert_eq!(var.align, Alignment::Left);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");

    let rv = expand_template("{foo:^5.red.on_blue/green.on_cyan}", |var| {
        assert_eq!(var.key, "foo");
        assert_eq!(var.width, Some(5));
        assert_eq!(var.align, Alignment::Center);
        assert_eq!(var.style, Some(Style::new().red().on_blue()));
        assert_eq!(var.alt_style, Some(Style::new().green().on_cyan()));
        "XXX".into()
    });
    assert_eq!(&rv, "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
}

#[test]
fn test_duration_stuff() {
    let duration = Duration::new(42, 100_000_000);
    let secs = duration_to_secs(duration);
    assert_eq!(secs_to_duration(secs), duration);
}
<|endoftext|>


<commit_before>use std::borrow::Cow;

pub fn dv(input: &[u8]) -> Cow<str> {
    String::from_utf8_lossy(&input)
}

pub fn epoch_int() -> u64 {
    use std::time::{UNIX_EPOCH, SystemTime};

    let now = SystemTime::now();
    let unix = now.duration_since(UNIX_EPOCH).unwrap().as_secs();

    unix
}

pub fn split_string(input: &[u8]) -> Vec<Vec<u8>> {
    let mut buf: Vec<Vec<u8>> = Vec::new();
    let mut tmp: Vec<u8> = Vec::new();

    for (index, &i) in input.iter().enumerate() {
        if i == b' ' {
            buf.push(tmp);
            tmp = Vec::new();
            continue;
        }

        tmp.push(i);

        if index + 1 == input.len() {
            buf.push(tmp);
            break;
        }
    }

    buf
}

pub fn unsplit_string(argv: &[Vec<u8>], argc: usize, startidx: usize, max: usize) -> Vec<u8> {
    let mut dest: Vec<u8> = Vec::new();
    let mut vec: Vec<Vec<u8>> = Vec::new();

    for i in startidx..argc {
        vec.push(argv[i as usize].clone());
    }

    if max > vec.len() { return dest; }

    for i in 0..max {
        for j in 0..vec[i as usize].len() {
            dest.push(vec[i as usize][j as usize]);
        }
        dest.push(b' ');
    }

    dest
}

pub fn u8_slice_to_lower(input: &[u8]) -> Vec<u8> {
    use std::ascii::AsciiExt;

    let mut buf: Vec<u8> = input.to_vec().clone();
    for mut byte in &mut buf {
        byte.make_ascii_lowercase();
    }

    return buf;
}

pub fn trim_bytes_right(mut input: &[u8]) -> &[u8] {
    loop {
        match input.iter().next_back() {
            Some(&b'\r') | Some(&b'\n') => {
                input = &input[0..input.len()-1]
            }
            _ => break,
        }
    }

    input
}

pub fn ceiling_division(left: usize, right: usize) -> usize {
    assert!(left > 0);

    1 + ((left - 1) / right)
}

#[test]
fn test_ceiling_division() {
    assert_eq!(ceiling_division(499, 500), 1);
    assert_eq!(ceiling_division(500, 500), 1);
    assert_eq!(ceiling_division(501, 500), 2);
}

#[test]
fn test_unsplit_string() {
    let my_argv: Vec<Vec<u8>> = vec![
        format!("A").into_bytes(),
        format!("BB").into_bytes(),
        format!("TEST").into_bytes(),
        format!("SOMETING").into_bytes(),
        format!("FDSAADFS").into_bytes(),
        format!("FADS").into_bytes(),
        format!("ASDf").into_bytes(),
    ];

    let new_unsplit = unsplit_string(&my_argv, 7, 3, 3);
    assert_eq!(&new_unsplit, b"SOMETING FDSAADFS FADS ");
    assert_eq!(new_unsplit.len(), 23);

    let my_argv: Vec<Vec<u8>> = vec![
        format!("B").into_bytes(),
        format!("#channel").into_bytes(),
        format!("9999999999").into_bytes(),
        format!("+stnzl").into_bytes(),
        format!("554").into_bytes(),
        format!("AAAAA:o,AAAAB,AAAAC").into_bytes(),
    ];

    let new_unsplit = unsplit_string(&my_argv, 6, 3, 2);
    assert_eq!(&new_unsplit, b"+stnzl 554 ");
    assert_eq!(new_unsplit.len(), 11);
}

#[test]
fn test_split_string() {
    let s = split_string(&format!("+ntl 34").into_bytes());
    assert_eq!(s.len(), 2);
    assert_eq!(s[0], b"+ntl");
    assert_eq!(s[1], b"34");

    let s = split_string(&format!("fdas fasd adsf asfd dfas").into_bytes());
    assert_eq!(s.len(), 5);
    assert_eq!(s[0], b"fdas");
    assert_eq!(s[1], b"fasd");
    assert_eq!(s[2], b"adsf");
    assert_eq!(s[3], b"asfd");
    assert_eq!(s[4], b"dfas");

    let s = split_string(&format!("aaaaaaa").into_bytes());
    assert_eq!(s.len(), 1);
    assert_eq!(s[0], b"aaaaaaa");

    let s = split_string(&format!("").into_bytes());
    assert_eq!(s.len(), 0);
}

#[test]
fn test_u8_slice_to_lower() {
    let caps = &String::from("THIS IS IN ALL CAPS").into_bytes();
    let lowered = u8_slice_to_lower(&caps);
    assert_eq!(lowered.len(), 19);
    assert_eq!(lowered, b"this is in all caps");
}

#[test]
fn test_trim_bytes_right() {
    let mystr: &[u8] = &String::from("This has newlines and a carriage return\r\n").into_bytes();
    let clean = trim_bytes_right(mystr);
    assert_eq!(clean.len(), 39);
    assert_eq!(clean[38], b'n');
}
<commit_msg>utils: Add P10 style base64<commit_after>use std::borrow::Cow;

pub fn dv(input: &[u8]) -> Cow<str> {
    String::from_utf8_lossy(&input)
}

pub fn epoch_int() -> u64 {
    use std::time::{UNIX_EPOCH, SystemTime};

    let now = SystemTime::now();
    let unix = now.duration_since(UNIX_EPOCH).unwrap().as_secs();

    unix
}

pub fn split_string(input: &[u8]) -> Vec<Vec<u8>> {
    let mut buf: Vec<Vec<u8>> = Vec::new();
    let mut tmp: Vec<u8> = Vec::new();

    for (index, &i) in input.iter().enumerate() {
        if i == b' ' {
            buf.push(tmp);
            tmp = Vec::new();
            continue;
        }

        tmp.push(i);

        if index + 1 == input.len() {
            buf.push(tmp);
            break;
        }
    }

    buf
}

pub fn unsplit_string(argv: &[Vec<u8>], argc: usize, startidx: usize, max: usize) -> Vec<u8> {
    let mut dest: Vec<u8> = Vec::new();
    let mut vec: Vec<Vec<u8>> = Vec::new();

    for i in startidx..argc {
        vec.push(argv[i as usize].clone());
    }

    if max > vec.len() { return dest; }

    for i in 0..max {
        for j in 0..vec[i as usize].len() {
            dest.push(vec[i as usize][j as usize]);
        }
        dest.push(b' ');
    }

    dest
}

pub fn u8_slice_to_lower(input: &[u8]) -> Vec<u8> {
    use std::ascii::AsciiExt;

    let mut buf: Vec<u8> = input.to_vec().clone();
    for mut byte in &mut buf {
        byte.make_ascii_lowercase();
    }

    return buf;
}

pub fn trim_bytes_right(mut input: &[u8]) -> &[u8] {
    loop {
        match input.iter().next_back() {
            Some(&b'\r') | Some(&b'\n') => {
                input = &input[0..input.len()-1]
            }
            _ => break,
        }
    }

    input
}

pub fn ceiling_division(left: usize, right: usize) -> usize {
    assert!(left > 0);

    1 + ((left - 1) / right)
}

// 64*64*1    64*1     1*2
// #define NUMNICKLOG 6
// #define NUMNICKBASE (1 << NUMNICKLOG)
// #define NUMNICKMASK (NUMNICKBASE - 1)
pub fn inttobase64(mut v: usize, count: usize) -> String {
    static CONVERT2Y: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789[]";

    let mut buf: Vec<u8> = Vec::new();
    for _ in 0..count {
        buf.push(CONVERT2Y[v & ((1 << 6) - 1)]);
        v >>= 6;
    }

    buf.reverse();
    String::from_utf8(buf).unwrap()
}

#[test]
fn test_inttobase64() {
    assert_eq!(&inttobase64(16, 3), "AAQ");
    assert_eq!(&inttobase64(80, 3), "ABQ");
    assert_eq!(&inttobase64(4176, 3), "BBQ");
    assert_eq!(&inttobase64(21399, 3), "FOX");
    assert_eq!(&inttobase64(91397, 3), "WUF");
}

#[test]
fn test_ceiling_division() {
    assert_eq!(ceiling_division(499, 500), 1);
    assert_eq!(ceiling_division(500, 500), 1);
    assert_eq!(ceiling_division(501, 500), 2);
}

#[test]
fn test_unsplit_string() {
    let my_argv: Vec<Vec<u8>> = vec![
        format!("A").into_bytes(),
        format!("BB").into_bytes(),
        format!("TEST").into_bytes(),
        format!("SOMETING").into_bytes(),
        format!("FDSAADFS").into_bytes(),
        format!("FADS").into_bytes(),
        format!("ASDf").into_bytes(),
    ];

    let new_unsplit = unsplit_string(&my_argv, 7, 3, 3);
    assert_eq!(&new_unsplit, b"SOMETING FDSAADFS FADS ");
    assert_eq!(new_unsplit.len(), 23);

    let my_argv: Vec<Vec<u8>> = vec![
        format!("B").into_bytes(),
        format!("#channel").into_bytes(),
        format!("9999999999").into_bytes(),
        format!("+stnzl").into_bytes(),
        format!("554").into_bytes(),
        format!("AAAAA:o,AAAAB,AAAAC").into_bytes(),
    ];

    let new_unsplit = unsplit_string(&my_argv, 6, 3, 2);
    assert_eq!(&new_unsplit, b"+stnzl 554 ");
    assert_eq!(new_unsplit.len(), 11);
}

#[test]
fn test_split_string() {
    let s = split_string(&format!("+ntl 34").into_bytes());
    assert_eq!(s.len(), 2);
    assert_eq!(s[0], b"+ntl");
    assert_eq!(s[1], b"34");

    let s = split_string(&format!("fdas fasd adsf asfd dfas").into_bytes());
    assert_eq!(s.len(), 5);
    assert_eq!(s[0], b"fdas");
    assert_eq!(s[1], b"fasd");
    assert_eq!(s[2], b"adsf");
    assert_eq!(s[3], b"asfd");
    assert_eq!(s[4], b"dfas");

    let s = split_string(&format!("aaaaaaa").into_bytes());
    assert_eq!(s.len(), 1);
    assert_eq!(s[0], b"aaaaaaa");

    let s = split_string(&format!("").into_bytes());
    assert_eq!(s.len(), 0);
}

#[test]
fn test_u8_slice_to_lower() {
    let caps = &String::from("THIS IS IN ALL CAPS").into_bytes();
    let lowered = u8_slice_to_lower(&caps);
    assert_eq!(lowered.len(), 19);
    assert_eq!(lowered, b"this is in all caps");
}

#[test]
fn test_trim_bytes_right() {
    let mystr: &[u8] = &String::from("This has newlines and a carriage return\r\n").into_bytes();
    let clean = trim_bytes_right(mystr);
    assert_eq!(clean.len(), 39);
    assert_eq!(clean[38], b'n');
}
<|endoftext|>


<commit_before>mod progress;

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

const REVISION: &str = "2c462a2f776b899d46743b1b44eda976e846e61d";

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

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    assert!(path_string.starts_with("tests/rust/src/"));
    let path = &path_string["tests/rust/src/".len()..];

    // TODO assert that parsing fails on the parse-fail cases
    if path.starts_with("test/parse-fail")
        || path.starts_with("test/compile-fail")
        || path.starts_with("test/rustfix")
    {
        return false;
    }

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

    match path {
        // TODO: or-patterns patterns: `Some(1 | 8)`
        "test/mir-opt/exponential-or.rs" |
        "test/ui/or-patterns/basic-switch.rs" |
        "test/ui/or-patterns/basic-switchint.rs" |
        "test/ui/or-patterns/bindings-runpass-1.rs" |
        "test/ui/or-patterns/bindings-runpass-2.rs" |
        "test/ui/or-patterns/consistent-bindings.rs" |
        "test/ui/or-patterns/exhaustiveness-pass.rs" |
        "test/ui/or-patterns/for-loop.rs" |
        "test/ui/or-patterns/if-let-while-let.rs" |
        "test/ui/or-patterns/issue-67514-irrefutable-param.rs" |
        "test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs" |
        "test/ui/or-patterns/let-pattern.rs" |
        "test/ui/or-patterns/mix-with-wild.rs" |
        "test/ui/or-patterns/or-patterns-default-binding-modes.rs" |
        "test/ui/or-patterns/or-patterns-syntactic-pass.rs" |
        "test/ui/or-patterns/search-via-bindings.rs" |
        "test/ui/or-patterns/struct-like.rs" |

        // TODO: inner attr in traits: `trait Foo { #![...] }`
        "test/pretty/trait-inner-attr.rs" |
        "test/ui/parser/inner-attr-in-trait-def.rs" |

        // TODO: const underscore in traits: `trait A { const _: (); }`
        "test/ui/parser/assoc-const-underscore-syntactic-pass.rs" |

        // TODO: top level fn without body: `fn f();`
        "test/ui/parser/fn-body-optional-syntactic-pass.rs" |
        "test/ui/parser/fn-header-syntactic-pass.rs" |

        // TODO: extern static with value: `extern { static X: u8 = 0; }`
        "test/ui/parser/foreign-static-syntactic-pass.rs" |

        // TODO: extern type with bound: `extern { type A: Ord; }`
        "test/ui/parser/foreign-ty-syntactic-pass.rs" |

        // TODO: top level const/static without value: `const X: u8;`
        "test/ui/parser/item-free-const-no-body-syntactic-pass.rs" |
        "test/ui/parser/item-free-static-no-body-syntactic-pass.rs" |

        // TODO: mut receiver in fn pointer type: `fn(mut self)`
        "test/ui/parser/self-param-syntactic-pass.rs" |

        // TODO: const trait impls and bounds
        "test/ui/rfc-2632-const-trait-impl/assoc-type.rs" |
        "test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs" |
        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs" |
        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs" |
        "test/ui/rfc-2632-const-trait-impl/feature-gate.rs" |
        "test/ui/rfc-2632-const-trait-impl/generic-bound.rs" |
        "test/ui/rfc-2632-const-trait-impl/syntax.rs" |

        // Deprecated placement syntax
        "test/ui/obsolete-in-place/bad.rs" |

        // Deprecated anonymous parameter syntax in traits
        "test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
        "test/ui/issues/issue-13105.rs" |
        "test/ui/issues/issue-13775.rs" |
        "test/ui/issues/issue-34074.rs" |

        // 2015-style dyn that libsyntax rejects
        "test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |

        // not actually test cases
        "test/rustdoc-ui/test-compile-fail2.rs" |
        "test/rustdoc-ui/test-compile-fail3.rs" |
        "test/ui/include-single-expr-helper.rs" |
        "test/ui/include-single-expr-helper-1.rs" |
        "test/ui/issues/auxiliary/issue-21146-inc.rs" |
        "test/ui/macros/auxiliary/macro-comma-support.rs" |
        "test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,

        _ => true,
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
}

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

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

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
        if common::travis_ci() {
            // Something about this makes the travis build not deadlock...
            errorf!(".");
        }
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Add syntax issue links<commit_after>mod progress;

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

const REVISION: &str = "2c462a2f776b899d46743b1b44eda976e846e61d";

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

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    assert!(path_string.starts_with("tests/rust/src/"));
    let path = &path_string["tests/rust/src/".len()..];

    // TODO assert that parsing fails on the parse-fail cases
    if path.starts_with("test/parse-fail")
        || path.starts_with("test/compile-fail")
        || path.starts_with("test/rustfix")
    {
        return false;
    }

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

    match path {
        // TODO: or-patterns patterns: `Some(1 | 8)`
        // https://github.com/dtolnay/syn/issues/758
        "test/mir-opt/exponential-or.rs" |
        "test/ui/or-patterns/basic-switch.rs" |
        "test/ui/or-patterns/basic-switchint.rs" |
        "test/ui/or-patterns/bindings-runpass-1.rs" |
        "test/ui/or-patterns/bindings-runpass-2.rs" |
        "test/ui/or-patterns/consistent-bindings.rs" |
        "test/ui/or-patterns/exhaustiveness-pass.rs" |
        "test/ui/or-patterns/for-loop.rs" |
        "test/ui/or-patterns/if-let-while-let.rs" |
        "test/ui/or-patterns/issue-67514-irrefutable-param.rs" |
        "test/ui/or-patterns/issue-68785-irrefutable-param-with-at.rs" |
        "test/ui/or-patterns/let-pattern.rs" |
        "test/ui/or-patterns/mix-with-wild.rs" |
        "test/ui/or-patterns/or-patterns-default-binding-modes.rs" |
        "test/ui/or-patterns/or-patterns-syntactic-pass.rs" |
        "test/ui/or-patterns/search-via-bindings.rs" |
        "test/ui/or-patterns/struct-like.rs" |

        // TODO: inner attr in traits: `trait Foo { #![...] }`
        // https://github.com/dtolnay/syn/issues/759
        "test/pretty/trait-inner-attr.rs" |
        "test/ui/parser/inner-attr-in-trait-def.rs" |

        // TODO: const underscore in traits: `trait A { const _: (); }`
        // https://github.com/dtolnay/syn/issues/760
        "test/ui/parser/assoc-const-underscore-syntactic-pass.rs" |

        // TODO: top level fn without body: `fn f();`
        // https://github.com/dtolnay/syn/issues/761
        "test/ui/parser/fn-body-optional-syntactic-pass.rs" |
        "test/ui/parser/fn-header-syntactic-pass.rs" |

        // TODO: extern static with value: `extern { static X: u8 = 0; }`
        // https://github.com/dtolnay/syn/issues/762
        "test/ui/parser/foreign-static-syntactic-pass.rs" |

        // TODO: extern type with bound: `extern { type A: Ord; }`
        // https://github.com/dtolnay/syn/issues/763
        "test/ui/parser/foreign-ty-syntactic-pass.rs" |

        // TODO: top level const/static without value: `const X: u8;`
        // https://github.com/dtolnay/syn/issues/764
        "test/ui/parser/item-free-const-no-body-syntactic-pass.rs" |
        "test/ui/parser/item-free-static-no-body-syntactic-pass.rs" |

        // TODO: mut receiver in fn pointer type: `fn(mut self)`
        // https://github.com/dtolnay/syn/issues/765
        "test/ui/parser/self-param-syntactic-pass.rs" |

        // TODO: const trait impls and bounds
        // https://github.com/dtolnay/syn/issues/766
        // https://github.com/dtolnay/syn/issues/767
        "test/ui/rfc-2632-const-trait-impl/assoc-type.rs" |
        "test/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs" |
        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs" |
        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs" |
        "test/ui/rfc-2632-const-trait-impl/feature-gate.rs" |
        "test/ui/rfc-2632-const-trait-impl/generic-bound.rs" |
        "test/ui/rfc-2632-const-trait-impl/syntax.rs" |

        // Deprecated placement syntax
        "test/ui/obsolete-in-place/bad.rs" |

        // Deprecated anonymous parameter syntax in traits
        "test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
        "test/ui/issues/issue-13105.rs" |
        "test/ui/issues/issue-13775.rs" |
        "test/ui/issues/issue-34074.rs" |

        // 2015-style dyn that libsyntax rejects
        "test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |

        // not actually test cases
        "test/rustdoc-ui/test-compile-fail2.rs" |
        "test/rustdoc-ui/test-compile-fail3.rs" |
        "test/ui/include-single-expr-helper.rs" |
        "test/ui/include-single-expr-helper-1.rs" |
        "test/ui/issues/auxiliary/issue-21146-inc.rs" |
        "test/ui/macros/auxiliary/macro-comma-support.rs" |
        "test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,

        _ => true,
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
}

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

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

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
        if common::travis_ci() {
            // Something about this makes the travis build not deadlock...
            errorf!(".");
        }
    }

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


<commit_before>// Translated from C to Rust. The original C code can be found at
// https://github.com/ulfjack/ryu and carries the following license:
//
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
//    (See accompanying file LICENSE-Apache or copy at
//     http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE-Boost or copy at
//     https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.

#![allow(dead_code)]

#[path = "../src/common.rs"]
mod common;

#[path = "../src/d2s_full_table.rs"]
mod d2s_full_table;

#[path = "../src/d2s_intrinsics.rs"]
mod d2s_intrinsics;

#[path = "../src/d2s_small_table.rs"]
mod d2s_small_table;

#[path = "../src/d2s.rs"]
mod d2s;

#[path = "../src/s2d.rs"]
mod s2d;

use s2d::{s2d, Error};

impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        *self as u8 == *other as u8
    }
}

#[test]
fn test_bad_input() {
    assert_eq!(Error::MalformedInput, s2d(b"x").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1..1").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"..").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1..1").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1ee1").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1e.1").unwrap_err());
    assert_eq!(Error::InputTooShort, s2d(b"").unwrap_err());
    assert_eq!(Error::InputTooLong, s2d(b"123456789012345678").unwrap_err());
    assert_eq!(Error::InputTooLong, s2d(b"1e12345").unwrap_err());
}

#[test]
fn test_basic() {
    assert_eq!(0.0, s2d(b"0").unwrap());
    assert_eq!(-0.0, s2d(b"-0").unwrap());
    assert_eq!(1.0, s2d(b"1").unwrap());
    assert_eq!(2.0, s2d(b"2").unwrap());
    assert_eq!(123456789.0, s2d(b"123456789").unwrap());
    assert_eq!(123.456, s2d(b"123.456").unwrap());
    assert_eq!(123.456, s2d(b"123456e-3").unwrap());
    assert_eq!(123.456, s2d(b"1234.56e-1").unwrap());
    assert_eq!(1.453, s2d(b"1.453").unwrap());
    assert_eq!(1453.0, s2d(b"1.453e+3").unwrap());
    assert_eq!(0.0, s2d(b".0").unwrap());
    assert_eq!(1.0, s2d(b"1e0").unwrap());
    assert_eq!(1.0, s2d(b"1E0").unwrap());
    assert_eq!(1.0, s2d(b"000001.000000").unwrap());
}

#[test]
fn test_min_max() {
    assert_eq!(
        1.7976931348623157e308,
        s2d(b"1.7976931348623157e308").unwrap(),
    );
    assert_eq!(5E-324, s2d(b"5E-324").unwrap());
}

#[test]
fn test_mantissa_rounding_overflow() {
    // This results in binary mantissa that is all ones and requires rounding up
    // because it is closer to 1 than to the next smaller float. This is a
    // regression test that the mantissa overflow is handled correctly by
    // increasing the exponent.
    assert_eq!(1.0, s2d(b"0.99999999999999999").unwrap());
    // This number overflows the mantissa *and* the IEEE exponent.
    assert_eq!(f64::INFINITY, s2d(b"1.7976931348623159e308").unwrap());
}

#[test]
fn test_underflow() {
    assert_eq!(0.0, s2d(b"2.4e-324").unwrap());
    assert_eq!(0.0, s2d(b"1e-324").unwrap());
    assert_eq!(0.0, s2d(b"9.99999e-325").unwrap());
    // These are just about halfway between 0 and the smallest float.
    // The first is just below the halfway point, the second just above.
    assert_eq!(0.0, s2d(b"2.4703282292062327e-324").unwrap());
    assert_eq!(5e-324, s2d(b"2.4703282292062328e-324").unwrap());
}

#[test]
fn test_overflow() {
    assert_eq!(f64::INFINITY, s2d(b"2e308").unwrap());
    assert_eq!(f64::INFINITY, s2d(b"1e309").unwrap());
}

#[test]
fn test_table_size_denormal() {
    assert_eq!(5e-324, s2d(b"4.9406564584124654e-324").unwrap());
}

#[test]
fn test_issue157() {
    assert_eq!(
        1.2999999999999999E+154,
        s2d(b"1.2999999999999999E+154").unwrap(),
    );
}
<commit_msg>Require d2s full tables for s2d<commit_after>// Translated from C to Rust. The original C code can be found at
// https://github.com/ulfjack/ryu and carries the following license:
//
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
//    (See accompanying file LICENSE-Apache or copy at
//     http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE-Boost or copy at
//     https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.

#![cfg(not(feature = "small"))]
#![allow(dead_code)]

#[path = "../src/common.rs"]
mod common;

#[path = "../src/d2s_full_table.rs"]
mod d2s_full_table;

#[path = "../src/d2s_intrinsics.rs"]
mod d2s_intrinsics;

#[path = "../src/d2s_small_table.rs"]
mod d2s_small_table;

#[path = "../src/d2s.rs"]
mod d2s;

#[path = "../src/s2d.rs"]
mod s2d;

use s2d::{s2d, Error};

impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        *self as u8 == *other as u8
    }
}

#[test]
fn test_bad_input() {
    assert_eq!(Error::MalformedInput, s2d(b"x").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1..1").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"..").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1..1").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1ee1").unwrap_err());
    assert_eq!(Error::MalformedInput, s2d(b"1e.1").unwrap_err());
    assert_eq!(Error::InputTooShort, s2d(b"").unwrap_err());
    assert_eq!(Error::InputTooLong, s2d(b"123456789012345678").unwrap_err());
    assert_eq!(Error::InputTooLong, s2d(b"1e12345").unwrap_err());
}

#[test]
fn test_basic() {
    assert_eq!(0.0, s2d(b"0").unwrap());
    assert_eq!(-0.0, s2d(b"-0").unwrap());
    assert_eq!(1.0, s2d(b"1").unwrap());
    assert_eq!(2.0, s2d(b"2").unwrap());
    assert_eq!(123456789.0, s2d(b"123456789").unwrap());
    assert_eq!(123.456, s2d(b"123.456").unwrap());
    assert_eq!(123.456, s2d(b"123456e-3").unwrap());
    assert_eq!(123.456, s2d(b"1234.56e-1").unwrap());
    assert_eq!(1.453, s2d(b"1.453").unwrap());
    assert_eq!(1453.0, s2d(b"1.453e+3").unwrap());
    assert_eq!(0.0, s2d(b".0").unwrap());
    assert_eq!(1.0, s2d(b"1e0").unwrap());
    assert_eq!(1.0, s2d(b"1E0").unwrap());
    assert_eq!(1.0, s2d(b"000001.000000").unwrap());
}

#[test]
fn test_min_max() {
    assert_eq!(
        1.7976931348623157e308,
        s2d(b"1.7976931348623157e308").unwrap(),
    );
    assert_eq!(5E-324, s2d(b"5E-324").unwrap());
}

#[test]
fn test_mantissa_rounding_overflow() {
    // This results in binary mantissa that is all ones and requires rounding up
    // because it is closer to 1 than to the next smaller float. This is a
    // regression test that the mantissa overflow is handled correctly by
    // increasing the exponent.
    assert_eq!(1.0, s2d(b"0.99999999999999999").unwrap());
    // This number overflows the mantissa *and* the IEEE exponent.
    assert_eq!(f64::INFINITY, s2d(b"1.7976931348623159e308").unwrap());
}

#[test]
fn test_underflow() {
    assert_eq!(0.0, s2d(b"2.4e-324").unwrap());
    assert_eq!(0.0, s2d(b"1e-324").unwrap());
    assert_eq!(0.0, s2d(b"9.99999e-325").unwrap());
    // These are just about halfway between 0 and the smallest float.
    // The first is just below the halfway point, the second just above.
    assert_eq!(0.0, s2d(b"2.4703282292062327e-324").unwrap());
    assert_eq!(5e-324, s2d(b"2.4703282292062328e-324").unwrap());
}

#[test]
fn test_overflow() {
    assert_eq!(f64::INFINITY, s2d(b"2e308").unwrap());
    assert_eq!(f64::INFINITY, s2d(b"1e309").unwrap());
}

#[test]
fn test_table_size_denormal() {
    assert_eq!(5e-324, s2d(b"4.9406564584124654e-324").unwrap());
}

#[test]
fn test_issue157() {
    assert_eq!(
        1.2999999999999999E+154,
        s2d(b"1.2999999999999999E+154").unwrap(),
    );
}
<|endoftext|>


<commit_before>extern crate simple_units;

use simple_units::si_units::*;

#[test]
fn test_meter_add() {
    assert_eq!(Meter(2.3) + Meter(7.5), Meter(9.8));
}

#[test]
fn test_meter_sub() {
    assert_eq!(Meter(8.1) - Meter(5.6), Meter(2.5));
}

#[test]
fn test_meter_mul_meter() {
    assert_eq!(Meter(2.5) * Meter(4.0), Meter2(10.0));
}

#[test]
fn test_meter_mul_number() {
    assert_eq!(Meter(3.0) * 4.0, Meter(12.0));
}

#[test]
fn test_number_mul_meter() {
    assert_eq!(4.0 * Meter(3.0), Meter(12.0));
}

#[test]
fn test_meter_div_meter() {
    assert_eq!(Meter(10.0) / Meter(5.0), 2.0);
}

#[test]
fn test_meter_div_number() {
    assert_eq!(Meter(10.0) / 2.0, Meter(5.0));
}

#[test]
fn meter2_div_meter() {
    assert_eq!(Meter2(12.0) / Meter(3.0), Meter(4.0));
}

#[test]
fn meter3_div_meter() {
    assert_eq!(Meter3(18.0) / Meter(3.0), Meter2(6.0));
}

#[test]
fn meter3_div_meter2() {
    assert_eq!(Meter3(24.0) / Meter2(4.0), Meter(6.0));
}

#[test]
fn test_meter_div_second() {
    assert_eq!(Meter(15.0) / Second(3.0), MeterPerSecond(5.0));
}

#[test]
fn test_meter_per_second_mul_second() {
    assert_eq!(MeterPerSecond(3.0) * Second(2.0), Meter(6.0));
}

#[test]
fn test_meter_div_meter_per_second() {
    assert_eq!(Meter(10.0) / MeterPerSecond(2.0), Second(5.0));
}
<commit_msg>add more tests<commit_after>extern crate simple_units;

use simple_units::si_units::*;

#[test]
fn test_meter_add() {
    assert_eq!(Meter(2.3) + Meter(7.5), Meter(9.8));
}

#[test]
fn test_meter_sub() {
    assert_eq!(Meter(8.1) - Meter(5.6), Meter(2.5));
}

#[test]
fn test_meter_mul_number() {
    assert_eq!(Meter(3.0) * 4.0, Meter(12.0));
}

#[test]
fn test_number_mul_meter() {
    assert_eq!(4.0 * Meter(3.0), Meter(12.0));
}

#[test]
fn test_meter_div_meter() {
    assert_eq!(Meter(10.0) / Meter(5.0), 2.0);
}

#[test]
fn test_meter_div_number() {
    assert_eq!(Meter(10.0) / 2.0, Meter(5.0));
}

#[test]
fn test_inverse() {
    assert_eq!(Meter(2.4) * PerMeter(2.0), 4.8);
    assert_eq!(5.0 / Meter(2.0), PerMeter(2.5));
}

#[test]
fn test_power3() {
    assert_eq!(Meter(2.5) * Meter(2.0), Meter2(5.0));
    assert_eq!(Meter2(1.5) * Meter(2.0), Meter3(3.0));
    assert_eq!(Meter(1.5) * Meter2(2.0), Meter3(3.0));

    assert_eq!(Meter2(9.0) / Meter(3.0), Meter(3.0));
    assert_eq!(Meter3(15.0) / Meter(2.0), Meter2(7.5));
    assert_eq!(Meter3(8.4) / Meter2(2.0), Meter(4.2));

    assert_eq!(PerMeter(2.5) * PerMeter(2.0), PerMeter2(5.0));
    assert_eq!(PerMeter2(1.5) * PerMeter(2.0), PerMeter3(3.0));
    assert_eq!(PerMeter(1.5) * PerMeter2(2.0), PerMeter3(3.0));

}

#[test]
fn test_meter_div_second() {
    assert_eq!(Meter(15.0) / Second(3.0), MeterPerSecond(5.0));
}

#[test]
fn test_meter_per_second_mul_second() {
    assert_eq!(MeterPerSecond(3.0) * Second(2.0), Meter(6.0));
}

#[test]
fn test_meter_div_meter_per_second() {
    assert_eq!(Meter(10.0) / MeterPerSecond(2.0), Second(5.0));
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Write;

use ppu::*;

#[cfg(test)]
mod spec_tests;

const ADDRESSABLE_MEMORY: usize = 65536;

pub trait Memory {
    fn store(&mut self, u16, u8);
    fn store16(&mut self, u16, u16);
    fn load(&self, u16) -> u8;
    fn load16(&self, u16) -> u16;

    // probably a premature optimization, but we implement inc and dec here so
    // that we can alter the values in place.
    fn inc(&mut self, u16) -> u8;
    fn dec(&mut self, u16) -> u8;
    fn dump(&self, file: &'static str);
}

pub struct NesMemory {
    ram: [u8; 2048],
    ppu: Ppu,
}

impl NesMemory {
    pub fn new() -> Self {
        NesMemory {
            ppu: Ppu::new(),
            ram: [0; 2048],
        }
    }
}

impl Memory for NesMemory {
    fn store(&mut self, addr: u16, data: u8) {
        match addr {
            0x0...0x1fff => {
                // 2kb ram, mirrored 4 times
                let addr = addr as usize;
                self.ram[addr & 0x7ff] = data;
            }
            0x2000...0x3fff => {
                // PPU registers, mirrored every 8 bytes
                self.ppu.write_reg(addr, data);
            }
            0x4000...0x4013 => {
                // APU Registers
                unimplemented!();
            }
            0x4014 => {
                // Sprite DMA W
                unimplemented!();
            }
            0x4015 => {
                // Sound Status RW
                unimplemented!();
            }
            0x4016 => {
                // Input 1 RW
                unimplemented!();
            }
            0x4017 => {
                // Input 2, Frame Counter RW
                unimplemented!();
            }
            0x4018...0x401f => {
                // ??
                unimplemented!();
            }
            0x4020...0xffff => {
                // mapped to cartridge
                unimplemented!();
            }
            _ => {
                panic!("Should never get here.");
            }
        }
    }

    fn store16(&mut self, addr: u16, data: u16) {
        unimplemented!();
    }

    fn load(&self, addr: u16) -> u8 {
        unimplemented!();
    }

    fn load16(&self, addr: u16) -> u16 {
        unimplemented!();
    }

    fn inc(&mut self, addr: u16) -> u8 {
        unimplemented!();
    }

    fn dec(&mut self, addr: u16) -> u8 {
        unimplemented!();
    }

    fn dump(&self, file_loc: &'static str) {
        unimplemented!();
    }
}

pub struct SimpleMemory {
    addr: [u8; ADDRESSABLE_MEMORY],
}

impl SimpleMemory {
    pub fn new() -> Self {
        SimpleMemory { addr: [0; ADDRESSABLE_MEMORY] }
    }

    pub fn store_many(&mut self, addr: u16, data: &[u8]) {
        for i in 0..data.len() {
            self.store(addr + i as u16, data[i]);
        }
    }
}

impl Memory for SimpleMemory {
    fn store(&mut self, addr: u16, data: u8) {
        let addr = addr as usize;
        self.addr[addr] = data;
    }

    fn store16(&mut self, addr: u16, data: u16) {
        let lowb = (data & 0xff) as u8;
        let highb = ((data >> 8) & 0xff) as u8;
        self.store(addr, lowb);
        self.store(addr + 1, highb);
    }

    fn load(&self, addr: u16) -> u8 {
        let addr = addr as usize;
        self.addr[addr]
    }

    fn load16(&self, addr: u16) -> u16 {
        let addr = addr as usize;
        self.addr[addr] as u16 | (self.addr[addr + 1] as u16) << 8
    }

    fn inc(&mut self, addr: u16) -> u8 {
        let addr = addr as usize;
        self.addr[addr] = (self.addr[addr] as u16 + 1) as u8;
        self.addr[addr]
    }

    fn dec(&mut self, addr: u16) -> u8 {
        let addr = addr as usize;
        self.addr[addr] = (self.addr[addr] as i16 - 1) as u8;
        self.addr[addr]
    }

    fn dump(&self, file_loc: &'static str) {
        let mut f = File::create(file_loc).unwrap();
        f.write_all(&self.addr).unwrap();
    }
}
<commit_msg>Add default implementations for memory<commit_after>use std::fs::File;
use std::io::Write;

use ppu::*;

#[cfg(test)]
mod spec_tests;

const ADDRESSABLE_MEMORY: usize = 65536;

pub trait Memory {
    fn store(&mut self, u16, u8);
    fn store16(&mut self, addr: u16, data: u16) {
        let lowb = (data & 0xff) as u8;
        let highb = ((data >> 8) & 0xff) as u8;
        self.store(addr, lowb);
        self.store(addr + 1, highb);
    }
    fn load(&self, u16) -> u8;
    fn load16(&self, addr: u16) -> u16 {
        self.load(addr) as u16 | (self.load(addr + 1) as u16) << 8
    }

    // probably a premature optimization, but we implement inc and dec here so
    // that we can alter the values in place.
    fn inc(&mut self, u16) -> u8;
    fn dec(&mut self, u16) -> u8;
    fn dump(&self, file: &'static str);
}

pub struct NesMemory {
    ram: [u8; 2048],
    ppu: Ppu,
}

impl NesMemory {
    pub fn new() -> Self {
        NesMemory {
            ppu: Ppu::new(),
            ram: [0; 2048],
        }
    }
}

impl Memory for NesMemory {
    fn store(&mut self, addr: u16, data: u8) {
        match addr {
            0x0...0x1fff => {
                // 2kb ram, mirrored 4 times
                let addr = addr as usize;
                self.ram[addr & 0x7ff] = data;
            }
            0x2000...0x3fff => {
                // PPU registers, mirrored every 8 bytes
                self.ppu.write_reg(addr, data);
            }
            0x4000...0x4013 => {
                // APU Registers
                unimplemented!();
            }
            0x4014 => {
                // Sprite DMA W
                unimplemented!();
            }
            0x4015 => {
                // Sound Status RW
                unimplemented!();
            }
            0x4016 => {
                // Input 1 RW
                unimplemented!();
            }
            0x4017 => {
                // Input 2, Frame Counter RW
                unimplemented!();
            }
            0x4018...0x401f => {
                // ??
                unimplemented!();
            }
            0x4020...0xffff => {
                // mapped to cartridge
                unimplemented!();
            }
            _ => {
                panic!("Should never get here.");
            }
        }
    }

    fn load(&self, addr: u16) -> u8 {
        unimplemented!();
    }

    fn load16(&self, addr: u16) -> u16 {
        unimplemented!();
    }

    fn inc(&mut self, addr: u16) -> u8 {
        unimplemented!();
    }

    fn dec(&mut self, addr: u16) -> u8 {
        unimplemented!();
    }

    fn dump(&self, file_loc: &'static str) {
        unimplemented!();
    }
}

pub struct SimpleMemory {
    addr: [u8; ADDRESSABLE_MEMORY],
}

impl SimpleMemory {
    pub fn new() -> Self {
        SimpleMemory { addr: [0; ADDRESSABLE_MEMORY] }
    }

    pub fn store_many(&mut self, addr: u16, data: &[u8]) {
        for i in 0..data.len() {
            self.store(addr + i as u16, data[i]);
        }
    }
}

impl Memory for SimpleMemory {
    fn store(&mut self, addr: u16, data: u8) {
        let addr = addr as usize;
        self.addr[addr] = data;
    }

    fn load(&self, addr: u16) -> u8 {
        let addr = addr as usize;
        self.addr[addr]
    }

    fn inc(&mut self, addr: u16) -> u8 {
        let addr = addr as usize;
        self.addr[addr] = (self.addr[addr] as u16 + 1) as u8;
        self.addr[addr]
    }

    fn dec(&mut self, addr: u16) -> u8 {
        let addr = addr as usize;
        self.addr[addr] = (self.addr[addr] as i16 - 1) as u8;
        self.addr[addr]
    }

    fn dump(&self, file_loc: &'static str) {
        let mut f = File::create(file_loc).unwrap();
        f.write_all(&self.addr).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::sync::Mutex;

macro_rules! cargo_script {
    ($($args:expr),* $(,)*) => {
        {
            extern crate tempdir;
            use std::process::Command;

            let cargo_lock = ::util::CARGO_MUTEX.lock().expect("could not acquire Cargo mutext");

            let temp_dir = tempdir::TempDir::new("cargo-script-test").unwrap();
            let cmd_str;
            let out = {
                let mut cmd = Command::new("target/debug/cargo-script");
                cmd.arg("script");
                cmd.arg("--pkg-path").arg(temp_dir.path());
                $(
                    cmd.arg($args);
                )*

                cmd_str = format!("{:?}", cmd);

                cmd.output()
                    .map(::util::Output::from)
            };

            if let Ok(out) = out.as_ref() {
                println!("cargo-script cmd: {}", cmd_str);
                println!("cargo-script stdout:");
                println!("-----");
                println!("{}", out.stdout);
                println!("-----");
                println!("cargo-script stderr:");
                println!("-----");
                println!("{}", out.stderr);
                println!("-----");
            }

            drop(temp_dir);
            drop(cargo_lock);

            out
        }
    };
}

macro_rules! with_output_marker {
    (prelude $p:expr; $e:expr) => {
        format!(concat!($p, "{}", $e), ::util::OUTPUT_MARKER_CODE)
    };

    ($e:expr) => {
        format!(concat!("{}", $e), ::util::OUTPUT_MARKER_CODE)
    };
}

lazy_static! {
    #[doc(hidden)]
    pub static ref CARGO_MUTEX: Mutex<()> = Mutex::new(());
}

pub const OUTPUT_MARKER: &'static str = "--output--";
pub const OUTPUT_MARKER_CODE: &'static str = "println!(\"--output--\");";

pub struct Output {
    pub status: ::std::process::ExitStatus,
    pub stdout: String,
    pub stderr: String,
}

impl Output {
    pub fn stdout_output(&self) -> &str {
        assert!(self.success());
        for marker in self.stdout.matches(OUTPUT_MARKER) {
            let i = subslice_offset(&self.stdout, marker)
                .expect("couldn't find marker in output");
            let before_cp = self.stdout[..i].chars().rev().next()
                .unwrap_or('\n');
            if !(before_cp == '\r' || before_cp == '\n') { continue; }
            let after = &self.stdout[i+OUTPUT_MARKER.len()..];
            let after_cp = after.chars().next()
                .expect("couldn't find cp after marker");
            if !(after_cp == '\r' || after_cp == '\n') { continue; }
            return after;
        }
        panic!("could not find `{}` in script output", OUTPUT_MARKER);
    }

    pub fn success(&self) -> bool {
        self.status.success()
    }
}

impl From<::std::process::Output> for Output {
    fn from(v: ::std::process::Output) -> Self {
        Output {
            status: v.status,
            stdout: String::from_utf8(v.stdout).unwrap(),
            stderr: String::from_utf8(v.stderr).unwrap(),
        }
    }
}

fn subslice_offset(outer: &str, inner: &str) -> Option<usize> {
    let outer_beg = outer.as_ptr() as usize;
    let inner = inner.as_ptr() as usize;
    if inner < outer_beg || inner > outer_beg.wrapping_add(outer.len()) {
        None
    } else {
        Some(inner.wrapping_sub(outer_beg))
    }
}
<commit_msg>Made test runner deal with CARGO_TARGET_DIR.<commit_after>use std::sync::Mutex;

macro_rules! cargo_script {
    ($($args:expr),* $(,)*) => {
        {
            extern crate tempdir;
            use std::process::Command;

            let cargo_lock = ::util::CARGO_MUTEX.lock().expect("could not acquire Cargo mutext");

            let temp_dir = tempdir::TempDir::new("cargo-script-test").unwrap();
            let cmd_str;
            let out = {
                let target_dir = ::std::env::var("CARGO_TARGET_DIR")
                    .unwrap_or_else(|_| String::from("target"));
                let mut cmd = Command::new(format!("{}/debug/cargo-script", target_dir));
                cmd.arg("script");
                cmd.arg("--pkg-path").arg(temp_dir.path());
                $(
                    cmd.arg($args);
                )*

                cmd.env_remove("CARGO_TARGET_DIR");

                cmd_str = format!("{:?}", cmd);

                cmd.output()
                    .map(::util::Output::from)
            };

            if let Ok(out) = out.as_ref() {
                println!("cargo-script cmd: {}", cmd_str);
                println!("cargo-script stdout:");
                println!("-----");
                println!("{}", out.stdout);
                println!("-----");
                println!("cargo-script stderr:");
                println!("-----");
                println!("{}", out.stderr);
                println!("-----");
            }

            drop(temp_dir);
            drop(cargo_lock);

            out
        }
    };
}

macro_rules! with_output_marker {
    (prelude $p:expr; $e:expr) => {
        format!(concat!($p, "{}", $e), ::util::OUTPUT_MARKER_CODE)
    };

    ($e:expr) => {
        format!(concat!("{}", $e), ::util::OUTPUT_MARKER_CODE)
    };
}

lazy_static! {
    #[doc(hidden)]
    pub static ref CARGO_MUTEX: Mutex<()> = Mutex::new(());
}

pub const OUTPUT_MARKER: &'static str = "--output--";
pub const OUTPUT_MARKER_CODE: &'static str = "println!(\"--output--\");";

pub struct Output {
    pub status: ::std::process::ExitStatus,
    pub stdout: String,
    pub stderr: String,
}

impl Output {
    pub fn stdout_output(&self) -> &str {
        assert!(self.success());
        for marker in self.stdout.matches(OUTPUT_MARKER) {
            let i = subslice_offset(&self.stdout, marker)
                .expect("couldn't find marker in output");
            let before_cp = self.stdout[..i].chars().rev().next()
                .unwrap_or('\n');
            if !(before_cp == '\r' || before_cp == '\n') { continue; }
            let after = &self.stdout[i+OUTPUT_MARKER.len()..];
            let after_cp = after.chars().next()
                .expect("couldn't find cp after marker");
            if !(after_cp == '\r' || after_cp == '\n') { continue; }
            return after;
        }
        panic!("could not find `{}` in script output", OUTPUT_MARKER);
    }

    pub fn success(&self) -> bool {
        self.status.success()
    }
}

impl From<::std::process::Output> for Output {
    fn from(v: ::std::process::Output) -> Self {
        Output {
            status: v.status,
            stdout: String::from_utf8(v.stdout).unwrap(),
            stderr: String::from_utf8(v.stderr).unwrap(),
        }
    }
}

fn subslice_offset(outer: &str, inner: &str) -> Option<usize> {
    let outer_beg = outer.as_ptr() as usize;
    let inner = inner.as_ptr() as usize;
    if inner < outer_beg || inner > outer_beg.wrapping_add(outer.len()) {
        None
    } else {
        Some(inner.wrapping_sub(outer_beg))
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

extern crate png;
extern crate std;
extern crate test;
extern crate regex;

use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::os;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;

enum RenderMode {
  CpuRendering = 1,
  GpuRendering = 2,
}

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, manifest, testname) = match harness_args {
      [] | [_] => fail!("USAGE: cpu|gpu manifest [testname regex]"),
      [ref render_mode_string, ref manifest] => (render_mode_string, manifest, None),
      [ref render_mode_string, ref manifest, ref testname, ..] => (render_mode_string, manifest, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let render_mode = match render_mode_string.as_slice() {
      "cpu" => CpuRendering,
      "gpu" => GpuRendering,
      _ => fail!("First argument must specify cpu or gpu as rendering mode")
    };

    let tests = parse_lists(manifest, servo_args, render_mode);
    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [String, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    flakiness: uint,
}

struct TestLine<'a> {
  conditions: &'a str,
  kind: &'a str,
  file_left: &'a str,
  file_right: &'a str,
}

fn parse_lists(file: &String, servo_args: &[String], render_mode: RenderMode) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let mut next_id = 0;
    let file_path = Path::new(file.clone());
    let contents = File::open_mode(&file_path, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
       // ignore comments or empty lines
       if line.starts_with("#") || line.is_empty() {
          continue;
       }

       let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

       let test_line = match parts.len() {
          3 => {
            TestLine {
              conditions: "",
              kind: parts[0],
              file_left: parts[1],
              file_right: parts[2],
            }
          },
          4 => {
            TestLine {
              conditions: parts[0],
              kind: parts[1],
              file_left: parts[2],
              file_right: parts[3],
            }
          },
          _ => {
            fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line);
          }
       };

       let kind = match test_line.kind {
          "==" => Same,
          "!=" => Different,
          part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
       };
       let src_path = file_path.dir_path();
       let src_dir = src_path.display().to_string();
       let file_left =  src_dir.clone().append("/").append(test_line.file_left);
       let file_right = src_dir.append("/").append(test_line.file_right);

       let mut conditions_list = test_line.conditions.split(',');
       let mut flakiness = 0;
       for condition in conditions_list {
          match condition {
            "flaky_cpu" => {
              flakiness |= CpuRendering as uint;
            },
            "flaky_gpu" => {
              flakiness |= GpuRendering as uint;
            },
            _ => {}
          }
       }

       let reftest = Reftest {
         name: test_line.file_left.to_string().append(" / ").append(test_line.file_right),
         kind: kind,
         files: [file_left, file_right],
         id: next_id,
         render_mode: render_mode,
         servo_args: servo_args.iter().map(|x| x.clone()).collect(),
         flakiness: flakiness,
       };

       next_id += 1;

       tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> png::Image {
    let filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut args = reftest.servo_args.clone();
    match reftest.render_mode {
      CpuRendering => args.push("-c".to_string()),
      _ => {}   // GPU rendering is the default
    }
    args.push_all_move(vec!("-f".to_string(), "-o".to_string(), filename.clone(), reftest.files[side].clone()));

    let retval = match Command::new("./servo").args(args.as_slice()).status() {
        Ok(status) => status,
        Err(e) => fail!("failed to execute process: {}", e),
    };
    assert!(retval == ExitStatus(0));

    png::load_png(&from_str::<Path>(filename.as_slice()).unwrap()).unwrap()
}

fn check_reftest(reftest: Reftest) {
    let left  = capture(&reftest, 0);
    let right = capture(&reftest, 1);

    let pixels: Vec<u8> = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| {
            if a as i8 - b as i8 == 0 {
                // White for correct
                0xFF
            } else {
                // "1100" in the RGBA channel with an error for an incorrect value
                // This results in some number of C0 and FFs, which is much more
                // readable (and distinguishable) than the previous difference-wise
                // scaling but does not require reconstructing the actual RGBA pixel.
                0xC0
            }
        }).collect();

    let test_is_flaky = (reftest.render_mode as uint & reftest.flakiness) != 0;

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left.width,
            height: left.height,
            color_type: png::RGBA8,
            pixels: pixels,
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, test_is_flaky) {
          (Same, true) => println!("flaky test - rendering difference: {}", output_str),
          (Same, false) => fail!("rendering difference: {}", output_str),
          (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(test_is_flaky || reftest.kind == Same);
    }
}
<commit_msg>Cleanup formatting in reftest.rs.<commit_after>// Copyright 2013 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.

extern crate png;
extern crate std;
extern crate test;
extern crate regex;

use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::os;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;

enum RenderMode {
  CpuRendering = 1,
  GpuRendering = 2,
}

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, manifest, testname) = match harness_args {
        [] | [_] => fail!("USAGE: cpu|gpu manifest [testname regex]"),
        [ref render_mode_string, ref manifest] => (render_mode_string, manifest, None),
        [ref render_mode_string, ref manifest, ref testname, ..] => (render_mode_string, manifest, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let render_mode = match render_mode_string.as_slice() {
        "cpu" => CpuRendering,
        "gpu" => GpuRendering,
        _ => fail!("First argument must specify cpu or gpu as rendering mode")
    };

    let tests = parse_lists(manifest, servo_args, render_mode);
    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [String, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    flakiness: uint,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &String, servo_args: &[String], render_mode: RenderMode) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let mut next_id = 0;
    let file_path = Path::new(file.clone());
    let contents = File::open_mode(&file_path, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => fail!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => fail!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };
        let src_path = file_path.dir_path();
        let src_dir = src_path.display().to_string();
        let file_left =  src_dir.clone().append("/").append(test_line.file_left);
        let file_right = src_dir.append("/").append(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = 0;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness |= CpuRendering as uint,
                "flaky_gpu" => flakiness |= GpuRendering as uint,
                _ => (),
            }
        }

        let reftest = Reftest {
            name: test_line.file_left.to_string().append(" / ").append(test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: next_id,
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            flakiness: flakiness,
        };

        next_id += 1;

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> png::Image {
    let filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut args = reftest.servo_args.clone();
    match reftest.render_mode {
        CpuRendering => args.push("-c".to_string()),
        _ => {}   // GPU rendering is the default
    }
    args.push_all_move(vec!("-f".to_string(), "-o".to_string(), filename.clone(), reftest.files[side].clone()));

    let retval = match Command::new("./servo").args(args.as_slice()).status() {
        Ok(status) => status,
        Err(e) => fail!("failed to execute process: {}", e),
    };
    assert!(retval == ExitStatus(0));

    png::load_png(&from_str::<Path>(filename.as_slice()).unwrap()).unwrap()
}

fn check_reftest(reftest: Reftest) {
    let left  = capture(&reftest, 0);
    let right = capture(&reftest, 1);

    let pixels = left.pixels.iter().zip(right.pixels.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    let test_is_flaky = (reftest.render_mode as uint & reftest.flakiness) != 0;

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left.width,
            height: left.height,
            color_type: png::RGBA8,
            pixels: pixels,
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, test_is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => fail!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(test_is_flaky || reftest.kind == Same);
    }
}
<|endoftext|>