<commit_before>use klee;

#[test]
fn basic_test() {
  let mut a : i32;
  klee::symbol(&a, "a");
  assert_eq!(a, 56);
}

#[test]
fn other_test() {
  let mut a : i32;
  let mut b : i32;

  klee::symbol(&a, "a");
  klee::symbol(&b, "b");

  if a == 50 {
    if b == 49 {
      klee::warning("this path is reachable");
    }
  }
}

#[test]
fn another_test() {
  let mut a : i32;
  klee::symbol(&a, "a");
  if a > 60 && a < 90 {
    let b = a + 40;
    klee::assert_warn(b == 0, "this assertion should not trigger");

    if b == 12 {
      klee::warning("This path should be unreachable");
    }

    klee::assert_warn(b == 101, "this assertion should trigger");
    klee::assert_warn(b == 150000, "this assertsion should not trigger");
  }
}
<commit_msg>Update simple.rs<commit_after>use klee;

#[test]
fn basic_test() {
  let mut a : i32;
  klee::symbol(&a, "a");
  assert_eq!(a, 56);
}

#[test]
fn other_test() {
  let mut a : i32;
  let mut b : i32;

  klee::symbol(&a, "a");
  klee::symbol(&b, "b");

  if a == 50 {
    if b == 49 {
      klee::warning("this path is reachable");
    }
  }
}

#[test]
fn another_test() {
  let a = klee::i32();
  if a > 60 && a < 90 {
    let b = a + 40;
    klee::assert_warn(b == 0, "this assertion should not trigger");

    if b == 12 {
      klee::warning("This path should be unreachable");
    }

    klee::assert_warn(b == 101, "this assertion should trigger");
    klee::assert_warn(b == 150000, "this assertsion should not trigger");
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 Anatoly Ikorsky
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ::futures::Future;
use mysql_async::{MyFuture, Pool, QueryResult, Opts};
use mysql_async::error::Error;
use mysql_async::prelude::*;

use std::env;
use std::io;

/// Same as `tokio::run`, but will panic if future panics and will return the result
/// of future execution.
fn run<F, T, U>(future: F) -> Result<T, U>
    where
        F: Future<Item = T, Error = U> + Send + 'static,
        T: Send + 'static,
        U: Send + 'static,
{
    let mut runtime = tokio::runtime::Runtime::new().unwrap();
    let result = runtime.block_on(future);
    runtime.shutdown_on_idle().wait().unwrap();
    result
}

fn get_url() -> String {
    if let Ok(url) = env::var("DATABASE_URL") {
        let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
        if opts
            .get_db_name()
            .expect("a database name is required")
            .is_empty()
        {
            panic!("database name is empty");
        }
        url
    } else {
        "mysql://root:password@127.0.0.1:3307/mysql".into()
    }
}

pub fn get_all_results<TupleType, T, P>(result : QueryResult<T, P>) -> impl MyFuture<Vec<TupleType>>
    where TupleType: FromRow + Send + 'static,
          P: Protocol + Send + 'static,
          T: ConnectionLike + Sized + Send + 'static,  {
    result.collect()
        .map(|(_, data)| data)
}

pub fn get_single_result<TupleType, T, P>(result : QueryResult<T, P>) -> impl MyFuture<TupleType>
    where TupleType: FromRow + Send + 'static,
          P: Protocol + Send + 'static,
          T: ConnectionLike + Sized + Send + 'static,  {
    get_all_results(result)
        .and_then(|mut data| if data.len() != 1 {
            Err(Error::from(io::Error::from(io::ErrorKind::InvalidData)))
        } else {
            Ok(data.remove(0))
        })
}

#[test]
fn use_generic_code() {
    let pool = Pool::new(Opts::from_url(&*get_url()).unwrap());
    let fut = pool.get_conn()
        .and_then(move |conn| conn.query("SELECT 1, 2, 3"))
        .and_then(get_single_result::<(u8, u8, u8), _, _>);

    let result = run(fut).unwrap();
    assert_eq!(result, (1, 2, 3));
}
<commit_msg>Cargo fmt<commit_after>// Copyright (c) 2019 Anatoly Ikorsky
//
// 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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ::futures::Future;
use mysql_async::error::Error;
use mysql_async::prelude::*;
use mysql_async::{MyFuture, Opts, Pool, QueryResult};

use std::env;
use std::io;

/// Same as `tokio::run`, but will panic if future panics and will return the result
/// of future execution.
fn run<F, T, U>(future: F) -> Result<T, U>
where
    F: Future<Item = T, Error = U> + Send + 'static,
    T: Send + 'static,
    U: Send + 'static,
{
    let mut runtime = tokio::runtime::Runtime::new().unwrap();
    let result = runtime.block_on(future);
    runtime.shutdown_on_idle().wait().unwrap();
    result
}

fn get_url() -> String {
    if let Ok(url) = env::var("DATABASE_URL") {
        let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
        if opts
            .get_db_name()
            .expect("a database name is required")
            .is_empty()
        {
            panic!("database name is empty");
        }
        url
    } else {
        "mysql://root:password@127.0.0.1:3307/mysql".into()
    }
}

pub fn get_all_results<TupleType, T, P>(result: QueryResult<T, P>) -> impl MyFuture<Vec<TupleType>>
where
    TupleType: FromRow + Send + 'static,
    P: Protocol + Send + 'static,
    T: ConnectionLike + Sized + Send + 'static,
{
    result.collect().map(|(_, data)| data)
}

pub fn get_single_result<TupleType, T, P>(result: QueryResult<T, P>) -> impl MyFuture<TupleType>
where
    TupleType: FromRow + Send + 'static,
    P: Protocol + Send + 'static,
    T: ConnectionLike + Sized + Send + 'static,
{
    get_all_results(result).and_then(|mut data| {
        if data.len() != 1 {
            Err(Error::from(io::Error::from(io::ErrorKind::InvalidData)))
        } else {
            Ok(data.remove(0))
        }
    })
}

#[test]
fn use_generic_code() {
    let pool = Pool::new(Opts::from_url(&*get_url()).unwrap());
    let fut = pool
        .get_conn()
        .and_then(move |conn| conn.query("SELECT 1, 2, 3"))
        .and_then(get_single_result::<(u8, u8, u8), _, _>);

    let result = run(fut).unwrap();
    assert_eq!(result, (1, 2, 3));
}
<|endoftext|>


<commit_before>#![cfg(test)]

use std::collections::HashMap;
use std::str::FromStr;

use traits::Cell;

/// Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
/// Assumes Von Nuemann's neighborhood.

#[derive(Clone, Copy, Debug)]
enum Stage {
    Collision,
    Transport
}

