<commit_before>use term;
use term::{Terminal,color};
use term::color::Color;
use term::attr::Attr;
use std::io::IoResult;

pub struct ShellConfig {
    pub color: bool,
    pub verbose: bool,
    pub tty: bool
}

enum AdequateTerminal<T> {
    NoColor(BasicTerminal<T>),
    Color(Box<Terminal<T>>)
}

pub struct Shell<T> {
    terminal: AdequateTerminal<T>,
    config: ShellConfig
}

impl<T: Writer + Send> Shell<T> {
    pub fn create(out: T, config: ShellConfig) -> Option<Shell<T>> {
        if config.tty && config.color {
            let term: Option<term::TerminfoTerminal<T>> = Terminal::new(out);
            term.map(|t| Shell { terminal: Color(box t as Box<Terminal<T>>), config: config })
        } else {
            Some(Shell { terminal: NoColor(BasicTerminal { writer: out }), config: config })
        }
    }

    pub fn verbose(&mut self, callback: |&mut Shell<T>| -> IoResult<()>) -> IoResult<()> {
        if self.config.verbose {
            return callback(self)
        }

        Ok(())
    }

    pub fn say<T: Str>(&mut self, message: T, color: Color) -> IoResult<()> {
        try!(self.reset());
        try!(self.fg(color));
        try!(self.write_line(message.as_slice()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }
}

impl<T: Writer + Send> Terminal<T> for Shell<T> {
    fn new(out: T) -> Option<Shell<T>> {
        Shell::create(out, ShellConfig { color: true, verbose: false, tty: false })
    }

    fn fg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.fg(color),
            NoColor(ref mut n) => n.fg(color)
        }
    }

    fn bg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.bg(color),
            NoColor(ref mut n) => n.bg(color)
        }
    }

    fn attr(&mut self, attr: Attr) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.attr(attr),
            NoColor(ref mut n) => n.attr(attr)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match self.terminal {
            Color(ref c) => c.supports_attr(attr),
            NoColor(ref n) => n.supports_attr(attr)
        }
    }

    fn reset(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.reset(),
            NoColor(ref mut n) => n.reset()
        }
    }

    fn unwrap(self) -> T {
        fail!("Can't unwrap a Shell");
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        match self.terminal {
            Color(ref c) => c.get_ref(),
            NoColor(ref n) => n.get_ref()
        }
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        match self.terminal {
            Color(ref mut c) => c.get_mut(),
            NoColor(ref mut n) => n.get_mut()
        }
    }
}

impl<T: Writer + Send> Writer for Shell<T> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}

pub struct BasicTerminal<T> {
    writer: T
}

impl<T: Writer + Send> Terminal<T> for BasicTerminal<T> {
    fn new(out: T) -> Option<BasicTerminal<T>> {
        Some(BasicTerminal { writer: out })
    }

    fn fg(&mut self, _: Color) -> IoResult<bool> {
        Ok(false)
    }

    fn bg(&mut self, _: Color) -> IoResult<bool> {
        Ok(false)
    }

    fn attr(&mut self, _: Attr) -> IoResult<bool> {
        Ok(false)
    }

    fn supports_attr(&self, _: Attr) -> bool {
        false
    }

    fn reset(&mut self) -> IoResult<()> {
        Ok(())
    }

    fn unwrap(self) -> T {
        self.writer
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        &self.writer
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        &mut self.writer
    }
}

impl<T: Writer + Send> Writer for BasicTerminal<T> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.writer.write(buf)
    }

    fn flush(&mut self) -> IoResult<()> {
        self.writer.flush()
    }
}

<commit_msg>Remove unnecessary BasicTerminal<commit_after>use term;
use term::{Terminal,color};
use term::color::Color;
use term::attr::Attr;
use std::io::IoResult;

pub struct ShellConfig {
    pub color: bool,
    pub verbose: bool,
    pub tty: bool
}

enum AdequateTerminal<T> {
    NoColor(T),
    Color(Box<Terminal<T>>)
}

pub struct Shell<T> {
    terminal: AdequateTerminal<T>,
    config: ShellConfig
}

impl<T: Writer + Send> Shell<T> {
    pub fn create(out: T, config: ShellConfig) -> Option<Shell<T>> {
        if config.tty && config.color {
            let term: Option<term::TerminfoTerminal<T>> = Terminal::new(out);
            term.map(|t| Shell { terminal: Color(box t as Box<Terminal<T>>), config: config })
        } else {
            Some(Shell { terminal: NoColor(out), config: config })
        }
    }

    pub fn verbose(&mut self, callback: |&mut Shell<T>| -> IoResult<()>) -> IoResult<()> {
        if self.config.verbose {
            return callback(self)
        }

        Ok(())
    }

    pub fn say<T: Str>(&mut self, message: T, color: Color) -> IoResult<()> {
        try!(self.reset());
        try!(self.fg(color));
        try!(self.write_line(message.as_slice()));
        try!(self.reset());
        try!(self.flush());
        Ok(())
    }
}

impl<T: Writer + Send> Terminal<T> for Shell<T> {
    fn new(out: T) -> Option<Shell<T>> {
        Shell::create(out, ShellConfig { color: true, verbose: false, tty: false })
    }

    fn fg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.fg(color),
            NoColor(_) => Ok(false)
        }
    }

    fn bg(&mut self, color: color::Color) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.bg(color),
            NoColor(_) => Ok(false)
        }
    }

    fn attr(&mut self, attr: Attr) -> IoResult<bool> {
        match self.terminal {
            Color(ref mut c) => c.attr(attr),
            NoColor(_) => Ok(false)
        }
    }

    fn supports_attr(&self, attr: Attr) -> bool {
        match self.terminal {
            Color(ref c) => c.supports_attr(attr),
            NoColor(_) => false
        }
    }

    fn reset(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.reset(),
            NoColor(_) => Ok(())
        }
    }

    fn unwrap(self) -> T {
        fail!("Can't unwrap a Shell");
    }

    fn get_ref<'a>(&'a self) -> &'a T {
        match self.terminal {
            Color(ref c) => c.get_ref(),
            NoColor(ref w) => w
        }
    }

    fn get_mut<'a>(&'a mut self) -> &'a mut T {
        match self.terminal {
            Color(ref mut c) => c.get_mut(),
            NoColor(ref mut w) => w
        }
    }
}

impl<T: Writer + Send> Writer for Shell<T> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.write(buf),
            NoColor(ref mut n) => n.write(buf)
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match self.terminal {
            Color(ref mut c) => c.flush(),
            NoColor(ref mut n) => n.flush()
        }
    }
}
<|endoftext|>


<commit_before>//! Defines the codec trait for decoding/encoding nodes

use eyre::{bail, Result};
use stencila_schema::Node;

// Export for use by other crates
pub use eyre;
pub use stencila_schema;

/// A codec for for decoding/encoding nodes
///
/// Defines similar functions to `serde_json` (and other `serde` crates) for
/// converting nodes to/from strings, files, readers etc.
pub trait Codec {
    fn from_str(_str: &str) -> Result<Node> {
        bail!("Decoding is not implemented for this format")
    }

    fn to_string(_node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
        bail!("Encoding is not implemented for this format")
    }
}

/// Common encoding options
///
/// Encoding functions (including those in plugins) are encouraged to respect these options
/// but are not required to. Indeed, some options do not apply for some formats.
/// For example, a PDF is always `standalone` (so if that option is set to `false` is it will be ignored).
/// Futhermore, some combinations of options are ineffectual e.g. a `theme` when `standalone: false`
#[derive(Clone)]
pub struct EncodeOptions {
    /// Whether to encode in compact form.
    ///
    /// Some formats (e.g HTML and JSON) can be encoded in either compact
    /// or "pretty-printed" ie.e. indented forms.
    pub compact: bool,

    /// Whether to ensure that the encoded document is standalone.
    ///
    /// Some formats (e.g. Markdown, DOCX) are always standalone, others
    /// can be frangments, or standalong documents (e.g HTML).
    pub standalone: bool,

    /// Whether to bundle local media files into the encoded document
    ///
    /// Some formats (e.g. DOCX, PDF) always bundle. For HTML,
    /// bundling means including media as data URIs rather than
    /// links to files.
    pub bundle: bool,

    /// The theme to apply to the encoded document
    ///
    /// Only applies to some formats (e.g. HTML, PDF, PNG).
    pub theme: String,
}

impl Default for EncodeOptions {
    fn default() -> Self {
        Self {
            compact: true,
            standalone: false,
            bundle: false,
            theme: "stencila".to_string(),
        }
    }
}
<commit_msg>chore(Rust): Add more `to_*` and `from_*` methods to crate<commit_after>//! Defines the codec trait for decoding/encoding nodes

use eyre::{bail, Result};
use std::{
    fs::File,
    io::{BufReader, BufWriter, Read, Write},
    path::Path,
};
use stencila_schema::Node;

// Export for use by other crates
pub use eyre;
pub use stencila_schema;

/// A codec for for decoding/encoding nodes
///
/// Defines similar functions to `serde_json` (and other `serde` crates) for
/// converting nodes to/from strings, files, readers etc.
pub trait Codec {
    fn from_str(_str: &str) -> Result<Node> {
        bail!("Decoding from string is not implemented for this format")
    }

    fn from_buffer<T: Read>(reader: &mut BufReader<T>) -> Result<Node> {
        let mut content = String::new();
        reader.read_to_string(&mut content)?;
        Self::from_str(&content)
    }

    fn from_file(file: &mut File) -> Result<Node> {
        let mut content = String::new();
        file.read_to_string(&mut content)?;
        Self::from_str(&content)
    }

    fn from_path<T: AsRef<Path>>(path: &T) -> Result<Node> {
        let mut file = File::open(path)?;
        Self::from_file(&mut file)
    }

    fn to_string(_node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
        bail!("Encoding to string is not implemented for this format")
    }

