<commit_before>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate serialize;

use libc::{c_void, c_char, c_int, c_ulong, c_float};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type RawGeoIp = *const c_void;
type In6Addr = [u8, ..16];

#[repr(C)]
struct GeoIpLookup {
    netmask: c_int
}

impl GeoIpLookup {
    fn new() -> GeoIpLookup {
        GeoIpLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *const c_char, flags: c_int) -> RawGeoIp;
    fn GeoIP_delete(db: RawGeoIp);
    fn GeoIP_name_by_ipnum_gl(db: RawGeoIp, ipnum: c_ulong, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: RawGeoIp, ipnum: In6Addr, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_record_by_ipnum(db: RawGeoIp, ipnum: c_ulong) -> *const GeoIpRecord;
    fn GeoIP_record_by_ipnum_v6(db: RawGeoIp, ipnum: In6Addr) -> *const GeoIpRecord;
    fn GeoIPRecord_delete(gir: *const GeoIpRecord);
    fn GeoIP_set_charset(db: RawGeoIp, charset: c_int) -> c_int;
}

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIp {
    db: RawGeoIp
}

#[repr(C)]
pub struct GeoIpRecord {
    country_code: *const c_char,
    country_code3: *const c_char,
    country_name: *const c_char,
    region: *const c_char,
    city: *const c_char,
    postal_code: *const c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *const c_char,
    netmask: c_int
}

#[deriving(Decodable, Encodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[deriving(Decodable, Encodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4((a as c_ulong << 24) | (b as c_ulong << 16) |
                               (c as c_ulong << 8)  | (d as c_ulong))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().into_inner(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { GeoIP_set_charset(db, Charset::UTF8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<commit_msg>Add some Copy implementations<commit_after>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate serialize;

use libc::{c_void, c_char, c_int, c_ulong, c_float};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type RawGeoIp = *const c_void;
type In6Addr = [u8, ..16];

#[repr(C)]
struct GeoIpLookup {
    netmask: c_int
}

impl GeoIpLookup {
    fn new() -> GeoIpLookup {
        GeoIpLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *const c_char, flags: c_int) -> RawGeoIp;
    fn GeoIP_delete(db: RawGeoIp);
    fn GeoIP_name_by_ipnum_gl(db: RawGeoIp, ipnum: c_ulong, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: RawGeoIp, ipnum: In6Addr, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_record_by_ipnum(db: RawGeoIp, ipnum: c_ulong) -> *const GeoIpRecord;
    fn GeoIP_record_by_ipnum_v6(db: RawGeoIp, ipnum: In6Addr) -> *const GeoIpRecord;
    fn GeoIPRecord_delete(gir: *const GeoIpRecord);
    fn GeoIP_set_charset(db: RawGeoIp, charset: c_int) -> c_int;
}

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

impl Copy for Options { }

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

impl Copy for DBType { }

pub struct GeoIp {
    db: RawGeoIp
}

impl Copy for GeoIp { }

#[repr(C)]
pub struct GeoIpRecord {
    country_code: *const c_char,
    country_code3: *const c_char,
    country_name: *const c_char,
    region: *const c_char,
    city: *const c_char,
    postal_code: *const c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *const c_char,
    netmask: c_int
}

impl Copy for GeoIpRecord { }

#[deriving(Decodable, Encodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[deriving(Decodable, Encodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4((a as c_ulong << 24) | (b as c_ulong << 16) |
                               (c as c_ulong << 8)  | (d as c_ulong))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().into_inner(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { GeoIP_set_charset(db, Charset::UTF8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;
extern crate lwkt;
use lwkt::{Context, StackSource};

static mut ctx_slot: *mut Context<lwkt::os::Stack> = 0 as *mut Context<_>;

#[bench]
fn context_new(b: &mut test::Bencher) {
  b.iter(|| unsafe {
    let stack = lwkt::os::StackSource::get_stack(4 << 20);

    let mut ctx = Context::new(stack, move || {
      let ctx_ptr = ctx_slot;
      loop {
        (*ctx_ptr).swap()
      }
    });

    ctx_slot = &mut ctx;

    ctx.swap();
  })
}
<commit_msg>use a statically-allocated stack for Context::new benchmarking<commit_after>#![feature(test)]
extern crate test;
extern crate lwkt;
use lwkt::{Context, Stack};

static mut ctx_slot: *mut Context<SliceStack<'static>> = 0 as *mut Context<_>;
static mut stack_buf: [u8; 1024] = [0; 1024];

#[bench]
fn context_new(b: &mut test::Bencher) {
  b.iter(|| unsafe {
    let stack = SliceStack(&mut stack_buf);

    let mut ctx = Context::new(stack, move || {
      let ctx_ptr = ctx_slot;
      loop {
        (*ctx_ptr).swap()
      }
    });

    ctx_slot = &mut ctx;

    ctx.swap();
  })
}

struct SliceStack<'a>(&'a mut [u8]);
impl<'a> lwkt::Stack for SliceStack<'a> {
  fn top(&mut self) -> *mut u8 {
    unsafe {
      self.0.as_mut_ptr().offset(self.0.len() as isize)
    }
  }

  fn limit(&self) -> *const u8 {
    self.0.as_ptr()
  }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate conrod;
use conrod::{widget, Borderable, Colorable, Positionable, Sizeable, Widget};
use conrod::backend::glium::glium;
use conrod::backend::glium::glium::{DisplayBuild, Surface};

struct AppState {
	board: [[bool; 8]; 8],
}

impl AppState {
	fn new() -> AppState {
		AppState {
			board: [ [true, true, true, true, true, true, true, true],
						   [true, false, false, false, false, false, false, true],
						   [true, false, true, false, true, true, true, true],
						   [true, false, true, false, true, true, true, true],
						   [true, false, false, false, true, true, true, true],
						   [true, true, true, true, true, true, true, true],
						   [true, true, false, true, false, false, false, true],
						   [true, true, true, true, true, true, true, true] ],
		}
	}
}

fn main() {
	const WIDTH: u32 = 400;
	const HEIGHT: u32 = 400;

	let mut app_state = AppState::new();

	// Build the window.
	let display = glium::glutin::WindowBuilder::new()
		.with_vsync()
		.with_dimensions(WIDTH, HEIGHT)
		.with_title("Game of Life")
		.with_multisampling(4)
		.build_glium()
		.unwrap();

	// construct our `Ui`.
	let mut ui = conrod::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build();

	// Generate the widget identifiers.
	widget_ids!(struct Ids {
		title,
		info,
		board
	});
	let ids = Ids::new(ui.widget_id_generator());

	// Add a `Font` to the `Ui`'s `font::Map` from file.
	const FONT_PATH: &'static str =
		concat!(env!("CARGO_MANIFEST_DIR"), "/assets/fonts/NotoSans/NotoSans-Regular.ttf");
	ui.fonts.insert_from_file(FONT_PATH).unwrap();

	// A type used for converting `conrod::render::Primitives` into `Command`s that can be used
	// for drawing to the glium `Surface`.
	let mut renderer = conrod::backend::glium::Renderer::new(&display).unwrap();

	// The image map describing each of our widget->image mappings (in our case, none).
	let image_map = conrod::image::Map::<glium::texture::Texture2d>::new();

	// Poll events from the window.
	let mut last_update = std::time::Instant::now();
	let mut ui_needs_update = true;
	'main: loop {

		// We don't want to loop any faster than 60 FPS, so wait until it has been at least
		// 16ms since the last yield.
		let sixteen_ms = std::time::Duration::from_millis(16);
		let duration_since_last_update = std::time::Instant::now().duration_since(last_update);
		if duration_since_last_update < sixteen_ms {
			std::thread::sleep(sixteen_ms - duration_since_last_update);
		}

		// Collect all pending events.
		let mut events: Vec<_> = display.poll_events().collect();

		// If there are no events and the `Ui` does not need updating, wait for the next event.
		if events.is_empty() && !ui_needs_update {
			events.extend(display.wait_events().next());
		}

		// Reset the needs_update flag and time this update.
		ui_needs_update = false;
		last_update = std::time::Instant::now();

		// Handle all events.
		for event in events {

			// Use the `winit` backend feature to convert the winit event to a conrod one.
			if let Some(event) = conrod::backend::winit::convert(event.clone(), &display) {
				ui.handle_event(event);
				ui_needs_update = true;
			}

			match event {
				// Break from the loop upon `Escape`.
				glium::glutin::Event::KeyboardInput(_, _, Some(glium::glutin::VirtualKeyCode::Escape)) |
				glium::glutin::Event::Closed =>
					break 'main,
				_ => {},
			}
		}

		// Instantiate all widgets in the GUI.
		{
			let ui = &mut ui.set_widgets();

			// "Hello World!" in the middle of the screen.
			widget::Text::new("Game of Life")
				.mid_top_of(ui.window)
				.color(conrod::color::WHITE)
				.font_size(32)
				.set(ids.title, ui);
			widget::Text::new("Start")
				.down(5.0)
				.color(conrod::color::WHITE)
				.font_size(8)
				.set(ids.info, ui);

			// A demonstration using widget_matrix to easily draw a matrix of any kind of widget.
			let (cols, rows) = (8, 8);
			let mut elements = widget::Matrix::new(cols, rows)
				.down(20.0)
				.w_h(260.0, 260.0)
				.set(ids.board, ui);

			// The `Matrix` widget returns an `Elements`, which can be used similar to an `Iterator`.
			while let Some(elem) = elements.next(ui) {
				let (col, row) = (elem.col, elem.row);

				// Color effect for fun.
				let (r, g, b, a) = (
					0.5 + (elem.col as f32 / cols as f32) / 2.0,
					0.75,
					1.0 - (elem.row as f32 / rows as f32) / 2.0,
					1.0
				);

				// We can use `Element`s to instantiate any kind of widget we like.
				// The `Element` does all of the positioning and sizing work for us.
				// Here, we use the `Element` to `set` a `Toggle` widget for us.
				let toggle = widget::Toggle::new(app_state.board[col][row])
					.rgba(r, g, b, a)
					.border(1.0);
				if let Some(new_value) = elem.set(toggle, ui).last() {
					app_state.board[col][row] = new_value;
				}
			}

		}

		// Render the `Ui` and then display it on the screen.
		if let Some(primitives) = ui.draw_if_changed() {
			renderer.fill(&display, primitives, &image_map);
			let mut target = display.draw();
			target.clear_color(0.0, 0.0, 0.0, 1.0);
			renderer.draw(&display, &mut target, &image_map).unwrap();
			target.finish().unwrap();
		}
	}
}
<commit_msg>Attempts to use chunks_mut() to expose a Vec as a 2D array. But run into lifetime specification issues (struct fields depending on lifetime of other struct fields).<commit_after>#[macro_use]
extern crate conrod;
use conrod::{widget, Borderable, Colorable, Positionable, Sizeable, Widget};
use conrod::backend::glium::glium;
use conrod::backend::glium::glium::{DisplayBuild, Surface};

struct AppState<'a> {
	board_data: Vec<bool>,
	board_rows: Vec<&'a mut [bool]>, // mutable slice for each row based on board_data.
	// mutable slice of mutable slice of bool, based on board_data. Emulate a 2D array.
	board: &'a mut [&'a mut [bool]],
	board_size: (usize, usize),
}

impl<'a> AppState<'a> {
	fn new() -> AppState<'a> {
		const BOARD_WIDTH: usize = 8;
		const BOARD_HEIGHT: usize = 8;
		let mut raw_board_data = vec![true; BOARD_WIDTH * BOARD_HEIGHT];
		// Notes an intermediate variable is required to force conversion from FromIterator<&mut [bool]> to Vec<&mut [bool]>.
		// This allows conversion to a slice of slice, emulating a 2D array.
		let mut grid_board_of_vec: Vec<&'a mut [bool]> = raw_board_data.as_mut_slice().chunks_mut(BOARD_WIDTH).collect();
		let mut grid_board: &mut [&mut [bool]] = grid_board_of_vec.as_mut_slice();

		AppState {
			board_data: raw_board_data,
			board_rows: grid_board_of_vec,
			board: grid_board,
			board_size: (BOARD_WIDTH, BOARD_HEIGHT)
		}
	}
}

fn main() {
	const WIDTH: u32 = 400;
	const HEIGHT: u32 = 400;

	let mut app_state = AppState::new();

	// Build the window.
	let display = glium::glutin::WindowBuilder::new()
		.with_vsync()
		.with_dimensions(WIDTH, HEIGHT)
		.with_title("Game of Life")
		.with_multisampling(4)
		.build_glium()
		.unwrap();

	// construct our `Ui`.
	let mut ui = conrod::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build();

	// Generate the widget identifiers.
	widget_ids!(struct Ids {
		title,
		info,
		board
	});
	let ids = Ids::new(ui.widget_id_generator());

	// Add a `Font` to the `Ui`'s `font::Map` from file.
	const FONT_PATH: &'static str =
		concat!(env!("CARGO_MANIFEST_DIR"), "/assets/fonts/NotoSans/NotoSans-Regular.ttf");
	ui.fonts.insert_from_file(FONT_PATH).unwrap();

	// A type used for converting `conrod::render::Primitives` into `Command`s that can be used
	// for drawing to the glium `Surface`.
	let mut renderer = conrod::backend::glium::Renderer::new(&display).unwrap();

	// The image map describing each of our widget->image mappings (in our case, none).
	let image_map = conrod::image::Map::<glium::texture::Texture2d>::new();

	// Poll events from the window.
	let mut last_update = std::time::Instant::now();
	let mut ui_needs_update = true;
	'main: loop {

		// We don't want to loop any faster than 60 FPS, so wait until it has been at least
		// 16ms since the last yield.
		let sixteen_ms = std::time::Duration::from_millis(16);
		let duration_since_last_update = std::time::Instant::now().duration_since(last_update);
		if duration_since_last_update < sixteen_ms {
			std::thread::sleep(sixteen_ms - duration_since_last_update);
		}

		// Collect all pending events.
		let mut events: Vec<_> = display.poll_events().collect();

		// If there are no events and the `Ui` does not need updating, wait for the next event.
		if events.is_empty() && !ui_needs_update {
			events.extend(display.wait_events().next());
		}

		// Reset the needs_update flag and time this update.
		ui_needs_update = false;
		last_update = std::time::Instant::now();

		// Handle all events.
		for event in events {

			// Use the `winit` backend feature to convert the winit event to a conrod one.
			if let Some(event) = conrod::backend::winit::convert(event.clone(), &display) {
				ui.handle_event(event);
				ui_needs_update = true;
			}

			match event {
				// Break from the loop upon `Escape`.
				glium::glutin::Event::KeyboardInput(_, _, Some(glium::glutin::VirtualKeyCode::Escape)) |
				glium::glutin::Event::Closed =>
					break 'main,
				_ => {},
			}
		}

		// Instantiate all widgets in the GUI.
		{
			let ui = &mut ui.set_widgets();

			// "Hello World!" in the middle of the screen.
			widget::Text::new("Game of Life")
				.mid_top_of(ui.window)
				.color(conrod::color::WHITE)
				.font_size(32)
				.set(ids.title, ui);
			widget::Text::new("Start")
				.down(5.0)
				.color(conrod::color::WHITE)
				.font_size(8)
				.set(ids.info, ui);

			// A demonstration using widget_matrix to easily draw a matrix of any kind of widget.
			let (cols, rows) = (8, 8);
			let mut elements = widget::Matrix::new(cols, rows)
				.down(20.0)
				.w_h(260.0, 260.0)
				.set(ids.board, ui);

			// The `Matrix` widget returns an `Elements`, which can be used similar to an `Iterator`.
			while let Some(elem) = elements.next(ui) {
				let (col, row) = (elem.col, elem.row);

				// Color effect for fun.
				let (r, g, b, a) = (
					0.5 + (elem.col as f32 / cols as f32) / 2.0,
					0.75,
					1.0 - (elem.row as f32 / rows as f32) / 2.0,
					1.0
				);

				// We can use `Element`s to instantiate any kind of widget we like.
				// The `Element` does all of the positioning and sizing work for us.
				// Here, we use the `Element` to `set` a `Toggle` widget for us.
				let toggle = widget::Toggle::new(app_state.board[col][row])
					.rgba(r, g, b, a)
					.border(1.0);
				if let Some(new_value) = elem.set(toggle, ui).last() {
					app_state.board[col][row] = new_value;
				}
			}

		}

		// Render the `Ui` and then display it on the screen.
		if let Some(primitives) = ui.draw_if_changed() {
			renderer.fill(&display, primitives, &image_map);
			let mut target = display.draw();
			target.clear_color(0.0, 0.0, 0.0, 1.0);
			renderer.draw(&display, &mut target, &image_map).unwrap();
			target.finish().unwrap();
		}
	}
}
<|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.

#![feature(fs_walk)]
#![feature(path_ext)]
#![feature(slice_patterns)]
#![feature(test)]

#[macro_use] extern crate bitflags;
extern crate png;
extern crate test;
extern crate url;
extern crate util;

use std::env;
use std::ffi::OsStr;
use std::fs::{PathExt, File, walk_dir};
use std::io::{self, Read, Result};
use std::path::{Path, PathBuf};
use std::process;
use std::process::{Command, Stdio};
use std::thread::sleep_ms;
use test::run_tests_console;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn, ShouldPanic};
use url::Url;