const COLLISION: &'static str = "c";
const TRANSPORT: &'static str = "t";

impl Stage {

    fn from_str(string: &str) -> Self {

        match string {
            COLLISION => Stage::Collision,
            TRANSPORT => Stage::Collision,
            _ => panic!("Unknown stage.")
        }
    }
}

impl From<Stage> for &'static str {

    fn from(stage: Stage) -> &'static str {

        match stage {
            Stage::Collision => COLLISION,
            Stage::Transport => TRANSPORT
        }
    }
}

#[derive(Clone, Copy, Debug)]
enum Direction {
    Up,
    Left,
    Right,
    Down
}

const UP: &'static str = "u";
const LEFT: &'static str = "l";
const RIGHT: &'static str = "r";
const DOWN: &'static str = "d";

impl Direction {

    fn from_str(string: &str) -> Self {

        match string {
            UP => Direction::Up,
            LEFT => Direction::Left,
            RIGHT => Direction::Right,
            DOWN => Direction::Down,
            _ => panic!("Unknown direction.")
        }
    }

    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right
        }
    }
}

impl<'a> From<&'a Direction> for &'static str {

    fn from(direction: &'a Direction) -> &'static str {

        match *direction {
            Direction::Up => UP,
            Direction::Left => LEFT,
            Direction::Right => RIGHT,
            Direction::Down => DOWN
        }
    }
}

#[derive(Clone, Debug)]
struct HPP {
    particles: [bool; 4],
    stage: Stage
}

impl Default for HPP {

    fn default() -> Self {

        HPP {
            stage: Stage::Collision,
            particles: [false; 4]
        }
    }
}

const EXISTS: &'static str = "true";
const DOESNT_EXIST: &'static str = "false";
const STAGE: &'static str = "stage";

impl Cell for HPP {

    fn step<'a, I>(&'a self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn repr(&self, meta: &mut HashMap<&str, &str>) {

        for direction in self.directions().iter() {

            let exists = self.particle(direction);

            meta.insert(
                direction.into(),
                if exists { EXISTS } else { DOESNT_EXIST }
            );
        }

        meta.insert(STAGE, self.stage.into());
    }

    fn from_repr(&mut self, meta: &HashMap<&str, &str>) {

        for (state, value) in meta.iter() {

            match *state {

                STAGE => {
                    self.stage = Stage::from_str(value);
                },
                _ => {
                    let direction = Direction::from_str(state);
                    let exists = bool::from_str(value).unwrap();

                    self.set_particle(&direction, exists);
                }
            }
        }
    }
}

impl HPP {

    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Transport,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) && neighbor.particle(&opposite);

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    }
                },
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    new.set_particle(&opposite, self.particle(direction));
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Collision,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            if let Some(neighbor) = neighbor {
                let opposite = direction.opposite();
                new.set_particle(&opposite, neighbor.particle(&opposite));
            }
        }

        new
    }

    fn particle(&self, direction: &Direction) -> bool {

        match *direction {
            Direction::Up => self.particles[0],
            Direction::Left => self.particles[1],
            Direction::Right => self.particles[2],
            Direction::Down => self.particles[3]
        }
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {

        match *direction {
            Direction::Up => { self.particles[0] = exists },
            Direction::Left => { self.particles[1] = exists },
            Direction::Right => { self.particles[2] = exists },
            Direction::Down => { self.particles[3] = exists }
        }
    }

    #[inline]
    fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}
<commit_msg>HPP tests and fixes.<commit_after>#![cfg(test)]

use std::collections::HashMap;
use std::str::FromStr;

use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::ReprConsumer;
use repr::CellRepr;
use repr::GridRepr;
use engine::sequential::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::GridCoord;
use grid::square::SquareGrid;

/// Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
/// Assumes Von Nuemann's neighborhood.

#[derive(Clone, Copy, Debug)]
enum Stage {
    Collision,
    Transport
}

const COLLISION: &'static str = "c";
const TRANSPORT: &'static str = "t";

impl Stage {

    fn from_str(string: &str) -> Self {

        match string {
            COLLISION => Stage::Collision,
            TRANSPORT => Stage::Collision,
            _ => panic!("Unknown stage.")
        }
    }
}

impl From<Stage> for &'static str {

    fn from(stage: Stage) -> &'static str {

        match stage {
            Stage::Collision => COLLISION,
            Stage::Transport => TRANSPORT
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down
}

const UP: &'static str = "u";
const LEFT: &'static str = "l";
const RIGHT: &'static str = "r";
const DOWN: &'static str = "d";

impl Direction {

    fn from_str(string: &str) -> Self {

        match string {
            UP => Direction::Up,
            LEFT => Direction::Left,
            RIGHT => Direction::Right,
            DOWN => Direction::Down,
            _ => panic!("Unknown direction.")
        }
    }

    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right
        }
    }
}

impl<'a> From<&'a Direction> for &'static str {

    fn from(direction: &'a Direction) -> &'static str {

        match *direction {
            Direction::Up => UP,
            Direction::Left => LEFT,
            Direction::Right => RIGHT,
            Direction::Down => DOWN
        }
    }
}

#[derive(Clone, Debug)]
struct HPP {
    particles: [bool; 4],
    stage: Stage
}

impl Default for HPP {

    fn default() -> Self {

        HPP {
            stage: Stage::Collision,
            particles: [false; 4]
        }
    }
}

const EXISTS: &'static str = "true";
const DOESNT_EXIST: &'static str = "false";
const STAGE: &'static str = "stage";

impl Cell for HPP {

    fn step<'a, I>(&'a self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn repr(&self, meta: &mut HashMap<&str, &str>) {

        for direction in self.directions().iter() {

            let exists = self.particle(direction);

            meta.insert(
                direction.into(),
                if exists { EXISTS } else { DOESNT_EXIST }
            );

        }
        meta.insert(STAGE, self.stage.into());
    }

    fn from_repr(&mut self, meta: &HashMap<&str, &str>) {

        for (state, value) in meta.iter() {

            match *state {

                STAGE => {
                    self.stage = Stage::from_str(value);
                },
                _ => {
                    let direction = Direction::from_str(state);
                    let exists = bool::from_str(value).unwrap();

                    self.set_particle(&direction, exists);
                }
            }
        }
    }
}