    fn to_buffer<T: Write>(
        node: &Node,
        writer: &mut BufWriter<T>,
        options: Option<EncodeOptions>,
    ) -> Result<()> {
        let content = Self::to_string(node, options)?;
        writer.write_all(content.as_bytes())?;
        Ok(())
    }

    fn to_file(node: &Node, file: &mut File, options: Option<EncodeOptions>) -> Result<()> {
        let content = Self::to_string(node, options)?;
        file.write_all(content.as_bytes())?;
        Ok(())
    }

    fn to_path<T: AsRef<Path>>(
        node: &Node,
        path: &T,
        options: Option<EncodeOptions>,
    ) -> Result<()> {
        let mut file = File::open(path)?;
        Self::to_file(node, &mut file, options)
    }
}

/// Common encoding options
///
/// Encoding functions (including those in plugins) are encouraged to respect these options
/// but are not required to. Indeed, some options do not apply for some formats.
/// For example, a PDF is always `standalone` (so if that option is set to `false` is it will be ignored).
/// Futhermore, some combinations of options are ineffectual e.g. a `theme` when `standalone: false`
#[derive(Clone)]
pub struct EncodeOptions {
    /// Whether to encode in compact form.
    ///
    /// Some formats (e.g HTML and JSON) can be encoded in either compact
    /// or "pretty-printed" ie.e. indented forms.
    pub compact: bool,

    /// Whether to ensure that the encoded document is standalone.
    ///
    /// Some formats (e.g. Markdown, DOCX) are always standalone, others
    /// can be frangments, or standalong documents (e.g HTML).
    pub standalone: bool,

    /// Whether to bundle local media files into the encoded document
    ///
    /// Some formats (e.g. DOCX, PDF) always bundle. For HTML,
    /// bundling means including media as data URIs rather than
    /// links to files.
    pub bundle: bool,

    /// The theme to apply to the encoded document
    ///
    /// Only applies to some formats (e.g. HTML, PDF, PNG).
    pub theme: String,
}

impl Default for EncodeOptions {
    fn default() -> Self {
        Self {
            compact: true,
            standalone: false,
            bundle: false,
            theme: "stencila".to_string(),
        }
    }
}
<|endoftext|>


<commit_before>use time;
use std::mem;
use std::num::{Float};
use image::{GenericImage, SubImage, Pixel};
use image::{ImageBuffer, Rgba};
use image;
use glutin;
use glium::{self, DisplayBuild};
use util::{self, AtlasBuilder, Atlas, V2, Rect, Rgb, Color};
use event::Event;
use renderer::{Renderer, Vertex};
use scancode;

pub static FONT_W: u32 = 8;
pub static FONT_H: u32 = 8;

/// The first font image in the atlas image set.
static FONT_IDX: usize = 0;

/// Image index of the solid color texture block.
static SOLID_IDX: usize = 96;

pub struct Canvas {
    title: String,
    dim: V2<u32>,
    frame_interval: Option<f64>,
    builder: AtlasBuilder,
}

/// Toplevel graphics drawing and input reading context.
impl Canvas {
    pub fn new() -> Canvas {
        let mut ret = Canvas {
            title: "window".to_string(),
            dim: V2(640, 360),
            frame_interval: None,
            builder: AtlasBuilder::new(),
        };
        ret.init_font();
        ret.init_solid();
        ret
    }

    pub fn set_title(mut self, title: &str) -> Canvas {
        self.title = title.to_string();
        self
    }

    /// Set the frame rate.
    pub fn set_frame_interval(mut self, interval_s: f64) -> Canvas {
        assert!(interval_s > 0.00001);
        self.frame_interval = Some(interval_s);
        self
    }

    /// Set the resolution.
    pub fn set_dim(mut self, dim: V2<u32>) -> Canvas {
        self.dim = dim;
        self
    }

    pub fn add_image<P: Pixel<u8> + 'static, I: GenericImage<P>>(
        &mut self, offset: V2<i32>, image: I) -> Image {
        Image(self.builder.push(offset, image))
    }

    /// Start running the engine, return an event iteration.
    pub fn run(&mut self) -> Context {
        Context::new(
            self.dim,
            self.title.as_slice(),
            self.frame_interval,
            Atlas::new(&self.builder))
    }

    /// Load the default font into the texture atlas.
    fn init_font(&mut self) {
        let mut font_sheet = util::color_key(
            &image::load_from_memory(include_bytes!("../assets/font.png")).unwrap(),
            &Rgb::new(0x80u8, 0x80u8, 0x80u8));
        for i in range(0u32, 96u32) {
            let x = 8u32 * (i % 16u32);
            let y = 8u32 * (i / 16u32);
            let Image(idx) = self.add_image(V2(0, -8), SubImage::new(&mut font_sheet, x, y, 8, 8));
            assert!(idx - i as usize == FONT_IDX);
        }
    }

    /// Load a solid color element into the texture atlas.
    fn init_solid(&mut self) {
        let image: ImageBuffer<Vec<u8>, u8, Rgba<u8>> = ImageBuffer::from_fn(1, 1, Box::new(|&: _, _| Rgba([0xffu8, 0xffu8, 0xffu8, 0xffu8])));
        let Image(idx) = self.add_image(V2(0, 0), image);
        assert!(idx == SOLID_IDX);
    }
}

/// Interface to render to a live display.
pub struct Context {
    display: glium::Display,
    events: Vec<glutin::Event>,
    renderer: Renderer,

    atlas: Atlas,

    state: State,
    frame_interval: Option<f64>,
    last_render_time: f64,
    image_dims: Vec<V2<u32>>,
    resolution: V2<u32>,
    window_resolution: V2<u32>,

    /// Time in seconds it took to render the last frame.
    pub render_duration: f64,
}

#[derive(PartialEq)]
enum State {
    Normal,
    EndFrame,
}

impl Context {
    fn new(
        dim: V2<u32>,
        title: &str,
        frame_interval: Option<f64>,
        atlas: Atlas) -> Context {

        let display = glutin::WindowBuilder::new()
            .with_title(title.to_string())
            .with_dimensions(dim.0 as usize, dim.1 as usize)
            .build_glium().unwrap();

        let (w, h) = display.get_framebuffer_dimensions();

        let tex_image = image::imageops::flip_vertical(&atlas.image);
        let renderer = Renderer::new(&display, tex_image);

        let mut dims = vec![];

        for i in range(0, atlas.vertices.len()) {
            dims.push(atlas.vertices[i].1.map(|x| x as u32));
        }

        Context {
            display: display,
            events: Vec::new(),
            renderer: renderer,

            atlas: atlas,

            state: State::Normal,
            frame_interval: frame_interval,
            last_render_time: time::precise_time_s(),
            image_dims: dims,
            resolution: dim,
            window_resolution: V2(w as u32, h as u32),

            render_duration: 0.1f64,
        }
    }

    /// Clear the screen
    pub fn clear(&mut self) {
        // TODO: use the color.
        self.renderer.clear();
    }

    fn window_to_device(&self, window_pos: V2<i32>, z: f32) -> [f32; 3] {
        let V2(w, h) = self.window_resolution;
        let Rect(V2(rx, ry), V2(rw, _)) = pixel_perfect(self.resolution, self.window_resolution);
        let zoom = (rw as f32) / (self.resolution.0 as f32);
        [-1.0 + (2.0 * (rx as f32 + window_pos.0 as f32 * zoom) / w as f32),
          1.0 - (2.0 * (ry as f32 + window_pos.1 as f32 * zoom) / h as f32),
         z]
    }

    fn tri_vtx(&mut self, window_pos: V2<i32>, layer: f32, texture_pos: V2<f32>, color: [f32; 4]) {
        let pos = self.window_to_device(window_pos, layer);
        let vertex = Vertex {
            pos: pos,
            color: color,
            tex_coord: texture_pos.to_array() };
        self.renderer.vertices.push(vertex);
        self.renderer.indices.push(self.renderer.vertices.len() as u16 - 1);
    }

    pub fn draw_image<C: Color>(&mut self, offset: V2<i32>, layer: f32, Image(idx): Image, color: &C) {
        let color = color.to_rgba();
        let rect = self.atlas.vertices[idx] + offset;
        let tex = self.atlas.texcoords[idx];

        self.tri_vtx(rect.p0(), layer, tex.p0(), color);
        self.tri_vtx(rect.p1(), layer, tex.p1(), color);
        self.tri_vtx(rect.p2(), layer, tex.p2(), color);
        self.tri_vtx(rect.p0(), layer, tex.p0(), color);
        self.tri_vtx(rect.p2(), layer, tex.p2(), color);
        self.tri_vtx(rect.p3(), layer, tex.p3(), color);
    }

    pub fn draw_tri<C: Color>(&mut self, layer: f32, p: [V2<i32>; 3], c: [C; 3]) {
        let tex = self.atlas.texcoords[SOLID_IDX].0;
        for i in range(0, 3) { self.tri_vtx(p[i], layer, tex, c[i].to_rgba()); }
    }

    pub fn font_image(&self, c: char) -> Option<Image> {
        let idx = c as usize;
        // Hardcoded limiting of the font to printable ASCII.
        if idx >= 32 && idx < 128 {
            Some(Image((idx - 32) as usize + FONT_IDX))
        } else {
            None
        }
    }

    pub fn image_dim(&self, Image(idx): Image) -> V2<u32> {
        self.image_dims[idx]
    }
}