bitflags!(
    flags RenderMode: u32 {
        const CPU_RENDERING  = 0x00000001,
        const GPU_RENDERING  = 0x00000010,
        const LINUX_TARGET   = 0x00000100,
        const MACOS_TARGET   = 0x00001000,
        const ANDROID_TARGET = 0x00010000
    }
);

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut parts = args[1..].split(|e| &**e == "--");

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

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => panic!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] =>
            (render_mode_string, base_path, Some(testname.clone())),
    };

    let mut render_mode = match &**render_mode_string {
        "cpu" => CPU_RENDERING,
        "gpu" => GPU_RENDERING,
        _ => panic!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LINUX_TARGET);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MACOS_TARGET);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(ANDROID_TARGET);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in walk_dir(base_path).unwrap() {
        let file = file.unwrap().path();
        let maybe_extension = file.extension();
        match maybe_extension {
            Some(extension) => {
                if extension == OsStr::new("list") && file.is_file() {
                    let mut tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.append(&mut tests);
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        bench_benchmarks: false,
        nocapture: false,
        color: AutoColor,
    };

    match run(test_opts,
              all_tests,
              servo_args.iter().map(|x| x.clone()).collect()) {
        Ok(false) => process::exit(1), // tests failed
        Err(_) => process::exit(2),    // I/O-related failure
        _ => (),
    }
}

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> io::Result<bool> {
    // Verify that we're passing in valid servo arguments. Otherwise, servo
    // will exit before we've run any tests, and it will appear to us as if
    // all the tests are failing.
    let mut command = Command::new(&servo_path());
    command
        .args(&servo_args)
        .arg("-z")
        .arg("about:blank");

    let mut child = match command.spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };

    // Wait for the shell to launch or to fail
    sleep_ms(1000);
    child.kill().unwrap();
    let output = try!(child.wait_with_output());

    let stderr = String::from_utf8(output.stderr).unwrap();

    if stderr.contains("Unrecognized") {
        println!("Servo: {}", stderr);
        return Ok(false);
    }

    run_tests_console(&test_opts, all_tests)
}

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

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [PathBuf; 2],
    id: usize,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
    prefs: Vec<String>,
    fragment_identifier: Option<String>,
    resolution: Option<String>,
    pixel_ratio: Option<f32>,
}

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

fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: usize) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let contents = {
        let mut f = File::open(file).unwrap();
        let mut contents = String::new();
        f.read_to_string(&mut contents).unwrap();
        contents
    };

    for line in contents.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],
            },
            _ => panic!("reftest line: '{}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => ReftestKind::Same,
            "!=" => ReftestKind::Different,
            part => panic!("reftest line: '{}' has invalid kind '{}'", line, part)
        };

        let base = env::current_dir().unwrap().join(file.parent().unwrap());

        let file_left =  base.join(test_line.file_left);
        let file_right = base.join(test_line.file_right);

        let conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut prefs = vec![];
        let mut fragment_identifier = None;
        let mut resolution = None;
        let mut pixel_ratio = None;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CPU_RENDERING),
                "flaky_gpu" => flakiness.insert(GPU_RENDERING),
                "flaky_linux" => flakiness.insert(LINUX_TARGET),
                "flaky_macos" => flakiness.insert(MACOS_TARGET),
                _ => ()
            }
            if condition.starts_with("prefs:\"") {
                if let Some(joined) = condition.split("\"").nth(1) {
                    prefs.extend(joined.split(",").map(str::to_owned));
                }
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition["fragment=".len()..].to_string());
            }
            if condition.starts_with("resolution=") {
                resolution = Some(condition["resolution=".len() ..].to_string());
            }
            if condition.starts_with("device-pixel-ratio=") {
                pixel_ratio = Some(condition["device-pixel-ratio=".len() ..].to_string()
                                   .parse().expect("Invalid device-pixel-ratio"));
            }
        }

        let reftest = Reftest {
            name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: id_offset + tests.len(),
            render_mode: render_mode,
            servo_args: servo_args.to_vec(),
            is_flaky: render_mode.intersects(flakiness),
            prefs: prefs,
            fragment_identifier: fragment_identifier,
            resolution: resolution,
            pixel_ratio: pixel_ratio,
        };

        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_panic: ShouldPanic::No,
        },
        testfn: DynTestFn(Box::new(move || {
            check_reftest(reftest);
        })),
    }
}