impl HPP {

    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Transport,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) && neighbor.particle(&opposite);

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    }
                    else {
                        let particle = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, particle);
                    }
                },
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    new.set_particle(&opposite, self.particle(direction));
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Collision,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();
                    new.set_particle(
                        &opposite,
                        neighbor.particle(&opposite) || self.particle(&opposite)
                    );
                },
                None => {
                    new.set_particle(&direction, self.particle(&direction))
                }
            }
        }

        new
    }

    fn particle(&self, direction: &Direction) -> bool {

        match *direction {
            Direction::Up => self.particles[0],
            Direction::Left => self.particles[1],
            Direction::Right => self.particles[2],
            Direction::Down => self.particles[3]
        }
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {

        match *direction {
            Direction::Up => { self.particles[0] = exists },
            Direction::Left => { self.particles[1] = exists },
            Direction::Right => { self.particles[2] = exists },
            Direction::Down => { self.particles[3] = exists }
        }
    }

    #[inline]
    fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPTestConsumer;

impl HPPTestConsumer {

    pub fn new() -> Self {
        HPPTestConsumer { }
    }

    fn particles_count<C: Coord>(&self,
                                 cells: &Vec<CellRepr<C>>,
                                 direction: &'static str) -> i32 {

        cells.iter()
             .filter(|c| c.state.get(direction).unwrap_or(&DOESNT_EXIST) == &EXISTS)
             .count() as i32
    }

    fn find_cell<'a: 'b, 'b, C: Coord>(&'a self,
                                       cells: &'a Vec<CellRepr<'a, C>>,
                                       x: i32, y: i32) -> &'b CellRepr<C> {

        cells.iter().find(|c| c.coord.x() == x &&
                              c.coord.y() == y).unwrap()
    }

    fn test_collision<C: Coord>(&self, repr: &GridRepr<C>) {

        let left_particle_count = self.particles_count(&repr.cells, LEFT);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(&repr.cells, RIGHT);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(&repr.cells, UP);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(&repr.cells, DOWN);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = self.find_cell(&repr.cells, 0, 1);
        assert_eq!(rebound_to_right.state.get(RIGHT).unwrap(), &EXISTS);

        let head_on_up = self.find_cell(&repr.cells, 1, 0);
        assert_eq!(head_on_up.state.get(UP).unwrap(), &EXISTS);

        let head_on_down = self.find_cell(&repr.cells, 2, 0);
        assert_eq!(head_on_down.state.get(DOWN).unwrap(), &EXISTS);
    }

    fn test_transport<C: Coord>(&self, repr: &GridRepr<C>) {
        let simple_move_to_right = self.find_cell(&repr.cells, 1, 2);
        assert_eq!(simple_move_to_right.state.get(RIGHT).unwrap(), &EXISTS);

        let move_to_right_after_rebound = self.find_cell(&repr.cells, 1, 2);
        assert_eq!(move_to_right_after_rebound.state.get(RIGHT).unwrap(), &EXISTS);

        let move_to_down_after_head_on = self.find_cell(&repr.cells, 2, 1);
        assert_eq!(move_to_down_after_head_on.state.get(DOWN).unwrap(), &EXISTS);

        let fixed_to_up_after_head_on = self.find_cell(&repr.cells, 1, 0);
        assert_eq!(fixed_to_up_after_head_on.state.get(UP).unwrap(), &EXISTS);
    }

    fn pretty_print<C: Coord>(&self, repr: &GridRepr<C>) {
        println!("");

        for y in 0 .. 3 {
            print!("|");

            for x in 0 .. 3 {
                let cell = self.find_cell(&repr.cells, x, y);
                let maybe_particle =
                    cell.state.iter()
                              .find(|&(k, v)| k != &STAGE && v == &EXISTS);

                let to_print = match maybe_particle {
                    Some((&UP, _)) => " ^ |",
                    Some((&LEFT, _)) => " < |",
                    Some((&RIGHT, _)) => " > |",
                    Some((&DOWN, _)) => " v |",
                    None => " * |",

                    Some((_, _)) => panic!("Unknown particle direction.")
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl ReprConsumer for HPPTestConsumer {

    fn consume<C: Coord>(&mut self, repr: &GridRepr<C>) {
        assert_eq!(repr.cells.len(), 9);

        self.pretty_print(repr);

        // We are testing previous state.
        match *repr.cells[0].state.get(STAGE).unwrap() {
            COLLISION => self.test_transport(repr),
            TRANSPORT => self.test_collision(repr),
            _ => panic!("Unknown cell stage.")
        }
    }
}

#[test]
fn test_particles() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let mut left_particle = HashMap::new();
    left_particle.insert(LEFT, EXISTS);

    let mut right_particle = HashMap::new();
    right_particle.insert(RIGHT, EXISTS);

    let cells = vec![
        CellRepr::new(GridCoord::from_2d(1, 0), Some(&right_particle)),
        CellRepr::new(GridCoord::from_2d(2, 0), Some(&left_particle)),
        CellRepr::new(GridCoord::from_2d(0, 1), Some(&left_particle)),
        CellRepr::new(GridCoord::from_2d(0, 2), Some(&right_particle)),
    ];

    let grid_repr = GridRepr::new(3, 3, Some(cells));

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.from_repr(&grid_repr);

    let right_particles_count =
        grid.repr().cells.iter()
            .filter(|c| c.state.get(RIGHT).unwrap_or(&DOESNT_EXIST) == &EXISTS)
            .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use sysinfo::ProcessExt;
use sysinfo::SystemExt;

#[test]
fn test_process() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);
    s.refresh_processes();
    if !sysinfo::System::IS_SUPPORTED {
        return;
    }
    assert!(!s.processes().is_empty());
    #[cfg(not(windows))]
    assert!(s
        .processes()
        .values()
        .any(|p| !p.exe().to_str().unwrap_or("").is_empty()));
}

#[test]
fn test_process_refresh() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);

    if !sysinfo::System::IS_SUPPORTED {
        return;
    }
    s.refresh_process(sysinfo::get_current_pid().expect("failed to get current pid"));
    assert!(s
        .process(sysinfo::get_current_pid().expect("failed to get current pid"))
        .is_some(),);
}

#[test]
#[cfg(windows)]
fn test_get_cmd_line() {
    let p = std::process::Command::new("timeout")
        .arg("/t")
        .arg("3")
        .spawn()
        .unwrap();
    let mut s = sysinfo::System::new();
    assert!(s.processes().is_empty());
    s.refresh_processes();
    assert!(!s.processes().is_empty());
    if let Some(process) = s.process(p.id() as sysinfo::Pid) {
        assert_eq!(process.cmd(), &["timeout", "/t", "3"]);
    } else {
        // We're very likely on a "linux-like" shell so let's try some unix command...
        unix_like_cmd();
    }
}

#[test]
#[cfg(not(windows))]
fn test_get_cmd_line() {
    if sysinfo::System::IS_SUPPORTED {
        unix_like_cmd();
    }
}