impl<'a> Iterator for Context {
    type Item=Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        // After a render event, control will return here on a new
        // iter call. Do post-render work here.
        if self.state == State::EndFrame {
            self.state = State::Normal;

            let mut target = self.display.draw();
            self.renderer.draw(&self.display, &mut target);
            target.finish();
        }

        loop {
            self.events.push_all(self.display.poll_events().as_slice());

            if !self.events.is_empty() {
                match self.events.remove(0) {
                    glutin::Event::ReceivedCharacter(ch) => {
                        return Some(Event::Char(ch));
                    }
                    glutin::Event::KeyboardInput(action, scan, _vko) => {
                        if (scan as usize) < scancode::MAP.len() {
                            if let Some(key) = scancode::MAP[scan as usize] {
                                return Some(if action == glutin::ElementState::Pressed {
                                    Event::KeyPressed(key)
                                }
                                else {
                                    Event::KeyReleased(key)
                                });
                            }
                        }
                    }
                    glutin::Event::Closed => {
                        return None;
                    }
                    // TODO Mouse events.
                    _ => ()
                }
            }

            let t = time::precise_time_s();
            if self.frame_interval.map_or(true,
                |x| t - self.last_render_time >= x) {
                let delta = t - self.last_render_time;
                let sensitivity = 0.25f64;
                self.render_duration = (1f64 - sensitivity) * self.render_duration + sensitivity * delta;

                self.last_render_time = t;

                // Time to render, must return a handle to self.
                // XXX: Need unsafe hackery to get around lifetimes check.
                self.state = State::EndFrame;

                let (w, h) = self.display.get_framebuffer_dimensions();
                self.window_resolution = V2(w as u32, h as u32);

                unsafe {
                    return Some(Event::Render(mem::transmute(self)))
                }
            }
        }
    }
}

/// Drawable images stored in the Canvas.
#[derive(Copy, Clone, PartialEq)]
pub struct Image(usize);

/// A pixel perfect centered and scaled rectangle of resolution dim in a
/// window of size area.
fn pixel_perfect(canvas: V2<u32>, window: V2<u32>) -> Rect<u32> {
    let mut scale = (window.0 as f32 / canvas.0 as f32)
        .min(window.1 as f32 / canvas.1 as f32);

    if scale > 1.0 {
        // Snap to pixel scale if more than 1 window pixel per canvas pixel.
        scale = scale.floor();
    }

    let dim = V2((scale * canvas.0 as f32) as u32, (scale * canvas.1 as f32) as u32);
    let offset = (window - dim) / 2;
    Rect(offset, dim)
}
<commit_msg>Glutin int API<commit_after>use time;
use std::mem;
use std::num::{Float};
use image::{GenericImage, SubImage, Pixel};
use image::{ImageBuffer, Rgba};
use image;
use glutin;
use glium::{self, DisplayBuild};
use util::{self, AtlasBuilder, Atlas, V2, Rect, Rgb, Color};
use event::Event;
use renderer::{Renderer, Vertex};
use scancode;

pub static FONT_W: u32 = 8;
pub static FONT_H: u32 = 8;

/// The first font image in the atlas image set.
static FONT_IDX: usize = 0;

/// Image index of the solid color texture block.
static SOLID_IDX: usize = 96;

pub struct Canvas {
    title: String,
    dim: V2<u32>,
    frame_interval: Option<f64>,
    builder: AtlasBuilder,
}

/// Toplevel graphics drawing and input reading context.
impl Canvas {
    pub fn new() -> Canvas {
        let mut ret = Canvas {
            title: "window".to_string(),
            dim: V2(640, 360),
            frame_interval: None,
            builder: AtlasBuilder::new(),
        };
        ret.init_font();
        ret.init_solid();
        ret
    }

    pub fn set_title(mut self, title: &str) -> Canvas {
        self.title = title.to_string();
        self
    }

    /// Set the frame rate.
    pub fn set_frame_interval(mut self, interval_s: f64) -> Canvas {
        assert!(interval_s > 0.00001);
        self.frame_interval = Some(interval_s);
        self
    }

    /// Set the resolution.
    pub fn set_dim(mut self, dim: V2<u32>) -> Canvas {
        self.dim = dim;
        self
    }

    pub fn add_image<P: Pixel<u8> + 'static, I: GenericImage<P>>(
        &mut self, offset: V2<i32>, image: I) -> Image {
        Image(self.builder.push(offset, image))
    }

    /// Start running the engine, return an event iteration.
    pub fn run(&mut self) -> Context {
        Context::new(
            self.dim,
            self.title.as_slice(),
            self.frame_interval,
            Atlas::new(&self.builder))
    }

    /// Load the default font into the texture atlas.
    fn init_font(&mut self) {
        let mut font_sheet = util::color_key(
            &image::load_from_memory(include_bytes!("../assets/font.png")).unwrap(),
            &Rgb::new(0x80u8, 0x80u8, 0x80u8));
        for i in range(0u32, 96u32) {
            let x = 8u32 * (i % 16u32);
            let y = 8u32 * (i / 16u32);
            let Image(idx) = self.add_image(V2(0, -8), SubImage::new(&mut font_sheet, x, y, 8, 8));
            assert!(idx - i as usize == FONT_IDX);
        }
    }

    /// Load a solid color element into the texture atlas.
    fn init_solid(&mut self) {
        let image: ImageBuffer<Vec<u8>, u8, Rgba<u8>> = ImageBuffer::from_fn(1, 1, Box::new(|&: _, _| Rgba([0xffu8, 0xffu8, 0xffu8, 0xffu8])));
        let Image(idx) = self.add_image(V2(0, 0), image);
        assert!(idx == SOLID_IDX);
    }
}

/// Interface to render to a live display.
pub struct Context {
    display: glium::Display,
    events: Vec<glutin::Event>,
    renderer: Renderer,

    atlas: Atlas,

    state: State,
    frame_interval: Option<f64>,
    last_render_time: f64,
    image_dims: Vec<V2<u32>>,
    resolution: V2<u32>,
    window_resolution: V2<u32>,

    /// Time in seconds it took to render the last frame.
    pub render_duration: f64,
}

#[derive(PartialEq)]
enum State {
    Normal,
    EndFrame,
}

impl Context {
    fn new(
        dim: V2<u32>,
        title: &str,
        frame_interval: Option<f64>,
        atlas: Atlas) -> Context {

        let display = glutin::WindowBuilder::new()
            .with_title(title.to_string())
            .with_dimensions(dim.0 as u32, dim.1 as u32)
            .build_glium().unwrap();

        let (w, h) = display.get_framebuffer_dimensions();

        let tex_image = image::imageops::flip_vertical(&atlas.image);
        let renderer = Renderer::new(&display, tex_image);

        let mut dims = vec![];

        for i in range(0, atlas.vertices.len()) {
            dims.push(atlas.vertices[i].1.map(|x| x as u32));
        }

        Context {
            display: display,
            events: Vec::new(),
            renderer: renderer,

            atlas: atlas,

            state: State::Normal,
            frame_interval: frame_interval,
            last_render_time: time::precise_time_s(),
            image_dims: dims,
            resolution: dim,
            window_resolution: V2(w as u32, h as u32),

            render_duration: 0.1f64,
        }
    }

    /// Clear the screen
    pub fn clear(&mut self) {
        // TODO: use the color.
        self.renderer.clear();
    }

    fn window_to_device(&self, window_pos: V2<i32>, z: f32) -> [f32; 3] {
        let V2(w, h) = self.window_resolution;
        let Rect(V2(rx, ry), V2(rw, _)) = pixel_perfect(self.resolution, self.window_resolution);
        let zoom = (rw as f32) / (self.resolution.0 as f32);
        [-1.0 + (2.0 * (rx as f32 + window_pos.0 as f32 * zoom) / w as f32),
          1.0 - (2.0 * (ry as f32 + window_pos.1 as f32 * zoom) / h as f32),
         z]
    }

    fn tri_vtx(&mut self, window_pos: V2<i32>, layer: f32, texture_pos: V2<f32>, color: [f32; 4]) {
        let pos = self.window_to_device(window_pos, layer);
        let vertex = Vertex {
            pos: pos,
            color: color,
            tex_coord: texture_pos.to_array() };
        self.renderer.vertices.push(vertex);
        self.renderer.indices.push(self.renderer.vertices.len() as u16 - 1);
    }

    pub fn draw_image<C: Color>(&mut self, offset: V2<i32>, layer: f32, Image(idx): Image, color: &C) {
        let color = color.to_rgba();
        let rect = self.atlas.vertices[idx] + offset;
        let tex = self.atlas.texcoords[idx];

        self.tri_vtx(rect.p0(), layer, tex.p0(), color);
        self.tri_vtx(rect.p1(), layer, tex.p1(), color);
        self.tri_vtx(rect.p2(), layer, tex.p2(), color);
        self.tri_vtx(rect.p0(), layer, tex.p0(), color);
        self.tri_vtx(rect.p2(), layer, tex.p2(), color);
        self.tri_vtx(rect.p3(), layer, tex.p3(), color);
    }

    pub fn draw_tri<C: Color>(&mut self, layer: f32, p: [V2<i32>; 3], c: [C; 3]) {
        let tex = self.atlas.texcoords[SOLID_IDX].0;
        for i in range(0, 3) { self.tri_vtx(p[i], layer, tex, c[i].to_rgba()); }
    }

    pub fn font_image(&self, c: char) -> Option<Image> {
        let idx = c as usize;
        // Hardcoded limiting of the font to printable ASCII.
        if idx >= 32 && idx < 128 {
            Some(Image((idx - 32) as usize + FONT_IDX))
        } else {
            None
        }
    }

    pub fn image_dim(&self, Image(idx): Image) -> V2<u32> {
        self.image_dims[idx]
    }
}