fn capture(reftest: &Reftest, side: usize) -> (u32, u32, Vec<u8>) {
    let png_filename = format!("/tmp/servo-reftest-{:06}-{}.png", reftest.id, side);
    let mut command = Command::new(&servo_path());
    command
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .args(&reftest.servo_args[..])
        .arg("--user-stylesheet").arg(util::resource_files::resources_dir_path().join("ahem.css"))
        // Allows pixel perfect rendering of Ahem font and the HTML canvas for reftests.
        .arg("-Z")
        .arg("disable-text-aa,disable-canvas-aa")
        .args(&["-f", "-o"])
        .arg(&png_filename)
        .arg(&{
            let mut url = Url::from_file_path(&*reftest.files[side]).unwrap();
            url.fragment = reftest.fragment_identifier.clone();
            url.to_string()
        });
    // CPU rendering is the default
    if reftest.render_mode.contains(CPU_RENDERING) {
        command.arg("-c");
    }
    if reftest.render_mode.contains(GPU_RENDERING) {
        command.arg("-g");
    }
    for pref in &reftest.prefs {
        command.arg("--pref");
        command.arg(pref);
    }
    if let Some(ref resolution) = reftest.resolution {
        command.arg("--resolution");
        command.arg(resolution);
    }
    if let Some(pixel_ratio) = reftest.pixel_ratio {
        command.arg("--device-pixel-ratio");
        command.arg(pixel_ratio.to_string());
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert!(retval.success());

    let image = png::load_png(&png_filename).unwrap();
    let rgba8_bytes = match image.pixels {
        png::PixelsByColorType::RGBA8(pixels) => pixels,
        _ => panic!(),
    };
    (image.width, image.height, rgba8_bytes)
}

fn servo_path() -> PathBuf {
    let current_exe = env::current_exe().ok().expect("Could not locate current executable");
    current_exe.parent().unwrap().join("servo")
}

fn check_reftest(reftest: Reftest) {
    let (left_width, left_height, left_bytes) = capture(&reftest, 0);
    let (right_width, right_height, right_bytes) = capture(&reftest, 1);

    assert_eq!(left_width, right_width);
    assert_eq!(left_height, right_height);

    let left_all_white = left_bytes.iter().all(|&p| p == 255);
    let right_all_white = right_bytes.iter().all(|&p| p == 255);

    if left_all_white && right_all_white {
        panic!("Both renderings are empty")
    }

    let pixels = left_bytes.iter().zip(right_bytes.iter()).map(|(&a, &b)| {
        if a == b {
            // 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>>();

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

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

        match (reftest.kind, reftest.is_flaky) {
            (ReftestKind::Same, true) => println!("flaky test - rendering difference: {}", output),
            (ReftestKind::Same, false) => panic!("rendering difference: {}", output),
            (ReftestKind::Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == ReftestKind::Same);
    }
}
<commit_msg>Allow reftest to take 0 or more testname arguments<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.

#![feature(fs_walk)]
#![feature(path_ext)]
#![feature(slice_patterns)]
#![feature(test)]

#[macro_use] extern crate bitflags;
extern crate png;
extern crate test;
extern crate url;
extern crate util;

use std::env;
use std::ffi::OsStr;
use std::fs::{PathExt, File, walk_dir};
use std::io::{self, Read, Result};
use std::path::{Path, PathBuf};
use std::process;
use std::process::{Command, Stdio};
use std::thread::sleep_ms;
use test::run_tests_console;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn, ShouldPanic};
use url::Url;

bitflags!(
    flags RenderMode: u32 {
        const CPU_RENDERING  = 0x00000001,
        const GPU_RENDERING  = 0x00000010,
        const LINUX_TARGET   = 0x00000100,
        const MACOS_TARGET   = 0x00001000,
        const ANDROID_TARGET = 0x00010000
    }
);

fn main() {
    let args: Vec<String> = env::args().collect();
    let mut parts = args[1..].split(|e| &**e == "--");

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

    let (render_mode_string, base_path, testnames) = match harness_args {
        [ref render_mode_string, ref base_path, testnames..] =>
            (render_mode_string, base_path, testnames),
        _ => panic!("USAGE: cpu|gpu base_path [testname ...]"),
    };

    let mut render_mode = match &**render_mode_string {
        "cpu" => CPU_RENDERING,
        "gpu" => GPU_RENDERING,
        _ => panic!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LINUX_TARGET);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MACOS_TARGET);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(ANDROID_TARGET);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in walk_dir(base_path).unwrap() {
        let file = file.unwrap().path();
        let maybe_extension = file.extension();
        match maybe_extension {
            Some(extension) => {
                if extension == OsStr::new("list") && file.is_file() {
                    let len = all_tests.len();
                    let mut tests = parse_lists(&file, testnames, servo_args, render_mode, len);
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.append(&mut tests);
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: None,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        bench_benchmarks: false,
        nocapture: false,
        color: AutoColor,
    };

    match run(test_opts,
              all_tests,
              servo_args.iter().map(|x| x.clone()).collect()) {
        Ok(false) => process::exit(1), // tests failed
        Err(_) => process::exit(2),    // I/O-related failure
        _ => (),
    }
}

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> io::Result<bool> {
    // Verify that we're passing in valid servo arguments. Otherwise, servo
    // will exit before we've run any tests, and it will appear to us as if
    // all the tests are failing.
    let mut command = Command::new(&servo_path());
    command
        .args(&servo_args)
        .arg("-z")
        .arg("about:blank");

    let mut child = match command.spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };

    // Wait for the shell to launch or to fail
    sleep_ms(1000);
    child.kill().unwrap();
    let output = try!(child.wait_with_output());

    let stderr = String::from_utf8(output.stderr).unwrap();

    if stderr.contains("Unrecognized") {
        println!("Servo: {}", stderr);
        return Ok(false);
    }

    run_tests_console(&test_opts, all_tests)
}

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

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [PathBuf; 2],
    id: usize,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
    prefs: Vec<String>,
    fragment_identifier: Option<String>,
    resolution: Option<String>,
    pixel_ratio: Option<f32>,
}

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

fn parse_lists(file: &Path,
               filters: &[String],
               servo_args: &[String],
               render_mode: RenderMode,
               id_offset: usize)
               -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let contents = {
        let mut f = File::open(file).unwrap();
        let mut contents = String::new();
        f.read_to_string(&mut contents).unwrap();
        contents
    };

    for line in contents.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],
            },
            _ => panic!("reftest line: '{}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => ReftestKind::Same,
            "!=" => ReftestKind::Different,
            part => panic!("reftest line: '{}' has invalid kind '{}'", line, part)
        };

        let base = env::current_dir().unwrap().join(file.parent().unwrap());

        let file_left =  base.join(test_line.file_left);
        let file_right = base.join(test_line.file_right);

        let conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut prefs = vec![];
        let mut fragment_identifier = None;
        let mut resolution = None;
        let mut pixel_ratio = None;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CPU_RENDERING),
                "flaky_gpu" => flakiness.insert(GPU_RENDERING),
                "flaky_linux" => flakiness.insert(LINUX_TARGET),
                "flaky_macos" => flakiness.insert(MACOS_TARGET),
                _ => ()
            }
            if condition.starts_with("prefs:\"") {
                if let Some(joined) = condition.split("\"").nth(1) {
                    prefs.extend(joined.split(",").map(str::to_owned));
                }
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition["fragment=".len()..].to_string());
            }
            if condition.starts_with("resolution=") {
                resolution = Some(condition["resolution=".len() ..].to_string());
            }
            if condition.starts_with("device-pixel-ratio=") {
                pixel_ratio = Some(condition["device-pixel-ratio=".len() ..].to_string()
                                   .parse().expect("Invalid device-pixel-ratio"));
            }
        }

        let reftest = Reftest {
            name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: id_offset + tests.len(),
            render_mode: render_mode,
            servo_args: servo_args.to_vec(),
            is_flaky: render_mode.intersects(flakiness),
            prefs: prefs,
            fragment_identifier: fragment_identifier,
            resolution: resolution,
            pixel_ratio: pixel_ratio,
        };

        if filters.is_empty() || filters.iter().any(|pattern| reftest.name.contains(pattern)) {
            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_panic: ShouldPanic::No,
        },
        testfn: DynTestFn(Box::new(move || {
            check_reftest(reftest);
        })),
    }
}