fn unix_like_cmd() {
    use std::{thread, time};

    let p = std::process::Command::new("sleep")
        .arg("3")
        .spawn()
        .unwrap();
    // To ensure that the system data are filled correctly...
    thread::sleep(time::Duration::from_millis(250));
    let mut s = sysinfo::System::new();
    assert!(s.processes().is_empty());
    s.refresh_processes();
    assert!(!s.processes().is_empty());
    let process = s.process(p.id() as sysinfo::Pid).unwrap();
    if process.cmd() != ["sleep", "3"] {
        panic!("cmd not equivalent to`[sleep, 3]`: {:?}", process);
    }
}

#[test]
fn test_process_disk_usage() {
    use std::fs;
    use std::fs::File;
    use std::io::prelude::*;
    use sysinfo::{get_current_pid, ProcessExt, SystemExt};

    if !sysinfo::System::IS_SUPPORTED {
        return;
    }

    {
        let mut file = File::create("test.txt").unwrap();
        file.write_all(b"This is a test file\nwith test data.\n")
            .unwrap();
    }
    fs::remove_file("test.txt").ok();
    let mut system = sysinfo::System::new();
    assert!(system.processes().is_empty());
    system.refresh_processes();
    assert!(!system.processes().is_empty());
    let p = system
        .process(get_current_pid().expect("Failed retrieving current pid."))
        .expect("failed to get process");

    assert!(
        p.disk_usage().total_written_bytes > 0,
        "found {} total written bytes...",
        p.disk_usage().total_written_bytes
    );
    assert!(
        p.disk_usage().written_bytes > 0,
        "found {} written bytes...",
        p.disk_usage().written_bytes
    );
}

#[test]
fn cpu_usage_is_not_nan() {
    let mut system = sysinfo::System::new();
    system.refresh_processes();

    if !sysinfo::System::IS_SUPPORTED {
        return;
    }

    let first_pids = system
        .processes()
        .iter()
        .take(10)
        .map(|(&pid, _)| pid)
        .collect::<Vec<_>>();
    let mut checked = 0;

    first_pids.into_iter().for_each(|pid| {
        system.refresh_process(pid);
        if let Some(p) = system.process(pid) {
            assert!(!p.cpu_usage().is_nan());
            checked += 1;
        }
    });
    assert!(checked > 0);
}
<commit_msg>Add test for environment variables<commit_after>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use sysinfo::ProcessExt;
use sysinfo::SystemExt;

#[test]
fn test_process() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);
    s.refresh_processes();
    if !sysinfo::System::IS_SUPPORTED {
        return;
    }
    assert!(!s.processes().is_empty());
    #[cfg(not(windows))]
    assert!(s
        .processes()
        .values()
        .any(|p| !p.exe().to_str().unwrap_or("").is_empty()));
}

#[test]
fn test_process_refresh() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);

    if !sysinfo::System::IS_SUPPORTED {
        return;
    }
    s.refresh_process(sysinfo::get_current_pid().expect("failed to get current pid"));
    assert!(s
        .process(sysinfo::get_current_pid().expect("failed to get current pid"))
        .is_some(),);
}

#[test]
#[cfg(windows)]
fn test_get_cmd_line() {
    let p = std::process::Command::new("timeout")
        .arg("/t")
        .arg("3")
        .spawn()
        .unwrap();
    let mut s = sysinfo::System::new();
    assert!(s.processes().is_empty());
    s.refresh_processes();
    assert!(!s.processes().is_empty());
    if let Some(process) = s.process(p.id() as sysinfo::Pid) {
        assert_eq!(process.cmd(), &["timeout", "/t", "3"]);
    } else {
        // We're very likely on a "linux-like" shell so let's try some unix command...
        unix_like_cmd();
    }
}

#[test]
#[cfg(not(windows))]
fn test_get_cmd_line() {
    if sysinfo::System::IS_SUPPORTED {
        unix_like_cmd();
    }
}

fn unix_like_cmd() {
    use std::{thread, time};

    let p = std::process::Command::new("sleep")
        .arg("3")
        .spawn()
        .unwrap();
    // To ensure that the system data are filled correctly...
    thread::sleep(time::Duration::from_millis(250));
    let mut s = sysinfo::System::new();
    assert!(s.processes().is_empty());
    s.refresh_processes();
    assert!(!s.processes().is_empty());
    let process = s.process(p.id() as sysinfo::Pid).unwrap();
    if process.cmd() != ["sleep", "3"] {
        panic!("cmd not equivalent to`[sleep, 3]`: {:?}", process);
    }
}

#[test]
#[cfg(not(windows))]
fn test_environ() {
    if !sysinfo::System::IS_SUPPORTED {
        return;
    }

    let mut p = std::process::Command::new("sleep")
        .arg("3")
        .stdout(std::process::Stdio::null())
        .env("FOO", "BAR")
        .env("OTHER", "VALUE")
        .spawn()
        .unwrap();
    let pid = p.id() as sysinfo::Pid;
    std::thread::sleep(std::time::Duration::from_millis(250));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().unwrap();

    let processes = s.processes();
    let p = processes.get(&pid);

    if let Some(p) = p {
        assert_eq!(p.pid(), pid);
        assert!(p.environ().iter().any(|e| e == "FOO=BAR"));
        assert!(p.environ().iter().any(|e| e == "OTHER=VALUE"));
    } else {
        panic!("No process found!")
    }
}

#[test]
fn test_process_disk_usage() {
    use std::fs;
    use std::fs::File;
    use std::io::prelude::*;
    use sysinfo::{get_current_pid, ProcessExt, SystemExt};

    if !sysinfo::System::IS_SUPPORTED {
        return;
    }

    {
        let mut file = File::create("test.txt").unwrap();
        file.write_all(b"This is a test file\nwith test data.\n")
            .unwrap();
    }
    fs::remove_file("test.txt").ok();
    let mut system = sysinfo::System::new();
    assert!(system.processes().is_empty());
    system.refresh_processes();
    assert!(!system.processes().is_empty());
    let p = system
        .process(get_current_pid().expect("Failed retrieving current pid."))
        .expect("failed to get process");

    assert!(
        p.disk_usage().total_written_bytes > 0,
        "found {} total written bytes...",
        p.disk_usage().total_written_bytes
    );
    assert!(
        p.disk_usage().written_bytes > 0,
        "found {} written bytes...",
        p.disk_usage().written_bytes
    );
}

#[test]
fn cpu_usage_is_not_nan() {
    let mut system = sysinfo::System::new();
    system.refresh_processes();

    if !sysinfo::System::IS_SUPPORTED {
        return;
    }

    let first_pids = system
        .processes()
        .iter()
        .take(10)
        .map(|(&pid, _)| pid)
        .collect::<Vec<_>>();
    let mut checked = 0;

    first_pids.into_iter().for_each(|pid| {
        system.refresh_process(pid);
        if let Some(p) = system.process(pid) {
            assert!(!p.cpu_usage().is_nan());
            checked += 1;
        }
    });
    assert!(checked > 0);
}
<|endoftext|>