impl<'a> Iterator for Context {
    type Item=Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        // After a render event, control will return here on a new
        // iter call. Do post-render work here.
        if self.state == State::EndFrame {
            self.state = State::Normal;

            let mut target = self.display.draw();
            self.renderer.draw(&self.display, &mut target);
            target.finish();
        }

        loop {
            self.events.push_all(self.display.poll_events().as_slice());

            if !self.events.is_empty() {
                match self.events.remove(0) {
                    glutin::Event::ReceivedCharacter(ch) => {
                        return Some(Event::Char(ch));
                    }
                    glutin::Event::KeyboardInput(action, scan, _vko) => {
                        if (scan as usize) < scancode::MAP.len() {
                            if let Some(key) = scancode::MAP[scan as usize] {
                                return Some(if action == glutin::ElementState::Pressed {
                                    Event::KeyPressed(key)
                                }
                                else {
                                    Event::KeyReleased(key)
                                });
                            }
                        }
                    }
                    glutin::Event::Closed => {
                        return None;
                    }
                    // TODO Mouse events.
                    _ => ()
                }
            }

            let t = time::precise_time_s();
            if self.frame_interval.map_or(true,
                |x| t - self.last_render_time >= x) {
                let delta = t - self.last_render_time;
                let sensitivity = 0.25f64;
                self.render_duration = (1f64 - sensitivity) * self.render_duration + sensitivity * delta;

                self.last_render_time = t;

                // Time to render, must return a handle to self.
                // XXX: Need unsafe hackery to get around lifetimes check.
                self.state = State::EndFrame;

                let (w, h) = self.display.get_framebuffer_dimensions();
                self.window_resolution = V2(w as u32, h as u32);

                unsafe {
                    return Some(Event::Render(mem::transmute(self)))
                }
            }
        }
    }
}

/// Drawable images stored in the Canvas.
#[derive(Copy, Clone, PartialEq)]
pub struct Image(usize);

/// A pixel perfect centered and scaled rectangle of resolution dim in a
/// window of size area.
fn pixel_perfect(canvas: V2<u32>, window: V2<u32>) -> Rect<u32> {
    let mut scale = (window.0 as f32 / canvas.0 as f32)
        .min(window.1 as f32 / canvas.1 as f32);

    if scale > 1.0 {
        // Snap to pixel scale if more than 1 window pixel per canvas pixel.
        scale = scale.floor();
    }

    let dim = V2((scale * canvas.0 as f32) as u32, (scale * canvas.1 as f32) as u32);
    let offset = (window - dim) / 2;
    Rect(offset, dim)
}
<|endoftext|>


<commit_before>use std::path::Path;

use regex::Regex;

use crate::rustc_stderr::Level;

use color_eyre::eyre::{bail, ensure, eyre, Result};

#[cfg(test)]
mod tests;

/// This crate supports various magic comments that get parsed as file-specific
/// configuration values. This struct parses them all in one go and then they
/// get processed by their respective use sites.
#[derive(Default, Debug)]
pub(crate) struct Comments {
    /// List of revision names to execute. Can only be speicified once
    pub revisions: Option<Vec<String>>,
    /// Don't run this test if any of these filters apply
    pub ignore: Vec<Condition>,
    /// Only run this test if all of these filters apply
    pub only: Vec<Condition>,
    /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar
    pub stderr_per_bitwidth: bool,
    /// Additional flags to pass to the executable
    pub compile_flags: Vec<String>,
    /// Additional env vars to set for the executable
    pub env_vars: Vec<(String, String)>,
    /// Normalizations to apply to the stderr output before emitting it to disk
    pub normalize_stderr: Vec<(Regex, String)>,
    /// An arbitrary pattern to look for in the stderr.
    pub error_pattern: Option<(String, usize)>,
    pub error_matches: Vec<ErrorMatch>,
}

/// The conditions used for "ignore" and "only" filters.
#[derive(Debug)]
pub(crate) enum Condition {
    /// The given string must appear in the target.
    Target(String),
    /// Tests that the bitwidth is the given one.
    Bitwidth(u8),
}

#[derive(Debug)]
pub(crate) struct ErrorMatch {
    pub matched: String,
    pub revision: Option<String>,
    pub level: Option<Level>,
    /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found).
    pub definition_line: usize,
    /// The line this pattern is expecting to find a message in.
    pub line: usize,
}

impl Condition {
    fn parse(c: &str) -> Self {
        if let Some(bits) = c.strip_suffix("bit") {
            let bits: u8 = bits.parse().expect(
                "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N",
            );
            Condition::Bitwidth(bits)
        } else {
            Condition::Target(c.to_owned())
        }
    }
}