fn capture(reftest: &Reftest, side: usize) -> (u32, u32, Vec<u8>) {
    let png_filename = format!("/tmp/servo-reftest-{:06}-{}.png", reftest.id, side);
    let mut command = Command::new(&servo_path());
    command
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .args(&reftest.servo_args[..])
        .arg("--user-stylesheet").arg(util::resource_files::resources_dir_path().join("ahem.css"))
        // Allows pixel perfect rendering of Ahem font and the HTML canvas for reftests.
        .arg("-Z")
        .arg("disable-text-aa,disable-canvas-aa")
        .args(&["-f", "-o"])
        .arg(&png_filename)
        .arg(&{
            let mut url = Url::from_file_path(&*reftest.files[side]).unwrap();
            url.fragment = reftest.fragment_identifier.clone();
            url.to_string()
        });
    // CPU rendering is the default
    if reftest.render_mode.contains(CPU_RENDERING) {
        command.arg("-c");
    }
    if reftest.render_mode.contains(GPU_RENDERING) {
        command.arg("-g");
    }
    for pref in &reftest.prefs {
        command.arg("--pref");
        command.arg(pref);
    }
    if let Some(ref resolution) = reftest.resolution {
        command.arg("--resolution");
        command.arg(resolution);
    }
    if let Some(pixel_ratio) = reftest.pixel_ratio {
        command.arg("--device-pixel-ratio");
        command.arg(pixel_ratio.to_string());
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert!(retval.success());

    let image = png::load_png(&png_filename).unwrap();
    let rgba8_bytes = match image.pixels {
        png::PixelsByColorType::RGBA8(pixels) => pixels,
        _ => panic!(),
    };
    (image.width, image.height, rgba8_bytes)
}

fn servo_path() -> PathBuf {
    let current_exe = env::current_exe().ok().expect("Could not locate current executable");
    current_exe.parent().unwrap().join("servo")
}

fn check_reftest(reftest: Reftest) {
    let (left_width, left_height, left_bytes) = capture(&reftest, 0);
    let (right_width, right_height, right_bytes) = capture(&reftest, 1);

    assert_eq!(left_width, right_width);
    assert_eq!(left_height, right_height);

    let left_all_white = left_bytes.iter().all(|&p| p == 255);
    let right_all_white = right_bytes.iter().all(|&p| p == 255);

    if left_all_white && right_all_white {
        panic!("Both renderings are empty")
    }

    let pixels = left_bytes.iter().zip(right_bytes.iter()).map(|(&a, &b)| {
        if a == b {
            // 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>>();

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

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

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


<commit_before>extern crate eventual;
extern crate sbahn;

use eventual::*;
use sbahn::client;
use sbahn::handler;
use sbahn::message::*;
use sbahn::storage::HashMapBackend;
use sbahn::storage_node::StorageNode;
use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
use std::thread;
use std::time::Duration;

// Milis to wait before trying to connect to any node.
static DELAY: u64 = 20;

static mut PORT: u16 = 1100;
/// Obtain an open port
fn get_port() -> u16 {
    let mut port = 0;
    loop {
        unsafe {
          PORT += 1;
          port = PORT;
        }
        let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port);
        if TcpListener::bind(&addr).is_ok() {
            // Check wether the port is open, and only return it if it is.
            return port;
        }
    }
}

fn get_storage_node(pos: usize, shard_count: usize) -> SocketAddrV4 {
    let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), get_port());
    let mut sn: StorageNode<HashMapBackend> = StorageNode::new(&addr, pos, shard_count);
    thread::spawn(move || {
        &sn.listen();
    });
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening
    addr
}

fn setup_handler_node(shards: &Vec<Vec<SocketAddrV4>>) -> SocketAddrV4 {
    let shards = shards.clone();
    let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), get_port());
    thread::spawn(move || {
        let _ = handler::listen(&addr, &shards);
    });
    thread::sleep(Duration::from_millis(DELAY));  // Wait for handler node to start listening
    addr
}

fn setup_cluster() -> (SocketAddrV4, Vec<Vec<SocketAddrV4>>) {
    let mut shards: Vec<Vec<SocketAddrV4>> = vec![];
    for i in 0..3 {
        let mut shard: Vec<SocketAddrV4> = vec![];
        for _ in 0..3 {
            shard.push(get_storage_node(i, 3));
        }
        shards.push(shard);
    }

    (setup_handler_node(&shards), shards)
}

fn write_to_storage_node(target: &SocketAddrV4, key: &Key, value: &Vec<u8>, timestamp: u64) {
    let request = InternodeRequest::Write {
        key: key.to_owned(),
        value: Value::Value {
            content: value.to_owned(),
            timestamp: timestamp,
        },
    };
    let r: Future<InternodeResponse, Error> = client::Client::send_to_node(target, &request);
    let _ = r.await();
}

fn key_and_value() -> (Key, Vec<u8>) {
    let key = Key {
        dataset: vec![1, 2, 3],
        pkey: vec![4, 5, 6],
        lkey: vec![7, 8, 9],
    };
    let value: Vec<u8> = vec![9, 8, 7];
    (key, value)
}

#[test]
fn read_what_you_insert() {
    let (handler_addr, _) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    let client = client::Client::new(vec![handler_addr]);
    // Should succeed
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::WriteAck {key, ..} => assert_eq!(key, local_key),
        _ => assert!(false),
    }
    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_one_all_nodes_available() {
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    for shard in shards {
        for node in shard {
            write_to_storage_node(&node, &local_key, &local_value, 100000);
        }
    }
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::One);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_one_one_node_available() {
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    // Write to only one node (local_key corresponds to shard 2).
    write_to_storage_node(&shards[2][0], &local_key, &local_value, 100000);

    thread::sleep(Duration::from_millis(DELAY*3));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::One);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_one_no_nodes_available() {
    // Should fail
}

#[test]
fn read_consistency_latest_all_same() {
    // Should succeed
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    for shard in shards {
        for node in shard {
            write_to_storage_node(&node, &local_key, &local_value, 100000);
        }
    }
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_latest_all_different() {
    // Should succeed
}

#[test]
fn read_consistency_latest_one_node_available() {
    // Should fail
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    // Write to only one node (local_key corresponds to shard 2).
    write_to_storage_node(&shards[2][0], &local_key, &local_value, 100000);

    thread::sleep(Duration::from_millis(DELAY*3));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Error {key, message} => {
            assert_eq!(key, local_key);
        }
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_one_all_available() {
    // Should succeed
}

#[test]
fn write_consistency_one_none_available() {
    // Should fail
}

#[test]
fn write_consistency_latest_all_available() {
    // Should succeed
}

#[test]
fn write_consistency_latest_quorum_available() {
    // Should succeed
}

#[test]
fn write_consistency_latest_one_available() {
    // Should fail
}

#[test]
fn write_consistency_latest_none_available() {
    // Should fail
}

#[test]
fn single_node() {
    let addr = get_storage_node(0, 1);
    let (insert_key, _) = key_and_value();

    let insert_key = Key {
        dataset: vec![1, 2, 3],
        pkey: vec![4, 5, 6],
        lkey: vec![7, 8, 9],
    };
    {
        let content = InternodeRequest::Write {
            key: insert_key.to_owned(),
            value: Value::Value {
                content: vec![1],
                timestamp: 10000000,
            },
        };
        let addr = &addr.to_owned();
        let r: Future<InternodeResponse, Error> = client::Client::send_to_node(addr, &content);
        match r.await().unwrap() {
            InternodeResponse::WriteAck {key, timestamp} => {
                assert_eq!(key, insert_key);
                assert_eq!(timestamp, 10000000);
            },
            e => panic!("{:?}", e),
        }
    }
    {
        let content = InternodeRequest::Read {
            key: insert_key.to_owned(),
        };
        let addr = &addr.to_owned();
        let r: Future<InternodeResponse, Error> = client::Client::send_to_node(addr, &content);
        let r = r.await();
        match r {
            Ok(r) => match r {
                InternodeResponse::Value {key, value} => {
                    assert_eq!(key, insert_key);
                    match value {
                        Value::Value {content, timestamp} => {
                            assert_eq!(&content[..], &[1][..]);
                            assert_eq!(timestamp, 10000000);
                        },
                        _ => panic!(),
                    }
                },
                _ => panic!(),
            },
            Err(_) => panic!(),
        }
    }
}
<commit_msg>Write operation tests<commit_after>extern crate eventual;
extern crate sbahn;

use eventual::*;
use sbahn::client;
use sbahn::handler;
use sbahn::message::*;
use sbahn::storage::HashMapBackend;
use sbahn::storage_node::StorageNode;
use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
use std::thread;
use std::time::Duration;

// Milis to wait before trying to connect to any node.
static DELAY: u64 = 50;

static mut PORT: u16 = 1100;
/// Obtain an open port
fn get_port() -> u16 {
    let mut port = 0;
    loop {
        unsafe {
          PORT += 1;
          port = PORT;
        }
        let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port);
        if TcpListener::bind(&addr).is_ok() {
            // Check wether the port is open, and only return it if it is.
            return port;
        }
    }
}

fn get_storage_node(pos: usize, shard_count: usize) -> SocketAddrV4 {
    let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), get_port());
    let mut sn: StorageNode<HashMapBackend> = StorageNode::new(&addr, pos, shard_count);
    thread::spawn(move || {
        &sn.listen();
    });
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening
    addr
}