<commit_before>use futures::channel::{mpsc, oneshot};
use futures::{SinkExt, StreamExt};
use std::convert::TryInto;
use std::time::Duration;
use zeromq::prelude::*;

#[tokio::test]
async fn test_pub_sub_sockets() {
    async fn helper(bind_addr: &'static str) {
        let payload = chrono::Utc::now().to_rfc2822();

        let cloned_payload = payload.clone();
        let (server_stop_sender, mut server_stop) = oneshot::channel::<()>();
        tokio::spawn(async move {
            let mut pub_socket = zeromq::PubSocket::new();
            pub_socket
                .bind(bind_addr)
                .await
                .unwrap_or_else(|_| panic!("Failed to bind to {}", bind_addr));

            loop {
                if let Ok(Some(_)) = server_stop.try_recv() {
                    break;
                }

                pub_socket
                    .send(cloned_payload.clone().into())
                    .expect("Failed to send");
                tokio::time::delay_for(Duration::from_millis(1)).await;
            }
        });

        let (sub_results_sender, sub_results) = mpsc::channel(100);
        for _ in 0..10 {
            let mut cloned_sub_sender = sub_results_sender.clone();
            let cloned_payload = payload.clone();
            tokio::spawn(async move {
                let mut sub_socket = zeromq::SubSocket::new();
                sub_socket
                    .connect(bind_addr)
                    .await
                    .unwrap_or_else(|_| panic!("Failed to connect to {}", bind_addr));

                sub_socket.subscribe("").await.expect("Failed to subscribe");

                for _ in 0..10 {
                    let recv_payload: String = sub_socket
                        .recv()
                        .await
                        .expect("Failed to recv")
                        .try_into()
                        .expect("Malformed string");
                    assert_eq!(cloned_payload, recv_payload);
                    cloned_sub_sender.send(()).await.unwrap();
                }
            });
        }
        drop(sub_results_sender);
        let res_vec: Vec<()> = sub_results.collect().await;
        assert_eq!(100, res_vec.len());

        server_stop_sender.send(()).unwrap();
    }
    let addrs = vec![
        "tcp://127.0.0.1:5554",
        "tcp://[::1]:5555",
        "tcp://127.0.0.1:5556",
        "tcp://localhost:5557",
    ];
    futures::future::join_all(addrs.into_iter().map(helper)).await;
}
<commit_msg>Fixed flaky test due to port collision<commit_after>use futures::channel::{mpsc, oneshot};
use futures::{SinkExt, StreamExt};
use std::convert::TryInto;
use std::time::Duration;
use zeromq::prelude::*;

#[tokio::test]
async fn test_pub_sub_sockets() {
    async fn helper(bind_addr: &'static str) {
        let payload = chrono::Utc::now().to_rfc2822();

        let cloned_payload = payload.clone();
        let (server_stop_sender, mut server_stop) = oneshot::channel::<()>();
        tokio::spawn(async move {
            let mut pub_socket = zeromq::PubSocket::new();
            pub_socket
                .bind(bind_addr)
                .await
                .unwrap_or_else(|_| panic!("Failed to bind to {}", bind_addr));

            loop {
                if let Ok(Some(_)) = server_stop.try_recv() {
                    break;
                }

                pub_socket
                    .send(cloned_payload.clone().into())
                    .expect("Failed to send");
                tokio::time::delay_for(Duration::from_millis(1)).await;
            }
        });

        let (sub_results_sender, sub_results) = mpsc::channel(100);
        for _ in 0..10 {
            let mut cloned_sub_sender = sub_results_sender.clone();
            let cloned_payload = payload.clone();
            tokio::spawn(async move {
                let mut sub_socket = zeromq::SubSocket::new();
                sub_socket
                    .connect(bind_addr)
                    .await
                    .unwrap_or_else(|_| panic!("Failed to connect to {}", bind_addr));

                sub_socket.subscribe("").await.expect("Failed to subscribe");

                for _ in 0..10 {
                    let recv_payload: String = sub_socket
                        .recv()
                        .await
                        .expect("Failed to recv")
                        .try_into()
                        .expect("Malformed string");
                    assert_eq!(cloned_payload, recv_payload);
                    cloned_sub_sender.send(()).await.unwrap();
                }
            });
        }
        drop(sub_results_sender);
        let res_vec: Vec<()> = sub_results.collect().await;
        assert_eq!(100, res_vec.len());

        server_stop_sender.send(()).unwrap();
    }
    let addrs = vec![
        "tcp://localhost:5553",
        "tcp://127.0.0.1:5554",
        "tcp://[::1]:5555",
        "tcp://127.0.0.1:5556",
    ];
    futures::future::join_all(addrs.into_iter().map(helper)).await;
}
<|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.

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

use std::ascii::AsciiExt;
use std::old_io as io;
use std::old_io::{File, Reader, Command, IoResult};
use std::old_io::process::ExitStatus;
use std::old_io::fs::PathExtensions;
use std::old_path::Path;
use std::os;
use std::thunk::Thunk;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn, ShouldFail};
use test::run_tests_console;
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 = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

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

    let (render_mode_string, 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.as_slice() {
        "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 io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lowercase().as_slice() == "list" && file.is_file() {
                    let tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.extend(tests.into_iter());
                }
            }
            _ => {}
        }
    }

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

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

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> IoResult<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 = match Command::new(os::self_exe_path().unwrap().join("servo"))
                            .args(servo_args.as_slice()).spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    let stderr = command.stderr.as_mut().unwrap().read_to_string().unwrap();

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

    run_tests_console(&test_opts, all_tests)
}

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

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

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 = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

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

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

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => 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)
        };

        // If we're running this directly, file.dir_path() might be relative.
        // (see issue #3521)
        let base = match file.dir_path().is_relative() {
            true  => os::getcwd().unwrap().join(file.dir_path()),
            false => file.dir_path()
        };

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

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut experimental = false;
        let mut fragment_identifier = None;
        let mut resolution = 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),
                "experimental" => experimental = true,
                _ => (),
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string());
            }
            if condition.starts_with("resolution=") {
                resolution = Some(condition.slice_from("resolution=".len()).to_string());
            }
        }

        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.iter().map(|x| x.clone()).collect(),
            is_flaky: render_mode.intersects(flakiness),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
            resolution: resolution,
        };

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

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: ShouldFail::No,
        },
        testfn: DynTestFn(Thunk::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(os::self_exe_path().unwrap().join("servo"));
    command
        .args(reftest.servo_args.as_slice())
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"].as_slice())
        .arg(png_filename.as_slice())
        .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");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    if let Some(ref resolution) = reftest.resolution {
        command.arg("--resolution");
        command.arg(resolution);
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

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

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

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

        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_str),
            (ReftestKind::Same, false) => panic!("rendering difference: {}", output_str),
            (ReftestKind::Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == ReftestKind::Same);
    }
}
<commit_msg>auto merge of #5177 : PeterReid/servo/issue5158, r=jdm<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(collections, core, env, io, hash, os, path, std_misc, test)]
#[macro_use] extern crate bitflags;
extern crate png;
extern crate test;
extern crate url;