impl Comments {
    pub(crate) fn parse_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        Self::parse(path, &content)
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    ///
    /// This function will only parse `//@` and `//~` style comments (and the `//[xxx]~` variant)
    /// and ignore all others
    fn parse_checked(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::default();

        let mut fallthrough_to = None; // The line that a `|` will refer to.
        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
                err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
            })?;
        }
        Ok(this)
    }

    fn parse_checked_line(
        &mut self,
        l: usize,
        fallthrough_to: &mut Option<usize>,
        line: &str,
    ) -> Result<()> {
        if let Some((_, command)) = line.split_once("//@") {
            self.parse_command(command.trim(), l)
        } else if let Some((_, pattern)) = line.split_once("//~") {
            self.parse_pattern(pattern, fallthrough_to, l)
        } else if let Some((_, pattern)) = line.split_once("//[") {
            self.parse_revisioned_pattern(pattern, fallthrough_to, l)
        } else {
            *fallthrough_to = None;
            Ok(())
        }
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::parse_checked(path, content)?;
        if content.contains("//@") {
            // Migration mode: if new syntax is used, ignore all old syntax
            return Ok(this);
        }

        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            if let Some(revisions) = line.strip_prefix("// revisions:") {
                assert_eq!(
                    this.revisions,
                    None,
                    "{}:{l}, cannot specifiy revisions twice",
                    path.display()
                );
                this.revisions =
                    Some(revisions.split_whitespace().map(|s| s.to_string()).collect());
            }
            if let Some(s) = line.strip_prefix("// ignore-") {
                let s = s
                    .split_once(|c: char| c == ':' || c.is_whitespace())
                    .map(|(s, _)| s)
                    .unwrap_or(s);
                this.ignore.push(Condition::parse(s));
            }
            if let Some(s) = line.strip_prefix("// only-") {
                let s = s
                    .split_once(|c: char| c == ':' || c.is_whitespace())
                    .map(|(s, _)| s)
                    .unwrap_or(s);
                this.only.push(Condition::parse(s));
            }
            if line.starts_with("// stderr-per-bitwidth") {
                assert!(
                    !this.stderr_per_bitwidth,
                    "{}:{l}, cannot specifiy stderr-per-bitwidth twice",
                    path.display()
                );
                this.stderr_per_bitwidth = true;
            }
            if let Some(s) = line.strip_prefix("// compile-flags:") {
                this.compile_flags.extend(s.split_whitespace().map(|s| s.to_string()));
            }
            if let Some(s) = line.strip_prefix("// rustc-env:") {
                for env in s.split_whitespace() {
                    if let Some((k, v)) = env.split_once('=') {
                        this.env_vars.push((k.to_string(), v.to_string()));
                    }
                }
            }
            if let Some(s) = line.strip_prefix("// normalize-stderr-test:") {
                let (from, to) = s.split_once("->").expect("normalize-stderr-test needs a `->`");
                let from = from.trim().trim_matches('"');
                let to = to.trim().trim_matches('"');
                let from = Regex::new(from).unwrap();
                this.normalize_stderr.push((from, to.to_string()));
            }
            if let Some(s) = line.strip_prefix("// error-pattern:") {
                assert_eq!(
                    this.error_pattern,
                    None,
                    "{}:{l}, cannot specifiy error_pattern twice",
                    path.display()
                );
                this.error_pattern = Some((s.trim().to_string(), l));
            }
        }
        Ok(this)
    }

    fn parse_command(&mut self, command: &str, l: usize) -> Result<()> {
        // Commands are letters or dashes, grab everything until the first character that is neither of those.
        let (command, args) =
            match command.chars().position(|c: char| !c.is_alphabetic() && c != '-') {
                None => (command, ""),
                Some(i) => {
                    let (command, args) = command.split_at(i);
                    let mut args = args.chars();
                    // Commands are separated from their arguments by ':' or ' '
                    let next = args
                        .next()
                        .expect("the `position` above guarantees that there is at least one char");
                    let args = match next {
                        ':' | ' ' => args.as_str(),
                        _ => bail!("expected space or `:`, got `{next}`"),
                    };
                    (command, args.trim())
                }
            };

        match command {
            "revisions" => {
                ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
                self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
            }
            "compile-flags" => {
                self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string()));
            }
            "rustc-env" =>
                for env in args.split_whitespace() {
                    let (k, v) = env.split_once('=').ok_or_else(|| {
                        eyre!("environment variables must be key/value pairs separated by a `=`")
                    })?;
                    self.env_vars.push((k.to_string(), v.to_string()));
                },
            "normalize-stderr-test" => {
                fn parse_str(s: &str) -> Result<(&str, &str)> {
                    let mut chars = s.char_indices();
                    match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 {
                        '"' => {
                            let s = chars.as_str();
                            let mut escaped = false;
                            for (i, c) in chars {
                                if escaped {
                                    escaped = false;
                                } else if c == '"' {
                                    return Ok((&s[..(i - 1)], s[i..].trim_start()))
                                } else {
                                    escaped = c == '\\';
                                }
                            }
                            bail!("no closing quotes found for {s}")
                        }
                        c => bail!("expected '\"', got {c}"),
                    }
                }

                let (from, rest) = parse_str(args)?;

                let to = rest.strip_prefix("->").ok_or_else(|| {
                    eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`")
                })?.trim_start();
                let (to, rest) = parse_str(to)?;

                ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}");

                let from = Regex::new(from)?;
                self.normalize_stderr.push((from, to.to_string()));
            }
            "error-pattern" => {
                ensure!(
                    self.error_pattern.is_none(),
                    "cannot specifiy error_pattern twice, previous: {:?}",
                    self.error_pattern
                );
                self.error_pattern = Some((args.trim().to_string(), l));
            }
            "stderr-per-bitwidth" => {
                ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
                self.stderr_per_bitwidth = true;
            }
            command => {
                if let Some(s) = command.strip_prefix("ignore-") {
                    self.ignore.push(Condition::parse(s));
                    return Ok(());
                }

                if let Some(s) = command.strip_prefix("only-") {
                    self.only.push(Condition::parse(s));
                    return Ok(());
                }
                bail!("unknown command {command}");
            }
        }

        Ok(())
    }

    fn parse_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        self.parse_pattern_inner(pattern, fallthrough_to, None, l)
    }

    fn parse_revisioned_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        let (revision, pattern) =
            pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
        if let Some(pattern) = pattern.strip_prefix('~') {
            self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
        } else {
            bail!("revisioned pattern must have `~` following the `]`");
        }
    }

    // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE)?:?(?P<text>.*)
    fn parse_pattern_inner(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        revision: Option<String>,
        l: usize,
    ) -> Result<()> {
        let (match_line, pattern) =
            match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
                '|' =>
                    (
                        *fallthrough_to
                            .as_mut()
                            .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
                        &pattern[1..],
                    ),
                '^' => {
                    let offset = pattern.chars().take_while(|&c| c == '^').count();
                    (l - offset, &pattern[offset..])
                }
                _ => (l, pattern),
            };

        let (level, pattern) = match pattern.trim_start().split_once(|c| matches!(c, ':' | ' ')) {
            None => (None, pattern),
            Some((level, pattern_without_level)) =>
                match level.parse().ok() {
                    Some(level) => (Some(level), pattern_without_level),
                    None => (None, pattern),
                },
        };

        let matched = pattern.trim().to_string();

        ensure!(!matched.is_empty(), "no pattern specified");

        *fallthrough_to = Some(match_line);

        self.error_matches.push(ErrorMatch {
            matched,
            revision,
            level,
            definition_line: l,
            line: match_line,
        });

        Ok(())
    }
}
<commit_msg>Rustfmt<commit_after>use std::path::Path;

use regex::Regex;

use crate::rustc_stderr::Level;

use color_eyre::eyre::{bail, ensure, eyre, Result};

#[cfg(test)]
mod tests;

/// This crate supports various magic comments that get parsed as file-specific
/// configuration values. This struct parses them all in one go and then they
/// get processed by their respective use sites.
#[derive(Default, Debug)]
pub(crate) struct Comments {
    /// List of revision names to execute. Can only be speicified once
    pub revisions: Option<Vec<String>>,
    /// Don't run this test if any of these filters apply
    pub ignore: Vec<Condition>,
    /// Only run this test if all of these filters apply
    pub only: Vec<Condition>,
    /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar
    pub stderr_per_bitwidth: bool,
    /// Additional flags to pass to the executable
    pub compile_flags: Vec<String>,
    /// Additional env vars to set for the executable
    pub env_vars: Vec<(String, String)>,
    /// Normalizations to apply to the stderr output before emitting it to disk
    pub normalize_stderr: Vec<(Regex, String)>,
    /// An arbitrary pattern to look for in the stderr.
    pub error_pattern: Option<(String, usize)>,
    pub error_matches: Vec<ErrorMatch>,
}

/// The conditions used for "ignore" and "only" filters.
#[derive(Debug)]
pub(crate) enum Condition {
    /// The given string must appear in the target.
    Target(String),
    /// Tests that the bitwidth is the given one.
    Bitwidth(u8),
}

#[derive(Debug)]
pub(crate) struct ErrorMatch {
    pub matched: String,
    pub revision: Option<String>,
    pub level: Option<Level>,
    /// The line where the message was defined, for reporting issues with it (e.g. in case it wasn't found).
    pub definition_line: usize,
    /// The line this pattern is expecting to find a message in.
    pub line: usize,
}

impl Condition {
    fn parse(c: &str) -> Self {
        if let Some(bits) = c.strip_suffix("bit") {
            let bits: u8 = bits.parse().expect(
                "ignore/only filter ending in 'bit' must be of the form 'Nbit' for some integer N",
            );
            Condition::Bitwidth(bits)
        } else {
            Condition::Target(c.to_owned())
        }
    }
}

impl Comments {
    pub(crate) fn parse_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        Self::parse(path, &content)
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    ///
    /// This function will only parse `//@` and `//~` style comments (and the `//[xxx]~` variant)
    /// and ignore all others
    fn parse_checked(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::default();

        let mut fallthrough_to = None; // The line that a `|` will refer to.
        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
                err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
            })?;
        }
        Ok(this)
    }

    fn parse_checked_line(
        &mut self,
        l: usize,
        fallthrough_to: &mut Option<usize>,
        line: &str,
    ) -> Result<()> {
        if let Some((_, command)) = line.split_once("//@") {
            self.parse_command(command.trim(), l)
        } else if let Some((_, pattern)) = line.split_once("//~") {
            self.parse_pattern(pattern, fallthrough_to, l)
        } else if let Some((_, pattern)) = line.split_once("//[") {
            self.parse_revisioned_pattern(pattern, fallthrough_to, l)
        } else {
            *fallthrough_to = None;
            Ok(())
        }
    }

    /// Parse comments in `content`.
    /// `path` is only used to emit diagnostics if parsing fails.
    pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
        let mut this = Self::parse_checked(path, content)?;
        if content.contains("//@") {
            // Migration mode: if new syntax is used, ignore all old syntax
            return Ok(this);
        }

        for (l, line) in content.lines().enumerate() {
            let l = l + 1; // enumerate starts at 0, but line numbers start at 1
            if let Some(revisions) = line.strip_prefix("// revisions:") {
                assert_eq!(
                    this.revisions,
                    None,
                    "{}:{l}, cannot specifiy revisions twice",
                    path.display()
                );
                this.revisions =
                    Some(revisions.split_whitespace().map(|s| s.to_string()).collect());
            }
            if let Some(s) = line.strip_prefix("// ignore-") {
                let s = s
                    .split_once(|c: char| c == ':' || c.is_whitespace())
                    .map(|(s, _)| s)
                    .unwrap_or(s);
                this.ignore.push(Condition::parse(s));
            }
            if let Some(s) = line.strip_prefix("// only-") {
                let s = s
                    .split_once(|c: char| c == ':' || c.is_whitespace())
                    .map(|(s, _)| s)
                    .unwrap_or(s);
                this.only.push(Condition::parse(s));
            }
            if line.starts_with("// stderr-per-bitwidth") {
                assert!(
                    !this.stderr_per_bitwidth,
                    "{}:{l}, cannot specifiy stderr-per-bitwidth twice",
                    path.display()
                );
                this.stderr_per_bitwidth = true;
            }
            if let Some(s) = line.strip_prefix("// compile-flags:") {
                this.compile_flags.extend(s.split_whitespace().map(|s| s.to_string()));
            }
            if let Some(s) = line.strip_prefix("// rustc-env:") {
                for env in s.split_whitespace() {
                    if let Some((k, v)) = env.split_once('=') {
                        this.env_vars.push((k.to_string(), v.to_string()));
                    }
                }
            }
            if let Some(s) = line.strip_prefix("// normalize-stderr-test:") {
                let (from, to) = s.split_once("->").expect("normalize-stderr-test needs a `->`");
                let from = from.trim().trim_matches('"');
                let to = to.trim().trim_matches('"');
                let from = Regex::new(from).unwrap();
                this.normalize_stderr.push((from, to.to_string()));
            }
            if let Some(s) = line.strip_prefix("// error-pattern:") {
                assert_eq!(
                    this.error_pattern,
                    None,
                    "{}:{l}, cannot specifiy error_pattern twice",
                    path.display()
                );
                this.error_pattern = Some((s.trim().to_string(), l));
            }
        }
        Ok(this)
    }

    fn parse_command(&mut self, command: &str, l: usize) -> Result<()> {
        // Commands are letters or dashes, grab everything until the first character that is neither of those.
        let (command, args) =
            match command.chars().position(|c: char| !c.is_alphabetic() && c != '-') {
                None => (command, ""),
                Some(i) => {
                    let (command, args) = command.split_at(i);
                    let mut args = args.chars();
                    // Commands are separated from their arguments by ':' or ' '
                    let next = args
                        .next()
                        .expect("the `position` above guarantees that there is at least one char");
                    let args = match next {
                        ':' | ' ' => args.as_str(),
                        _ => bail!("expected space or `:`, got `{next}`"),
                    };
                    (command, args.trim())
                }
            };

        match command {
            "revisions" => {
                ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
                self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
            }
            "compile-flags" => {
                self.compile_flags.extend(args.split_whitespace().map(|s| s.to_string()));
            }
            "rustc-env" =>
                for env in args.split_whitespace() {
                    let (k, v) = env.split_once('=').ok_or_else(|| {
                        eyre!("environment variables must be key/value pairs separated by a `=`")
                    })?;
                    self.env_vars.push((k.to_string(), v.to_string()));
                },
            "normalize-stderr-test" => {
                fn parse_str(s: &str) -> Result<(&str, &str)> {
                    let mut chars = s.char_indices();
                    match chars.next().ok_or_else(|| eyre!("missing arguments"))?.1 {
                        '"' => {
                            let s = chars.as_str();
                            let mut escaped = false;
                            for (i, c) in chars {
                                if escaped {
                                    escaped = false;
                                } else if c == '"' {
                                    return Ok((&s[..(i - 1)], s[i..].trim_start()));
                                } else {
                                    escaped = c == '\\';
                                }
                            }
                            bail!("no closing quotes found for {s}")
                        }
                        c => bail!("expected '\"', got {c}"),
                    }
                }

                let (from, rest) = parse_str(args)?;

                let to = rest.strip_prefix("->").ok_or_else(|| {
                    eyre!("normalize-stderr-test needs a pattern and replacement separated by `->`")
                })?.trim_start();
                let (to, rest) = parse_str(to)?;

                ensure!(rest.is_empty(), "trailing text after pattern replacement: {rest}");

                let from = Regex::new(from)?;
                self.normalize_stderr.push((from, to.to_string()));
            }
            "error-pattern" => {
                ensure!(
                    self.error_pattern.is_none(),
                    "cannot specifiy error_pattern twice, previous: {:?}",
                    self.error_pattern
                );
                self.error_pattern = Some((args.trim().to_string(), l));
            }
            "stderr-per-bitwidth" => {
                ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
                self.stderr_per_bitwidth = true;
            }
            command => {
                if let Some(s) = command.strip_prefix("ignore-") {
                    self.ignore.push(Condition::parse(s));
                    return Ok(());
                }

                if let Some(s) = command.strip_prefix("only-") {
                    self.only.push(Condition::parse(s));
                    return Ok(());
                }
                bail!("unknown command {command}");
            }
        }

        Ok(())
    }

    fn parse_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        self.parse_pattern_inner(pattern, fallthrough_to, None, l)
    }

    fn parse_revisioned_pattern(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        l: usize,
    ) -> Result<()> {
        let (revision, pattern) =
            pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
        if let Some(pattern) = pattern.strip_prefix('~') {
            self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
        } else {
            bail!("revisioned pattern must have `~` following the `]`");
        }
    }

    // parse something like (?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE)?:?(?P<text>.*)
    fn parse_pattern_inner(
        &mut self,
        pattern: &str,
        fallthrough_to: &mut Option<usize>,
        revision: Option<String>,
        l: usize,
    ) -> Result<()> {
        let (match_line, pattern) =
            match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
                '|' =>
                    (
                        *fallthrough_to
                            .as_mut()
                            .ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
                        &pattern[1..],
                    ),
                '^' => {
                    let offset = pattern.chars().take_while(|&c| c == '^').count();
                    (l - offset, &pattern[offset..])
                }
                _ => (l, pattern),
            };

        let (level, pattern) = match pattern.trim_start().split_once(|c| matches!(c, ':' | ' ')) {
            None => (None, pattern),
            Some((level, pattern_without_level)) =>
                match level.parse().ok() {
                    Some(level) => (Some(level), pattern_without_level),
                    None => (None, pattern),
                },
        };

        let matched = pattern.trim().to_string();

        ensure!(!matched.is_empty(), "no pattern specified");

        *fallthrough_to = Some(match_line);

        self.error_matches.push(ErrorMatch {
            matched,
            revision,
            level,
            definition_line: l,
            line: match_line,
        });

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate arguments;
extern crate configuration;
extern crate random;
extern crate sql;
extern crate sqlite;
extern crate term;
extern crate time;