fn setup_handler_node(shards: &Vec<Vec<SocketAddrV4>>) -> SocketAddrV4 {
    let shards = shards.clone();
    let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), get_port());
    thread::spawn(move || {
        let _ = handler::listen(&addr, &shards);
    });
    thread::sleep(Duration::from_millis(DELAY));  // Wait for handler node to start listening
    addr
}

/// Listen on `address` for incoming client requests, and do nothing.
pub fn dead_node(address: &SocketAddrV4) -> Future<(), ()> {
    let address = address.to_owned();

    let read_timeout = Some(Duration::from_millis(300));
    let write_timeout = Some(Duration::from_millis(300));

    Future::spawn(move || {
        match TcpListener::bind(&address) {
            Ok(listener) => {
                // Accept connections and process them, spawning a new thread for each one.
                for stream in listener.incoming() {
                    match stream {
                        Ok(stream) => {
                            let _ = stream.set_read_timeout(read_timeout);
                            let _ = stream.set_write_timeout(write_timeout);
                            thread::spawn(|| {
                                thread::sleep(Duration::from_millis(500));
                            });
                        }
                        Err(e) => panic!("Connection failed!: {:?}", e),
                    }
                }
            }
            Err(e) => panic!("Error while binding to {:?}: {:?}", address, e),
        }
    })
}

fn get_dead_storage_node() -> SocketAddrV4 {
    let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), get_port());
    thread::spawn(move || {
        let _ = dead_node(&addr);
    });
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening
    addr
}

fn setup_cluster() -> (SocketAddrV4, Vec<Vec<SocketAddrV4>>) {
    setup_bad_cluster(0)
}

fn setup_bad_cluster(bad_nodes: u32) -> (SocketAddrV4, Vec<Vec<SocketAddrV4>>) {
    let mut shards: Vec<Vec<SocketAddrV4>> = vec![];
    for i in 0..3 {
        let mut shard: Vec<SocketAddrV4> = vec![];
        for _ in 0..bad_nodes {
            shard.push(get_dead_storage_node());
        }
        for _ in bad_nodes..3 {
            shard.push(get_storage_node(i, 3));
        }
        shards.push(shard);
    }

    (setup_handler_node(&shards), shards)
}

fn write_to_storage_node(target: &SocketAddrV4, key: &Key, value: &Vec<u8>, timestamp: u64) {
    let request = InternodeRequest::Write {
        key: key.to_owned(),
        value: Value::Value {
            content: value.to_owned(),
            timestamp: timestamp,
        },
    };
    let r: Future<InternodeResponse, Error> = client::Client::send_to_node(target, &request);
    let _ = r.await();
}

fn key_and_value() -> (Key, Vec<u8>) {
    let key = Key {
        dataset: vec![1, 2, 3],
        pkey: vec![4, 5, 6],
        lkey: vec![7, 8, 9],
    };
    let value: Vec<u8> = vec![9, 8, 7];
    (key, value)
}

#[test]
fn read_what_you_insert() {
    let (handler_addr, _) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    let client = client::Client::new(vec![handler_addr]);
    // Should succeed
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::WriteAck {key, ..} => assert_eq!(key, local_key),
        _ => assert!(false),
    }
    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_one_all_nodes_available() {
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    for shard in shards {
        for node in shard {
            write_to_storage_node(&node, &local_key, &local_value, 100000);
        }
    }
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::One);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_one_one_node_available() {
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    // Write to only one node (local_key corresponds to shard 2).
    write_to_storage_node(&shards[2][0], &local_key, &local_value, 100000);

    thread::sleep(Duration::from_millis(DELAY*3));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::One);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_latest_all_same() {
    // Should succeed
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    for shard in shards {
        for node in shard {
            write_to_storage_node(&node, &local_key, &local_value, 100000);
        }
    }
    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Value {key, value} => {
            assert_eq!(key, local_key);
            match value {
                Value::Value {content, ..} => assert_eq!(content, local_value),
                _ => assert!(false),
            }
        },
        _ => assert!(false),
    }
}

#[test]
fn read_consistency_latest_all_different() {
    // Should succeed
}