use std::ascii::AsciiExt;
use std::env;
use std::old_io as io;
use std::old_io::{File, Reader, Command, IoResult};
use std::old_io::process::ExitStatus;
use std::old_io::fs::PathExtensions;
use std::old_path::Path;
use std::thunk::Thunk;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn, ShouldFail};
use test::run_tests_console;
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().map(|arg| arg.into_string().ok().expect("argument ws not a String")).collect();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

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

    let (render_mode_string, 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.as_slice() {
        "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 io::fs::walk_dir(&Path::new(base_path)).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lowercase().as_slice() == "list" && file.is_file() {
                    let tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.extend(tests.into_iter());
                }
            }
            _ => {}
        }
    }

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

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

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> IoResult<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 = match Command::new(servo_path())
                            .args(servo_args.as_slice()).spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    let stderr = command.stderr.as_mut().unwrap().read_to_string().unwrap();

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

    run_tests_console(&test_opts, all_tests)
}

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

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

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 = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

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

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

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => 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)
        };

        // If we're running this directly, file.dir_path() might be relative.
        // (see issue #3521)
        let base = match file.dir_path().is_relative() {
            true  => env::current_dir().unwrap().join(file.dir_path()),
            false => file.dir_path()
        };

        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 experimental = false;
        let mut fragment_identifier = None;
        let mut resolution = 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),
                "experimental" => experimental = true,
                _ => (),
            }
            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());
            }
        }

        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),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
            resolution: resolution,
        };

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

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: ShouldFail::No,
        },
        testfn: DynTestFn(Thunk::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
        .args(&reftest.servo_args[..])
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"].as_slice())
        .arg(png_filename.as_slice())
        .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");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    if let Some(ref resolution) = reftest.resolution {
        command.arg("--resolution");
        command.arg(resolution);
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

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

fn servo_path() -> Path {
    let current_exe = env::current_exe().ok().expect("Could not locate current executable");
    current_exe.dir_path().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 as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

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

        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_str),
            (ReftestKind::Same, false) => panic!("rendering difference: {}", output_str),
            (ReftestKind::Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == ReftestKind::Same);
    }
}
<|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(collections, core, env, io, hash, os, path, std_misc, test)]
#[macro_use] extern crate bitflags;
extern crate png;
extern crate test;
extern crate url;

use std::ascii::AsciiExt;
use std::env;
use std::old_io as io;
use std::old_io::{File, Reader, Command, IoResult};
use std::old_io::process::ExitStatus;
use std::old_io::fs::PathExtensions;
use std::old_path::Path;
use std::thunk::Thunk;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn, ShouldPanic};
use test::run_tests_console;
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.tail().split(|e| "--" == e.as_slice());

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

    let (render_mode_string, 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.as_slice() {
        "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 io::fs::walk_dir(&Path::new(base_path)).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lowercase().as_slice() == "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,
        run_benchmarks: false,
        nocapture: false,
        color: AutoColor,
    };

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

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> IoResult<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 = match Command::new(servo_path())
                            .args(servo_args.as_slice()).spawn() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    let stderr = command.stderr.as_mut().unwrap().read_to_string().unwrap();

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

    run_tests_console(&test_opts, all_tests)
}

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

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

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 = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

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

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

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => 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 = Path::new(env::current_dir().unwrap().to_str().unwrap()).join(file.dir_path());

        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 experimental = false;
        let mut fragment_identifier = None;
        let mut resolution = 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),
                "experimental" => experimental = true,
                _ => (),
            }
            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());
            }
        }

        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),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
            resolution: resolution,
        };

        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(Thunk::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
        .args(&reftest.servo_args[..])
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"].as_slice())
        .arg(png_filename.as_slice())
        .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");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    if let Some(ref resolution) = reftest.resolution {
        command.arg("--resolution");
        command.arg(resolution);
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

    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() -> Path {
    let current_exe = env::current_exe().ok().expect("Could not locate current executable");
    Path::new(current_exe.to_str().unwrap()).dir_path().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>Move to new path and io APIs in reftest.rs.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(collections, core, exit_status, fs_walk, io, old_io, path, path_ext, std_misc, test)]
#[macro_use] extern crate bitflags;
extern crate png;
extern crate test;
extern crate url;

use std::env;
use std::ffi::OsStr;
use std::fs::{PathExt, File, walk_dir};
use std::io::Read;
use std::old_io::{Reader, IoResult};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thunk::Thunk;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn, ShouldPanic};
use test::run_tests_console;
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.tail().split(|e| "--" == e.as_slice());

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

    let (render_mode_string, 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.as_slice() {
        "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::from_str("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,
        run_benchmarks: false,
        nocapture: false,
        color: AutoColor,
    };

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

fn run(test_opts: TestOpts, all_tests: Vec<TestDescAndFn>,
       servo_args: Vec<String>) -> IoResult<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 output = match Command::new(&servo_path()).args(&servo_args).output() {
        Ok(p) => p,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    let stderr = String::from_utf8(output.stderr).unwrap();

    if stderr.as_slice().contains("Unrecognized") {
        println!("Servo: {}", stderr.as_slice());
        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,
    experimental: bool,
    fragment_identifier: Option<String>,
    resolution: Option<String>,
}

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.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

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

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => 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 experimental = false;
        let mut fragment_identifier = None;
        let mut resolution = 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),
                "experimental" => experimental = true,
                _ => (),
            }
            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());
            }
        }

        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),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
            resolution: resolution,
        };

        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(Thunk::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[..])
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"].as_slice())
        .arg(png_filename.as_slice())
        .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");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    if let Some(ref resolution) = reftest.resolution {
        command.arg("--resolution");
        command.arg(resolution);
    }
    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>use std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum TokenType {
    NOT,
    EQ,
    LT,
    LE,

    ASSIGN,

    PLUS,
    MINUS,
    ASTER,
    SLASH,

    Comma,
    SemiColon,

    LParenth,
    RParenth,
    LBracket,
    RBracket,
    LBrace,
    RBrace,

    KwVoid,
    KwInt,
    KwFloat,

    KwIf,
    KwElse,
    KwWhile,
    KwReturn,

    Identifier(String),

    INum(i32),
    FNum(f32),

    Str(String),
}