#[macro_use]
extern crate log;

#[macro_use]
extern crate streamer;

use configuration::format::TOML;
use log::LogLevel;
use streamer::{platform, schedule, system, traffic, workload};

pub use streamer::{Config, Error, Result};

pub type System = system::System<platform::Thermal,
                                 schedule::Impartial,
                                 traffic::Fractal,
                                 workload::Random>;

pub type Increment = (system::Event, (platform::Profile, platform::Profile));

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).
    --length <time>          Time span to synthesize in seconds [default: 10].
    --output <path>          Output file for power and temperature profiles.

    --verbose                Display progress information.
    --help                   Display this message.
";

mod logger;
mod output;

use output::Output;

fn main() {
    start().unwrap_or_else(|error| fail(error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.get::<bool>("help").unwrap_or(false) {
        help();
    }
    if arguments.get::<bool>("verbose").unwrap_or(false) {
        logger::setup(LogLevel::Info);
    } else {
        logger::setup(LogLevel::Warn);
    }

    let config = ok!(TOML::open(some!(arguments.get::<String>("config"),
                                      "a configuration file is required")));

    let mut system = try!(construct_system(&config));
    let mut output = try!(output::new(&system, arguments.get::<String>("output")));

    let length = arguments.get::<f64>("length").unwrap_or(10.0);
    info!(target: "Streamer", "Synthesizing {} seconds...", length);
    let start = time::now();

    while let Some((event, (power, temperature))) = system.next() {
        let last = event.time > length;
        info!(target: "Streamer", "{} | {:2} queued", event,
              system.history().arrived - system.history().started);
        try!(output.next((event, (power, temperature))));
        if last {
            break;
        }
    }

    let elapsed = time::now() - start;
    info!(target: "Streamer", "Well done in {:.2} seconds.",
          elapsed.num_milliseconds() as f64 / 1000.0);

    Ok(())
}

fn construct_system(config: &Config) -> Result<System> {
    use streamer::platform::Platform;

    let source = {
        let seed = config.get::<i64>("seed").map(|&seed| seed as u64).unwrap_or(0);
        let seed = if seed > 0 { seed } else { time::now().to_timespec().sec as u64 };
        let seed = [0x12345678 & seed, 0x87654321 & seed];
        random::default().seed(seed)
    };

    macro_rules! branch(
        ($name:expr) => (config.branch($name).unwrap_or_else(|| Config::new()));
    );

    let traffic = try!(traffic::Fractal::new(&branch!("traffic"), &source));
    let workload = try!(workload::Random::new(&branch!("workload"), &source));
    let platform = try!(platform::Thermal::new(&branch!("platform")));
    let schedule = try!(schedule::Impartial::new(&branch!("schedule"), platform.elements()));

    System::new(traffic, workload, platform, schedule)
}

fn help() -> ! {
    println!("{}", USAGE.trim());
    std::process::exit(0);
}

#[allow(unused_must_use)]
fn fail(error: Error) -> ! {
    use std::io::{stderr, Write};
    if let Some(mut output) = term::stderr() {
        output.fg(term::color::RED);
        output.write_all(format!("Error: {}.\n", error).as_bytes());
    }
    std::process::exit(1);
}
<commit_msg>Fix bin/streamer<commit_after>extern crate arguments;
extern crate configuration;
extern crate random;
extern crate sql;
extern crate sqlite;
extern crate term;
extern crate time;

#[macro_use]
extern crate log;

#[macro_use]
extern crate streamer;

use configuration::format::TOML;
use log::LogLevel;
use streamer::{platform, schedule, system, traffic, workload};

pub use streamer::{Config, Error, Result};

pub type System = system::System<traffic::Fractal,
                                 workload::Random,
                                 platform::Thermal,
                                 schedule::Impartial>;

pub type Increment = (system::Event, (platform::Profile, platform::Profile));

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).
    --length <time>          Time span to synthesize in seconds [default: 10].
    --output <path>          Output file for power and temperature profiles.

    --verbose                Display progress information.
    --help                   Display this message.
";

mod logger;
mod output;

use output::Output;

fn main() {
    start().unwrap_or_else(|error| fail(error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));
    if arguments.get::<bool>("help").unwrap_or(false) {
        help();
    }
    if arguments.get::<bool>("verbose").unwrap_or(false) {
        logger::setup(LogLevel::Info);
    } else {
        logger::setup(LogLevel::Warn);
    }

    let config = ok!(TOML::open(some!(arguments.get::<String>("config"),
                                      "a configuration file is required")));

    let mut system = try!(construct_system(&config));
    let mut output = try!(output::new(&system, arguments.get::<String>("output")));

    let length = arguments.get::<f64>("length").unwrap_or(10.0);
    info!(target: "Streamer", "Synthesizing {} seconds...", length);
    let start = time::now();

    while let Some((event, (power, temperature))) = system.next() {
        let last = event.time > length;
        info!(target: "Streamer", "{} | {:2} queued", event,
              system.history().arrived - system.history().started);
        try!(output.next((event, (power, temperature))));
        if last {
            break;
        }
    }

    let elapsed = time::now() - start;
    info!(target: "Streamer", "Well done in {:.2} seconds.",
          elapsed.num_milliseconds() as f64 / 1000.0);

    Ok(())
}

fn construct_system(config: &Config) -> Result<System> {
    use streamer::platform::Platform;

    let source = {
        let seed = config.get::<i64>("seed").map(|&seed| seed as u64).unwrap_or(0);
        let seed = if seed > 0 { seed } else { time::now().to_timespec().sec as u64 };
        let seed = [0x12345678 & seed, 0x87654321 & seed];
        random::default().seed(seed)
    };

    macro_rules! branch(
        ($name:expr) => (config.branch($name).unwrap_or_else(|| Config::new()));
    );

    let traffic = try!(traffic::Fractal::new(&branch!("traffic"), &source));
    let workload = try!(workload::Random::new(&branch!("workload"), &source));
    let platform = try!(platform::Thermal::new(&branch!("platform")));
    let schedule = try!(schedule::Impartial::new(&branch!("schedule"), platform.elements()));

    System::new(traffic, workload, platform, schedule)
}

fn help() -> ! {
    println!("{}", USAGE.trim());
    std::process::exit(0);
}

#[allow(unused_must_use)]
fn fail(error: Error) -> ! {
    use std::io::{stderr, Write};
    if let Some(mut output) = term::stderr() {
        output.fg(term::color::RED);
        output.write_all(format!("Error: {}.\n", error).as_bytes());
    }
    std::process::exit(1);
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use std::fmt::Debug;
use quickcheck::quickcheck;

struct Unspecialized<I>(I);
impl<I> Iterator for Unspecialized<I>
where
    I: Iterator,
{
    type Item = I::Item;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}

fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F)
where
    V: Eq + Debug,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
    F: Fn(Box<dyn Iterator<Item = IterItem> + 'a>) -> V,
{
    assert_eq!(
        mapper(Box::new(Unspecialized(iterator.clone()))),
        mapper(Box::new(iterator.clone()))
    )
}

fn check_specialized_count_last_nth_sizeh<IterItem, Iter>(
    it: &Iter,
    known_expected_size: Option<usize>,
) where
    IterItem: Eq + Debug,
    Iter: Iterator<Item = IterItem> + Clone,
{
    let size = it.clone().count();
    if let Some(expected_size) = known_expected_size {
        assert_eq!(size, expected_size);
    }
    check_specialized(it, |i| i.count());
    check_specialized(it, |i| i.last());
    for n in 0..size + 2 {
        check_specialized(it, |mut i| i.nth(n));
    }
    let mut it_sh = it.clone();
    for n in 0..size + 2 {
        let len = it_sh.clone().count();
        let (min, max) = it_sh.size_hint();
        assert_eq!((size - n.min(size)), len);
        assert!(min <= len);
        if let Some(max) = max {
            assert!(len <= max);
        }
        it_sh.next();
    }
}

fn check_specialized_fold<IterItem, Iter>(it: &Iter)
where
    IterItem: Eq + Debug + Clone,
    Iter: Iterator<Item = IterItem> + Clone,
{
    check_specialized(it, |i| {
        let mut parameters_from_fold = vec![];
        let fold_result = i.fold(vec![], |mut acc, v: IterItem| {
            parameters_from_fold.push((acc.clone(), v.clone()));
            acc.push(v);
            acc
        });
        (parameters_from_fold, fold_result)
    });
}

fn put_back_test(test_vec: Vec<i32>, known_expected_size: Option<usize>) {
    {
        // Lexical lifetimes support
        let pb = itertools::put_back(test_vec.iter());
        check_specialized_count_last_nth_sizeh(&pb, known_expected_size);
        check_specialized_fold(&pb);
    }

    let mut pb = itertools::put_back(test_vec.into_iter());
    pb.put_back(1);
    check_specialized_count_last_nth_sizeh(&pb, known_expected_size.map(|x| x + 1));
    check_specialized_fold(&pb)
}

#[test]
fn put_back() {
    put_back_test(vec![7, 4, 1], Some(3));
}

quickcheck! {
    fn put_back_qc(test_vec: Vec<i32>) -> () {
        put_back_test(test_vec, None)
    }
}

fn merge_join_by_test(i1: Vec<usize>, i2: Vec<usize>, known_expected_size: Option<usize>) {
    let i1 = i1.into_iter();
    let i2 = i2.into_iter();
    let mjb = i1.clone().merge_join_by(i2.clone(), std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, known_expected_size);
    check_specialized_fold(&mjb);

    // And the other way around
    let mjb = i2.merge_join_by(i1, std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, known_expected_size);
    check_specialized_fold(&mjb);
}

#[test]
fn merge_join_by() {
    let i1 = vec![1, 3, 5, 7, 8, 9];
    let i2 = vec![0, 3, 4, 5];
    merge_join_by_test(i1, i2, Some(8));
}

quickcheck! {
    fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () {
        merge_join_by_test(i1, i2, None)
    }
}
<commit_msg>Test specializations in one function (1)<commit_after>use itertools::Itertools;
use std::fmt::Debug;
use quickcheck::quickcheck;

struct Unspecialized<I>(I);
impl<I> Iterator for Unspecialized<I>
where
    I: Iterator,
{
    type Item = I::Item;

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}

fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F)
where
    V: Eq + Debug,
    Iter: Iterator<Item = IterItem> + Clone + 'a,
    F: Fn(Box<dyn Iterator<Item = IterItem> + 'a>) -> V,
{
    assert_eq!(
        mapper(Box::new(Unspecialized(iterator.clone()))),
        mapper(Box::new(iterator.clone()))
    )
}

fn check_specialized_count_last_nth_sizeh<IterItem, Iter>(
    it: &Iter,
    known_expected_size: Option<usize>,
) where
    IterItem: Eq + Debug + Clone,
    Iter: Iterator<Item = IterItem> + Clone,
{
    let size = it.clone().count();
    if let Some(expected_size) = known_expected_size {
        assert_eq!(size, expected_size);
    }
    check_specialized(it, |i| i.count());
    check_specialized(it, |i| i.last());
    for n in 0..size + 2 {
        check_specialized(it, |mut i| i.nth(n));
    }
    let mut it_sh = it.clone();
    for n in 0..size + 2 {
        let len = it_sh.clone().count();
        let (min, max) = it_sh.size_hint();
        assert_eq!((size - n.min(size)), len);
        assert!(min <= len);
        if let Some(max) = max {
            assert!(len <= max);
        }
        it_sh.next();
    }
    check_specialized(it, |i| {
        let mut parameters_from_fold = vec![];
        let fold_result = i.fold(vec![], |mut acc, v: IterItem| {
            parameters_from_fold.push((acc.clone(), v.clone()));
            acc.push(v);
            acc
        });
        (parameters_from_fold, fold_result)
    });
}

fn put_back_test(test_vec: Vec<i32>, known_expected_size: Option<usize>) {
    {
        // Lexical lifetimes support
        let pb = itertools::put_back(test_vec.iter());
        check_specialized_count_last_nth_sizeh(&pb, known_expected_size);
    }

    let mut pb = itertools::put_back(test_vec.into_iter());
    pb.put_back(1);
    check_specialized_count_last_nth_sizeh(&pb, known_expected_size.map(|x| x + 1));
}

#[test]
fn put_back() {
    put_back_test(vec![7, 4, 1], Some(3));
}

quickcheck! {
    fn put_back_qc(test_vec: Vec<i32>) -> () {
        put_back_test(test_vec, None)
    }
}

fn merge_join_by_test(i1: Vec<usize>, i2: Vec<usize>, known_expected_size: Option<usize>) {
    let i1 = i1.into_iter();
    let i2 = i2.into_iter();
    let mjb = i1.clone().merge_join_by(i2.clone(), std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, known_expected_size);

    // And the other way around
    let mjb = i2.merge_join_by(i1, std::cmp::Ord::cmp);
    check_specialized_count_last_nth_sizeh(&mjb, known_expected_size);
}

#[test]
fn merge_join_by() {
    let i1 = vec![1, 3, 5, 7, 8, 9];
    let i2 = vec![0, 3, 4, 5];
    merge_join_by_test(i1, i2, Some(8));
}

quickcheck! {
    fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () {
        merge_join_by_test(i1, i2, None)
    }
}
<|endoftext|>


<commit_before>use std::path;
use std::str;

use support::{project, execs, basic_bin_manifest, basic_lib_manifest};
use support::{COMPILING, cargo_dir, ResultTest};
use hamcrest::{assert_that, existing_file};
use cargo::util::process;

fn setup() {}

test!(cargo_test_simple {
    let p = project("foo")
        .file("Cargo.toml", basic_bin_manifest("foo").as_slice())
        .file("src/foo.rs", r#"
            fn hello() -> &'static str {
                "hello"
            }

            pub fn main() {
                println!("{}", hello())
            }

            #[test]
            fn test_hello() {
                assert_eq!(hello(), "hello")
            }"#);

    assert_that(p.cargo_process("cargo-build"), execs());
    assert_that(&p.bin("foo"), existing_file());

    assert_that(
        process(p.bin("foo")),
        execs().with_stdout("hello\n"));

    assert_that(p.process(cargo_dir().join("cargo-test")),
        execs().with_stdout(format!("{} foo v0.5.0 (file:{})\n\n\
                                    running 1 test\n\
                                    test test_hello ... ok\n\n\
                                    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n",
                                    COMPILING, p.root().display())));

    assert_that(&p.bin("test/foo"), existing_file());
})

test!(cargo_test_failing_test {
    let p = project("foo")
        .file("Cargo.toml", basic_bin_manifest("foo").as_slice())
        .file("src/foo.rs", r#"
            fn hello() -> &'static str {
                "hello"
            }

            pub fn main() {
                println!("{}", hello())
            }

            #[test]
            fn test_hello() {
                assert_eq!(hello(), "nope")
            }"#);

    assert_that(p.cargo_process("cargo-build"), execs());
    assert_that(&p.bin("foo"), existing_file());

    assert_that(
        process(p.bin("foo")),
        execs().with_stdout("hello\n"));

    assert_that(p.process(cargo_dir().join("cargo-test")),
        execs().with_stdout(format!("{} foo v0.5.0 (file:{})\n\n\
                                    running 1 test\n\
                                    test test_hello ... FAILED\n\n\
                                    failures:\n\n\
                                    ---- test_hello stdout ----\n<tab>\
                                    task 'test_hello' failed at 'assertion failed: \
                                    `(left == right) && (right == left)` (left: \
                                    `hello`, right: `nope`)', src{sep}foo.rs:12\n<tab>\n<tab>\n\n\
                                    failures:\n    test_hello\n\n\
                                    test result: FAILED. 0 passed; 1 failed; \
                                    0 ignored; 0 measured\n\n",
                                    COMPILING, p.root().display(),
                                    sep = path::SEP)));

    assert_that(&p.bin("test/foo"), existing_file());
})

test!(test_with_lib_dep {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[bin]]
            name = "baz"
            path = "src/main.rs"
        "#)
        .file("src/lib.rs", "
            pub fn foo(){}
            #[test] fn lib_test() {}
        ")
        .file("src/main.rs", "
            extern crate foo;

            fn main() {}

            #[test]
            fn bin_test() {}
        ");

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let bin = "\
running 1 test
test bin_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let lib = "\
running 1 test
test lib_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, bin, lib).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, lib, bin).as_slice());
})