#[test]
fn read_consistency_latest_one_node_available() {
    // Should fail
    let (handler_addr, shards) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    // Write to only one node (local_key corresponds to shard 2).
    write_to_storage_node(&shards[2][0], &local_key, &local_value, 100000);

    thread::sleep(Duration::from_millis(DELAY*3));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.get(&local_key);
    let r = r.await().unwrap();
    match r.message {
        Response::Error {key, ..} => {
            assert_eq!(key, local_key);
        }
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_one_all_available() {
    // Should succeed
    let (handler_addr, _) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::WriteAck {key, timestamp} => {
            assert_eq!(key, local_key);
            assert!(timestamp > 0);
        },
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_one_none_available() {
    // Should fail
    let (handler_addr, _) = setup_bad_cluster(3);
    let (local_key, local_value) = key_and_value();

    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::One);

    // Should succeed
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::Error {key, message} => {
            assert_eq!(key, local_key);
            assert_eq!("Quorum write could not be accomplished.".to_string(), message);
        },
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_latest_all_available() {
    let (handler_addr, _) = setup_cluster();
    let (local_key, local_value) = key_and_value();

    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::WriteAck {key, timestamp} => {
            assert_eq!(key, local_key);
            assert!(timestamp > 0);
        },
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_latest_quorum_available() {
    let (handler_addr, _) = setup_bad_cluster(1);
    let (local_key, local_value) = key_and_value();

    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should succeed
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::WriteAck {key, timestamp} => {
            assert_eq!(key, local_key);
            assert!(timestamp > 0);
        },
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_latest_one_available() {
    let (handler_addr, _) = setup_bad_cluster(2);
    let (local_key, local_value) = key_and_value();

    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should fail
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::Error {key, message} => {
            assert_eq!(local_key, key);
            assert_eq!("Quorum write could not be accomplished.".to_string(), message);
        }
        _ => assert!(false),
    }
}

#[test]
fn write_consistency_latest_none_available() {
    // Should fail
    let (handler_addr, _) = setup_bad_cluster(3);
    let (local_key, local_value) = key_and_value();

    thread::sleep(Duration::from_millis(DELAY));  // Wait for storage node to start listening

    let client = client::Client::with_consistency(vec![handler_addr], Consistency::Latest);

    // Should fail
    let r = client.insert(&local_key, &local_value);
    let r = r.await().unwrap();
    match r.message {
        Response::Error {key, message} => {
            assert_eq!(local_key, key);
            assert_eq!("Quorum write could not be accomplished.".to_string(), message);
        }
        _ => assert!(false),
    }
}

#[test]
fn single_node() {
    let addr = get_storage_node(0, 1);
    let (insert_key, _) = key_and_value();

    {
        let content = InternodeRequest::Write {
            key: insert_key.to_owned(),
            value: Value::Value {
                content: vec![1],
                timestamp: 10000000,
            },
        };
        let addr = &addr.to_owned();
        let r: Future<InternodeResponse, Error> = client::Client::send_to_node(addr, &content);
        match r.await().unwrap() {
            InternodeResponse::WriteAck {key, timestamp} => {
                assert_eq!(key, insert_key);
                assert_eq!(timestamp, 10000000);
            },
            e => panic!("{:?}", e),
        }
    }
    {
        let content = InternodeRequest::Read {
            key: insert_key.to_owned(),
        };
        let addr = &addr.to_owned();
        let r: Future<InternodeResponse, Error> = client::Client::send_to_node(addr, &content);
        let r = r.await();
        match r {
            Ok(r) => match r {
                InternodeResponse::Value {key, value} => {
                    assert_eq!(key, insert_key);
                    match value {
                        Value::Value {content, timestamp} => {
                            assert_eq!(&content[..], &[1][..]);
                            assert_eq!(timestamp, 10000000);
                        },
                        _ => panic!(),
                    }
                },
                _ => panic!(),
            },
            Err(_) => panic!(),
        }
    }
}
<|endoftext|>


<commit_before>use super::validator_record::ValidatorRecord;
use super::utils::types::Bitfield;
use super::utils::bls::{ AggregateSignature, PublicKey };
use super::utils::logging::Logger;
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::config::Config;
use super::shuffling::get_shuffling;

pub fn process_recent_attesters(
    cry_state: &CrystallizedState,
    recent_attesters: &Vec<usize>,
    config: &Config)
    -> Vec<i64>
{
    let mut deltas: Vec<i64> = vec![0; cry_state.num_active_validators()];
    for v in recent_attesters {
        deltas[*v] += config.attester_reward;
    }
    deltas
}

// For a given state set and skip_count, return a proposer and set
// of attestors.
pub fn get_attesters_and_proposer(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    skip_count: &u64,
    config: &Config,
    log:&Logger)
    -> (Vec<usize>, usize)
{
    let active_validator_count = cry_state.num_active_validators();
    assert!(active_validator_count >= 2, "must be >=2 active validators");
    let shuffled_validator_indicies = get_shuffling(
        &act_state.randao,
        &active_validator_count,
        config);
    let proposer_count: usize = 1;
    let ideal_validator_count: usize = (config.attester_count as usize)
        + (*skip_count as usize) + proposer_count;
    assert!(ideal_validator_count >= 2,
            "ideal_validator_count must be >=2");
    /*
     * If there are adequate validators to allocate a full set of assesters and
     * a proposer, then do so. Otherwise, the amount of attesters will need to be
     * validator_count - 1.
     */
    match ideal_validator_count > active_validator_count {
        true => {
            /*
             * The active validator count is too low.
             */
            warn!(log, "active validator count is low";
                  "active_validator_count" => active_validator_count,
                  "ideal_validator_count" => ideal_validator_count);
            (shuffled_validator_indicies[0..active_validator_count - 1].to_vec(),
            shuffled_validator_indicies[active_validator_count - 1])
        }
        false => {
            /*
             * The active validator count is adequate.
             */
            (shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(),
            shuffled_validator_indicies[ideal_validator_count - 1])
        }
    }
}

#[allow(unused_variables)]
pub fn process_attestations(
    validators: &Vec<ValidatorRecord>,
    attestation_indicies: &Vec<usize>,
    attestation_bitfield: &Bitfield,
    msg: &Vec<u8>,
    aggregate_sig: &AggregateSignature)
    -> Option<Vec<usize>>
{
    let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![];
    let mut attesters: Vec<usize> = vec![];

    assert_eq!(attestation_indicies.len(), attestation_bitfield.len());
    for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() {
        if attestation_bitfield.get_bit(&bitfield_bit) {
            key_msg_tuples.push(
                (&validators[*validators_index].pubkey,
                &msg)
                );
            attesters.push(*validators_index);
        }
    }
    // TODO: figure out why this assert exists in the Python impl.
    assert!(attesters.len() <= 128, "Max attesters is 128.");

    /*
    // TODO: ensure signature verification actually takes place.
    // It is completely bypassed here.
    match aggregate_sig.verify(&key_msg_tuples) {
        false => None,
        true => Some(attesters)
    }
    */
    Some(attesters)
}

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

    #[test]
    fn test_process_recent_attesters() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();
        let validator_count = 20;

        config.attester_reward = 12;

        let mut recent_attesters: Vec<usize> = vec![];

        for i in 0..validator_count {
            cry_state.active_validators
                .push(ValidatorRecord::zero_with_thread_rand_pub_key());
            if i % 2 == 0 {
                recent_attesters.push(i);
            }
        }

        let d = process_recent_attesters(
            &cry_state,
            &recent_attesters,
            &config);

        for i in 0..validator_count {
            if i % 2 == 0 {
                assert_eq!(d[i], config.attester_reward);
            } else {
                assert_eq!(d[i], 0);
            }
        }
    }

    #[test]
    fn test_attester_and_proposer_selection() {
        let mut cry_state = CrystallizedState::zero();

        (0..10).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        let act_state = ActiveState::zero();
        let (attestors, proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard(),
            &test_logger());
        assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
        assert_eq!(proposer, 3);
    }

    #[test]
    #[should_panic(expected = "must be >=2 active validators")]
    fn test_attester_and_proposer_selection_with_zero_active_validators() {
        let mut cry_state = CrystallizedState::zero();
        cry_state.active_validators = Vec::new();
        let act_state = ActiveState::zero();
        let (_attestors, _proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard(),
            &test_logger());
    }

    #[test]
    fn test_attestation_processing() {
        let validator_count = 10;
        let mut validators: Vec<ValidatorRecord> = vec![];
        let mut attestation_indicies: Vec<usize> = vec![];
        let mut bitfield = Bitfield::new();
        let mut agg_sig = AggregateSignature::new();
        let msg = "Message that's longer than 16 chars".as_bytes();

        for i in 0..validator_count {
            let (v, keypair) =
                ValidatorRecord::zero_with_thread_rand_keypair();
            validators.push(v);
            attestation_indicies.push(i);
            bitfield.set_bit(&i, &true);
            let sig = keypair.sign(&msg);
            agg_sig.aggregate(&sig);
        }

        let result = process_attestations(
            &validators,
            &attestation_indicies,
            &bitfield,
            &msg.to_vec(),
            &agg_sig);

        match result {
            None => panic!("Verification failed."),
            Some(x) => println!("{:?}", x)
        };
    }
}

<commit_msg>Add message to assert in attesters<commit_after>use super::validator_record::ValidatorRecord;
use super::utils::types::Bitfield;
use super::utils::bls::{ AggregateSignature, PublicKey };
use super::utils::logging::Logger;
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::config::Config;
use super::shuffling::get_shuffling;

pub fn process_recent_attesters(
    cry_state: &CrystallizedState,
    recent_attesters: &Vec<usize>,
    config: &Config)
    -> Vec<i64>
{
    let mut deltas: Vec<i64> = vec![0; cry_state.num_active_validators()];
    for v in recent_attesters {
        deltas[*v] += config.attester_reward;
    }
    deltas
}

// For a given state set and skip_count, return a proposer and set
// of attestors.
pub fn get_attesters_and_proposer(
    cry_state: &CrystallizedState,
    act_state: &ActiveState,
    skip_count: &u64,
    config: &Config,
    log:&Logger)
    -> (Vec<usize>, usize)
{
    let active_validator_count = cry_state.num_active_validators();
    assert!(active_validator_count >= 2, "must be >=2 active validators");
    let shuffled_validator_indicies = get_shuffling(
        &act_state.randao,
        &active_validator_count,
        config);
    let proposer_count: usize = 1;
    let ideal_validator_count: usize = (config.attester_count as usize)
        + (*skip_count as usize) + proposer_count;
    assert!(ideal_validator_count >= 2,
            "ideal_validator_count must be >=2");
    /*
     * If there are adequate validators to allocate a full set of assesters and
     * a proposer, then do so. Otherwise, the amount of attesters will need to be
     * validator_count - 1.
     */
    match ideal_validator_count > active_validator_count {
        true => {
            /*
             * The active validator count is too low.
             */
            warn!(log, "active validator count is low";
                  "active_validator_count" => active_validator_count,
                  "ideal_validator_count" => ideal_validator_count);
            (shuffled_validator_indicies[0..active_validator_count - 1].to_vec(),
            shuffled_validator_indicies[active_validator_count - 1])
        }
        false => {
            /*
             * The active validator count is adequate.
             */
            (shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(),
            shuffled_validator_indicies[ideal_validator_count - 1])
        }
    }
}

#[allow(unused_variables)]
pub fn process_attestations(
    validators: &Vec<ValidatorRecord>,
    attestation_indicies: &Vec<usize>,
    attestation_bitfield: &Bitfield,
    msg: &Vec<u8>,
    aggregate_sig: &AggregateSignature)
    -> Option<Vec<usize>>
{
    let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![];
    let mut attesters: Vec<usize> = vec![];

    assert_eq!(
        attestation_indicies.len(), attestation_bitfield.len(),
        "Bitfield length does not match required attestors."
    );
    for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() {
        if attestation_bitfield.get_bit(&bitfield_bit) {
            key_msg_tuples.push(
                (&validators[*validators_index].pubkey,
                &msg)
                );
            attesters.push(*validators_index);
        }
    }
    // TODO: figure out why this assert exists in the Python impl.
    assert!(attesters.len() <= 128, "Max attesters is 128.");

    /*
    // TODO: ensure signature verification actually takes place.
    // It is completely bypassed here.
    match aggregate_sig.verify(&key_msg_tuples) {
        false => None,
        true => Some(attesters)
    }
    */
    Some(attesters)
}

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

    #[test]
    fn test_process_recent_attesters() {
        let mut cry_state = CrystallizedState::zero();
        let mut config = Config::standard();
        let validator_count = 20;

        config.attester_reward = 12;

        let mut recent_attesters: Vec<usize> = vec![];

        for i in 0..validator_count {
            cry_state.active_validators
                .push(ValidatorRecord::zero_with_thread_rand_pub_key());
            if i % 2 == 0 {
                recent_attesters.push(i);
            }
        }

        let d = process_recent_attesters(
            &cry_state,
            &recent_attesters,
            &config);

        for i in 0..validator_count {
            if i % 2 == 0 {
                assert_eq!(d[i], config.attester_reward);
            } else {
                assert_eq!(d[i], 0);
            }
        }
    }

    #[test]
    fn test_attester_and_proposer_selection() {
        let mut cry_state = CrystallizedState::zero();

        (0..10).for_each(
            |_| cry_state.active_validators.push(
                ValidatorRecord::zero_with_thread_rand_pub_key()));

        let act_state = ActiveState::zero();
        let (attestors, proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard(),
            &test_logger());
        assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
        assert_eq!(proposer, 3);
    }

    #[test]
    #[should_panic(expected = "must be >=2 active validators")]
    fn test_attester_and_proposer_selection_with_zero_active_validators() {
        let mut cry_state = CrystallizedState::zero();
        cry_state.active_validators = Vec::new();
        let act_state = ActiveState::zero();
        let (_attestors, _proposer) = get_attesters_and_proposer(
            &cry_state,
            &act_state,
            &0,
            &Config::standard(),
            &test_logger());
    }

    #[test]
    fn test_attestation_processing() {
        let validator_count = 10;
        let mut validators: Vec<ValidatorRecord> = vec![];
        let mut attestation_indicies: Vec<usize> = vec![];
        let mut bitfield = Bitfield::new();
        let mut agg_sig = AggregateSignature::new();
        let msg = "Message that's longer than 16 chars".as_bytes();

        for i in 0..validator_count {
            let (v, keypair) =
                ValidatorRecord::zero_with_thread_rand_keypair();
            validators.push(v);
            attestation_indicies.push(i);
            bitfield.set_bit(&i, &true);
            let sig = keypair.sign(&msg);
            agg_sig.aggregate(&sig);
        }

        let result = process_attestations(
            &validators,
            &attestation_indicies,
            &bitfield,
            &msg.to_vec(),
            &agg_sig);

        match result {
            None => panic!("Verification failed."),
            Some(x) => println!("{:?}", x)
        };
    }
}

<|endoftext|>


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

use std::error::Error;
use std::fmt;
use std::str;

use nom::IResult;

use super::ast;
use {Format, Message};

/// An error resulting from `parse`.
#[derive(Clone,Debug)]
pub enum ParseError {
    /// The message could not be parsed.
    NotImplemented,
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::NotImplemented => "Not implemented.",
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

named!(format_body <&[u8], Box<Format> >,
    map!(is_not!("}"), |name| Box::new(ast::SimpleFormat::new(str::from_utf8(name).unwrap()))));

named!(format <&[u8], Box<Format> >,
    delimited!(
        char!('{'),
        format_body,
        char!('}')));

named!(plain_text <&[u8], Box<Format> >,
    map!(is_not!("{"), |text| Box::new(ast::PlainText::new(str::from_utf8(text).unwrap()))));

named!(message_parts <&[u8], Vec<Box<Format> > >,
    many0!(alt!(call!(format) | call!(plain_text))));

named!(message_parser <&[u8], Message>,
    chain!(
        parts: message_parts,
        || Message::new(parts)));

/// Parse some text and hopefully return a [`Message`].
///
/// [`Message`]: ../struct.Message.html
pub fn parse(message: &str) -> Result<Message, ParseError> {
    match message_parser(message.as_bytes()) {
        IResult::Error(_) |
        IResult::Incomplete(_) => Err(ParseError::NotImplemented),
        IResult::Done(_, m) => Ok(m),
    }
}

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

    #[test]
    fn it_works() {
        match parse("{name} is from {city}.") {
            Ok(m) => {
                assert_eq!(m.format_message(&arg("name", "Hendrik").arg("city", "Berlin")),
                           "Hendrik is from Berlin.");
            }
            Err(e) => panic!("Parse failed: {}", e),
        }
    }
}
<commit_msg>Convert parser to operating on &str, not &[u8].<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::error::Error;
use std::fmt;
use std::str;

use nom::IResult;

use super::ast;
use {Format, Message};

/// An error resulting from `parse`.
#[derive(Clone,Debug)]
pub enum ParseError {
    /// The message could not be parsed.
    NotImplemented,
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::NotImplemented => "Not implemented.",
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

named!(variable_name <&str, &str>, is_not_s!(",}"));

named!(format <&str, Box<Format> >,
    delimited!(
        tag_s!("{"),
        chain!(
            name: variable_name,
            || Box::new(ast::SimpleFormat::new(name))),
        tag_s!("}")));

named!(plain_text <&str, Box<Format> >,
    map!(is_not_s!("{"), |text| Box::new(ast::PlainText::new(text))));

named!(message_parts <&str, Vec<Box<Format> > >,
    many0!(alt!(call!(format) | call!(plain_text))));

named!(pub message_parser <&str, Message>,
    chain!(
        parts: message_parts,
        || Message::new(parts)));

/// Parse some text and hopefully return a [`Message`].
///
/// [`Message`]: ../struct.Message.html
pub fn parse(message: &str) -> Result<Message, ParseError> {
    match message_parser(message) {
        IResult::Error(_) |
        IResult::Incomplete(_) => Err(ParseError::NotImplemented),
        IResult::Done(_, m) => Ok(m),
    }
}

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

    #[test]
    fn it_works() {
        match parse("{name} is from {city}.") {
            Ok(m) => {
                assert_eq!(m.format_message(&arg("name", "Hendrik").arg("city", "Berlin")),
                           "Hendrik is from Berlin.");
            }
            Err(e) => panic!("Parse failed: {}", e),
        }
    }

    #[test]
    fn incomplete_fails() {
        match message_parser("{name") {
            IResult::Incomplete(_) => {}
            IResult::Error(e) => panic!("Expected incomplete failure: Got {}", e),
            IResult::Done(_, _) => panic!("Expected incomplete failure, but succeeded."),
        }
    }

    #[test]
    fn all_text_works() {
        match message_parser("Hello, world!") {
            IResult::Done(_, _) => {}
            _ => panic!("Expected successful parse."),
        }
    }
}
<|endoftext|>