pub struct Lexer<I: Iterator<Item = char>> {
    input: Peekable<I>,
    line: i32,
    col: i32,
}

impl<I: Iterator<Item = char>> Lexer<I> {
    fn new(input: I) -> Lexer<I> {
        Lexer {
            input: input.peekable(),
            line: 0,
            col: 0,
        }
    }

    fn lex_word(&mut self, c: char) -> TokenType {
        let mut ident = self.input
            .by_ref()
            .take_while(|&c| c.is_alphabetic() || c.is_numeric() || c == '_')
            .collect::<String>();
        ident.insert(0, c);

        match ident.as_ref() {
            // handle type keywords
            "void" => TokenType::KwVoid,
            "int" => TokenType::KwInt,
            "float" => TokenType::KwFloat,

            // handle keywords
            "if" => TokenType::KwIf,
            "else" => TokenType::KwElse,
            "while" => TokenType::KwWhile,
            "return" => TokenType::KwReturn,

            // handle identifier
            _ => TokenType::Identifier(ident),
        }
    }

    fn lex_number(&mut self, c: char) -> TokenType {
        let mut num = self.input
            .by_ref()
            .take_while(|&c| c.is_numeric() || c == '.')
            .collect::<String>();
        num.insert(0, c);

        if let Ok(n) = num.parse::<i32>() {
            TokenType::INum(n)
        } else if let Ok(n) = num.parse::<f32>() {
            TokenType::FNum(n)
        } else {
            panic!("wrong number format: {}", num);
        }
    }

    fn lex_string(&mut self) -> TokenType {
        TokenType::Str(self.input.by_ref().take_while(|&c| c != '\"').collect())
    }
}

impl<I: Iterator<Item = char>> Iterator for Lexer<I> {
    type Item = TokenType;