test!(test_with_deep_lib_dep {
    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [dependencies.foo]
            path = "../foo"
        "#)
        .file("src/lib.rs", "
            extern crate foo;
            #[test]
            fn bar_test() {
                foo::foo();
            }
        ");
    let p2 = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "
            pub fn foo() {}

            #[test]
            fn foo_test() {}
        ");

    p2.build();
    assert_that(p.cargo_process("cargo-test"),
                execs().with_status(0)
                       .with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})
{compiling} bar v0.0.1 (file:{dir})

running 1 test
test bar_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                       ",
                       compiling = COMPILING,
                       dir = p.root().display()).as_slice()));
})

test!(external_test_explicit {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[test]]
            name = "test"
            path = "src/test.rs"
        "#)
        .file("src/lib.rs", r#"
            pub fn get_hello() -> &'static str { "Hello" }

            #[test]
            fn internal_test() {}
        "#)
        .file("src/test.rs", r#"
            extern crate foo;

            #[test]
            fn external_test() { assert_eq!(foo::get_hello(), "Hello") }
        "#);

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let internal = "\
running 1 test
test internal_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let external = "\
running 1 test
test external_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice());
})

test!(external_test_implicit {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", r#"
            pub fn get_hello() -> &'static str { "Hello" }

            #[test]
            fn internal_test() {}
        "#)
        .file("tests/external.rs", r#"
            extern crate foo;

            #[test]
            fn external_test() { assert_eq!(foo::get_hello(), "Hello") }
        "#);

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let internal = "\
running 1 test
test internal_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let external = "\
running 1 test
test external_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice());
})

test!(dont_run_examples {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", r#"
        "#)
        .file("examples/dont-run-me-i-will-fail.rs", r#"
            fn main() { fail!("Examples should not be run by 'cargo test'"); }
        "#);
    assert_that(p.cargo_process("cargo-test"),
                execs().with_status(0));
})

test!(pass_through_command_line {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "
            #[test] fn foo() {}
            #[test] fn bar() {}
        ");

    assert_that(p.cargo_process("cargo-test").arg("bar"),
                execs().with_status(0)
                       .with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})

running 1 test
test bar ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                       ",
                       compiling = COMPILING,
                       dir = p.root().display()).as_slice()));

    assert_that(p.cargo_process("cargo-test").arg("foo"),
                execs().with_status(0)
                       .with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})

running 1 test
test foo ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                       ",
                       compiling = COMPILING,
                       dir = p.root().display()).as_slice()));
})

// Regression test for running cargo-test twice with
// tests in an rlib
test!(cargo_test_twice {
    let p = project("test_twice")
        .file("Cargo.toml", basic_lib_manifest("test_twice").as_slice())
        .file("src/test_twice.rs", r#"
            #![crate_type = "rlib"]

            #[test]
            fn dummy_test() { }
            "#);

    p.cargo_process("cargo-build");

    for _ in range(0u, 2) {
        assert_that(p.process(cargo_dir().join("cargo-test")),
                    execs().with_status(0));
    }
})
<commit_msg>auto merge of #242 : alexcrichton/cargo/issue-111, r=wycats<commit_after>use std::path;
use std::str;

use support::{project, execs, basic_bin_manifest, basic_lib_manifest};
use support::{COMPILING, cargo_dir, ResultTest};
use hamcrest::{assert_that, existing_file};
use cargo::util::process;

fn setup() {}

test!(cargo_test_simple {
    let p = project("foo")
        .file("Cargo.toml", basic_bin_manifest("foo").as_slice())
        .file("src/foo.rs", r#"
            fn hello() -> &'static str {
                "hello"
            }

            pub fn main() {
                println!("{}", hello())
            }

            #[test]
            fn test_hello() {
                assert_eq!(hello(), "hello")
            }"#);

    assert_that(p.cargo_process("cargo-build"), execs());
    assert_that(&p.bin("foo"), existing_file());

    assert_that(
        process(p.bin("foo")),
        execs().with_stdout("hello\n"));

    assert_that(p.process(cargo_dir().join("cargo-test")),
        execs().with_stdout(format!("{} foo v0.5.0 (file:{})\n\n\
                                    running 1 test\n\
                                    test test_hello ... ok\n\n\
                                    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n",
                                    COMPILING, p.root().display())));

    assert_that(&p.bin("test/foo"), existing_file());
})

test!(cargo_test_failing_test {
    let p = project("foo")
        .file("Cargo.toml", basic_bin_manifest("foo").as_slice())
        .file("src/foo.rs", r#"
            fn hello() -> &'static str {
                "hello"
            }

            pub fn main() {
                println!("{}", hello())
            }

            #[test]
            fn test_hello() {
                assert_eq!(hello(), "nope")
            }"#);

    assert_that(p.cargo_process("cargo-build"), execs());
    assert_that(&p.bin("foo"), existing_file());

    assert_that(
        process(p.bin("foo")),
        execs().with_stdout("hello\n"));

    assert_that(p.process(cargo_dir().join("cargo-test")),
        execs().with_stdout(format!("{} foo v0.5.0 (file:{})\n\n\
                                    running 1 test\n\
                                    test test_hello ... FAILED\n\n\
                                    failures:\n\n\
                                    ---- test_hello stdout ----\n<tab>\
                                    task 'test_hello' failed at 'assertion failed: \
                                    `(left == right) && (right == left)` (left: \
                                    `hello`, right: `nope`)', src{sep}foo.rs:12\n<tab>\n<tab>\n\n\
                                    failures:\n    test_hello\n\n\
                                    test result: FAILED. 0 passed; 1 failed; \
                                    0 ignored; 0 measured\n\n",
                                    COMPILING, p.root().display(),
                                    sep = path::SEP)));

    assert_that(&p.bin("test/foo"), existing_file());
})

test!(test_with_lib_dep {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[bin]]
            name = "baz"
            path = "src/main.rs"
        "#)
        .file("src/lib.rs", "
            pub fn foo(){}
            #[test] fn lib_test() {}
        ")
        .file("src/main.rs", "
            extern crate foo;

            fn main() {}

            #[test]
            fn bin_test() {}
        ");

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let bin = "\
running 1 test
test bin_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let lib = "\
running 1 test
test lib_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, bin, lib).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, lib, bin).as_slice());
})

test!(test_with_deep_lib_dep {
    let p = project("bar")
        .file("Cargo.toml", r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [dependencies.foo]
            path = "../foo"
        "#)
        .file("src/lib.rs", "
            extern crate foo;
            #[test]
            fn bar_test() {
                foo::foo();
            }
        ");
    let p2 = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "
            pub fn foo() {}

            #[test]
            fn foo_test() {}
        ");

    p2.build();
    assert_that(p.cargo_process("cargo-test"),
                execs().with_status(0)
                       .with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})
{compiling} bar v0.0.1 (file:{dir})

running 1 test
test bar_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                       ",
                       compiling = COMPILING,
                       dir = p.root().display()).as_slice()));
})

test!(external_test_explicit {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[test]]
            name = "test"
            path = "src/test.rs"
        "#)
        .file("src/lib.rs", r#"
            pub fn get_hello() -> &'static str { "Hello" }

            #[test]
            fn internal_test() {}
        "#)
        .file("src/test.rs", r#"
            extern crate foo;

            #[test]
            fn external_test() { assert_eq!(foo::get_hello(), "Hello") }
        "#);

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let internal = "\
running 1 test
test internal_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let external = "\
running 1 test
test external_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice());
})

test!(external_test_implicit {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", r#"
            pub fn get_hello() -> &'static str { "Hello" }

            #[test]
            fn internal_test() {}
        "#)
        .file("tests/external.rs", r#"
            extern crate foo;

            #[test]
            fn external_test() { assert_eq!(foo::get_hello(), "Hello") }
        "#);

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let internal = "\
running 1 test
test internal_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let external = "\
running 1 test
test external_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, internal, external).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, external, internal).as_slice());
})

test!(dont_run_examples {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", r#"
        "#)
        .file("examples/dont-run-me-i-will-fail.rs", r#"
            fn main() { fail!("Examples should not be run by 'cargo test'"); }
        "#);
    assert_that(p.cargo_process("cargo-test"),
                execs().with_status(0));
})

test!(pass_through_command_line {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
        "#)
        .file("src/lib.rs", "
            #[test] fn foo() {}
            #[test] fn bar() {}
        ");

    assert_that(p.cargo_process("cargo-test").arg("bar"),
                execs().with_status(0)
                       .with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})

running 1 test
test bar ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                       ",
                       compiling = COMPILING,
                       dir = p.root().display()).as_slice()));

    assert_that(p.cargo_process("cargo-test").arg("foo"),
                execs().with_status(0)
                       .with_stdout(format!("\
{compiling} foo v0.0.1 (file:{dir})

running 1 test
test foo ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured\n\n\
                       ",
                       compiling = COMPILING,
                       dir = p.root().display()).as_slice()));
})

// Regression test for running cargo-test twice with
// tests in an rlib
test!(cargo_test_twice {
    let p = project("test_twice")
        .file("Cargo.toml", basic_lib_manifest("test_twice").as_slice())
        .file("src/test_twice.rs", r#"
            #![crate_type = "rlib"]

            #[test]
            fn dummy_test() { }
            "#);

    p.cargo_process("cargo-build");

    for _ in range(0u, 2) {
        assert_that(p.process(cargo_dir().join("cargo-test")),
                    execs().with_status(0));
    }
})

test!(lib_bin_same_name {
    let p = project("foo")
        .file("Cargo.toml", r#"
            [project]
            name = "foo"
            version = "0.0.1"
            authors = []

            [[lib]]
            name = "foo"
            [[bin]]
            name = "foo"
        "#)
        .file("src/lib.rs", "
            #[test] fn lib_test() {}
        ")
        .file("src/main.rs", "
            extern crate foo;

            #[test]
            fn bin_test() {}
        ");

    let output = p.cargo_process("cargo-test")
                  .exec_with_output().assert();
    let out = str::from_utf8(output.output.as_slice()).assert();

    let bin = "\
running 1 test
test bin_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";
    let lib = "\
running 1 test
test lib_test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured";

    let head = format!("{compiling} foo v0.0.1 (file:{dir})",
                       compiling = COMPILING, dir = p.root().display());

    assert!(out == format!("{}\n\n{}\n\n\n{}\n\n", head, bin, lib).as_slice() ||
            out == format!("{}\n\n{}\n\n\n{}\n\n", head, lib, bin).as_slice());
})
<|endoftext|>