    fn next(&mut self) -> Option<TokenType> {
        loop {
            // handle EOF
            if self.input.peek().is_none() {
                return None;
            }

            return Some(match self.input.next().unwrap() {
                // handle WS
                ' ' | '\t' | '\n' | '\r' => continue,

                // handle Syntax
                ',' => TokenType::Comma,
                ';' => TokenType::SemiColon,
                '(' => TokenType::LParenth,
                ')' => TokenType::RParenth,
                '[' => TokenType::LBracket,
                ']' => TokenType::RBracket,
                '{' => TokenType::LBrace,
                '}' => TokenType::RBrace,

                // handle OPs (simple)
                '!' => TokenType::NOT,
                '+' => TokenType::PLUS,
                '-' => TokenType::MINUS,
                '*' => TokenType::ASTER,
                '/' => TokenType::SLASH,

                // handle OPs (complex)
                '=' => {
                    if let Some(&'=') = self.input.peek() {
                        self.input.next();
                        TokenType::EQ
                    } else {
                        TokenType::ASSIGN
                    }
                }
                '<' => {
                    if let Some(&'=') = self.input.peek() {
                        self.input.next();
                        TokenType::LE
                    } else {
                        TokenType::LT
                    }
                }

                // handle keywords, types and identifier
                c @ 'A'...'z' => self.lex_word(c),

                // handle number
                c @ '0'...'9' => self.lex_number(c),

                // handle strings
                '\"' => self.lex_string(),

                x @ _ => panic!("unexpected input: `{}`", x),
            });
        }
    }
}

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

    #[test]
    fn syntax() {
        let mut l = Lexer::new("! == < <= = + - * / , ; ( [ { } ] )".chars());
        assert_eq!(l.next(), Some(TokenType::NOT));
        assert_eq!(l.next(), Some(TokenType::EQ));
        assert_eq!(l.next(), Some(TokenType::LT));
        assert_eq!(l.next(), Some(TokenType::LE));
        assert_eq!(l.next(), Some(TokenType::ASSIGN));
        assert_eq!(l.next(), Some(TokenType::PLUS));
        assert_eq!(l.next(), Some(TokenType::MINUS));
        assert_eq!(l.next(), Some(TokenType::ASTER));
        assert_eq!(l.next(), Some(TokenType::SLASH));
        assert_eq!(l.next(), Some(TokenType::Comma));
        assert_eq!(l.next(), Some(TokenType::SemiColon));
        assert_eq!(l.next(), Some(TokenType::LParenth));
        assert_eq!(l.next(), Some(TokenType::LBracket));
        assert_eq!(l.next(), Some(TokenType::LBrace));
        assert_eq!(l.next(), Some(TokenType::RBrace));
        assert_eq!(l.next(), Some(TokenType::RBracket));
        assert_eq!(l.next(), Some(TokenType::RParenth));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn keywords() {
        let mut l = Lexer::new("void int float if else while return".chars());
        assert_eq!(l.next(), Some(TokenType::KwVoid));
        assert_eq!(l.next(), Some(TokenType::KwInt));
        assert_eq!(l.next(), Some(TokenType::KwFloat));
        assert_eq!(l.next(), Some(TokenType::KwIf));
        assert_eq!(l.next(), Some(TokenType::KwElse));
        assert_eq!(l.next(), Some(TokenType::KwWhile));
        assert_eq!(l.next(), Some(TokenType::KwReturn));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn identifiers() {
        let mut l = Lexer::new("foo b4r ba_".chars());
        assert_eq!(l.next(), Some(TokenType::Identifier("foo".to_string())));
        assert_eq!(l.next(), Some(TokenType::Identifier("b4r".to_string())));
        assert_eq!(l.next(), Some(TokenType::Identifier("ba_".to_string())));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn numbers() {
        let mut l = Lexer::new("42 3.14".chars());
        assert_eq!(l.next(), Some(TokenType::INum(42)));
        assert_eq!(l.next(), Some(TokenType::FNum(3.14)));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn strings() {
        let mut l = Lexer::new("\"foo\" \"bar\"".chars());
        assert_eq!(l.next(), Some(TokenType::Str("foo".to_string())));
        assert_eq!(l.next(), Some(TokenType::Str("bar".to_string())));
        assert_eq!(l.next(), None);
    }
}
<commit_msg>Front: Add booleans to lexer<commit_after>use std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum TokenType {
    NOT,
    EQ,
    LT,
    LE,

    ASSIGN,

    PLUS,
    MINUS,
    ASTER,
    SLASH,

    Comma,
    SemiColon,

    LParenth,
    RParenth,
    LBracket,
    RBracket,
    LBrace,
    RBrace,

    KwVoid,
    KwBool,
    KwInt,
    KwFloat,

    KwTrue,
    KwFalse,

    KwIf,
    KwElse,
    KwWhile,
    KwReturn,

    Identifier(String),

    INum(i32),
    FNum(f32),

    Str(String),
}

pub struct Lexer<I: Iterator<Item = char>> {
    input: Peekable<I>,
    line: i32,
    col: i32,
}

impl<I: Iterator<Item = char>> Lexer<I> {
    fn new(input: I) -> Lexer<I> {
        Lexer {
            input: input.peekable(),
            line: 0,
            col: 0,
        }
    }

    fn lex_word(&mut self, c: char) -> TokenType {
        let mut ident = self.input
            .by_ref()
            .take_while(|&c| c.is_alphabetic() || c.is_numeric() || c == '_')
            .collect::<String>();
        ident.insert(0, c);

        match ident.as_ref() {
            // handle type keywords
            "void" => TokenType::KwVoid,
            "bool" => TokenType::KwBool,
            "int" => TokenType::KwInt,
            "float" => TokenType::KwFloat,

            // handle bool literals
            "true" => TokenType::KwTrue,
            "false" => TokenType::KwFalse,

            // handle keywords
            "if" => TokenType::KwIf,
            "else" => TokenType::KwElse,
            "while" => TokenType::KwWhile,
            "return" => TokenType::KwReturn,

            // handle identifier
            _ => TokenType::Identifier(ident),
        }
    }

    fn lex_number(&mut self, c: char) -> TokenType {
        let mut num = self.input
            .by_ref()
            .take_while(|&c| c.is_numeric() || c == '.')
            .collect::<String>();
        num.insert(0, c);

        if let Ok(n) = num.parse::<i32>() {
            TokenType::INum(n)
        } else if let Ok(n) = num.parse::<f32>() {
            TokenType::FNum(n)
        } else {
            panic!("wrong number format: {}", num);
        }
    }

    fn lex_string(&mut self) -> TokenType {
        TokenType::Str(self.input.by_ref().take_while(|&c| c != '\"').collect())
    }
}

impl<I: Iterator<Item = char>> Iterator for Lexer<I> {
    type Item = TokenType;

    fn next(&mut self) -> Option<TokenType> {
        loop {
            // handle EOF
            if self.input.peek().is_none() {
                return None;
            }

            return Some(match self.input.next().unwrap() {
                // handle WS
                ' ' | '\t' | '\n' | '\r' => continue,

                // handle Syntax
                ',' => TokenType::Comma,
                ';' => TokenType::SemiColon,
                '(' => TokenType::LParenth,
                ')' => TokenType::RParenth,
                '[' => TokenType::LBracket,
                ']' => TokenType::RBracket,
                '{' => TokenType::LBrace,
                '}' => TokenType::RBrace,

                // handle Ops (simple)
                '!' => TokenType::NOT,
                '+' => TokenType::PLUS,
                '-' => TokenType::MINUS,
                '*' => TokenType::ASTER,
                '/' => TokenType::SLASH,

                // handle Ops (complex)
                '=' => {
                    if let Some(&'=') = self.input.peek() {
                        self.input.next();
                        TokenType::EQ
                    } else {
                        TokenType::ASSIGN
                    }
                }
                '<' => {
                    if let Some(&'=') = self.input.peek() {
                        self.input.next();
                        TokenType::LE
                    } else {
                        TokenType::LT
                    }
                }

                // handle keywords, types and identifier
                c @ 'A'...'z' => self.lex_word(c),

                // handle number
                c @ '0'...'9' => self.lex_number(c),

                // handle strings
                '\"' => self.lex_string(),

                x @ _ => panic!("unexpected input: `{}`", x),
            });
        }
    }
}

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

    #[test]
    fn syntax() {
        let mut l = Lexer::new("! == < <= = + - * / , ; ( [ { } ] )".chars());
        assert_eq!(l.next(), Some(TokenType::NOT));
        assert_eq!(l.next(), Some(TokenType::EQ));
        assert_eq!(l.next(), Some(TokenType::LT));
        assert_eq!(l.next(), Some(TokenType::LE));
        assert_eq!(l.next(), Some(TokenType::ASSIGN));
        assert_eq!(l.next(), Some(TokenType::PLUS));
        assert_eq!(l.next(), Some(TokenType::MINUS));
        assert_eq!(l.next(), Some(TokenType::ASTER));
        assert_eq!(l.next(), Some(TokenType::SLASH));
        assert_eq!(l.next(), Some(TokenType::Comma));
        assert_eq!(l.next(), Some(TokenType::SemiColon));
        assert_eq!(l.next(), Some(TokenType::LParenth));
        assert_eq!(l.next(), Some(TokenType::LBracket));
        assert_eq!(l.next(), Some(TokenType::LBrace));
        assert_eq!(l.next(), Some(TokenType::RBrace));
        assert_eq!(l.next(), Some(TokenType::RBracket));
        assert_eq!(l.next(), Some(TokenType::RParenth));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn keywords() {
        let mut l = Lexer::new("void bool int float true false if else while return".chars());
        assert_eq!(l.next(), Some(TokenType::KwVoid));
        assert_eq!(l.next(), Some(TokenType::KwBool));
        assert_eq!(l.next(), Some(TokenType::KwInt));
        assert_eq!(l.next(), Some(TokenType::KwFloat));
        assert_eq!(l.next(), Some(TokenType::KwTrue));
        assert_eq!(l.next(), Some(TokenType::KwFalse));
        assert_eq!(l.next(), Some(TokenType::KwIf));
        assert_eq!(l.next(), Some(TokenType::KwElse));
        assert_eq!(l.next(), Some(TokenType::KwWhile));
        assert_eq!(l.next(), Some(TokenType::KwReturn));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn identifiers() {
        let mut l = Lexer::new("foo b4r ba_".chars());
        assert_eq!(l.next(), Some(TokenType::Identifier("foo".to_string())));
        assert_eq!(l.next(), Some(TokenType::Identifier("b4r".to_string())));
        assert_eq!(l.next(), Some(TokenType::Identifier("ba_".to_string())));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn numbers() {
        let mut l = Lexer::new("42 3.14".chars());
        assert_eq!(l.next(), Some(TokenType::INum(42)));
        assert_eq!(l.next(), Some(TokenType::FNum(3.14)));
        assert_eq!(l.next(), None);
    }

    #[test]
    fn strings() {
        let mut l = Lexer::new("\"foo\" \"bar\"".chars());
        assert_eq!(l.next(), Some(TokenType::Str("foo".to_string())));
        assert_eq!(l.next(), Some(TokenType::Str("bar".to_string())));
        assert_eq!(l.next(), None);
    }
}
<|endoftext|>