<commit_before>use ::cssparser::{Parser, ParserInput, Token};
use ::glib::translate::*;
use ::libc;

use std::f64::consts::*;

use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use parsers::Parse;
use parsers::ParseError;
use error::*;

/* Keep this in sync with ../../rsvg-private.h:LengthUnit */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthUnit {
    Default,
    Percent,
    FontEm,
    FontEx,
    Inch,
    RelativeLarger,
    RelativeSmaller
}

/* Keep this in sync with ../../rsvg-private.h:LengthDir */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthDir {
    Horizontal,
    Vertical,
    Both
}

/* This is *not* an opaque struct; it is actually visible to the C code.  It is so
 * that the remaining C code can create RsvgLength values as part of existing
 * structures or objects, without allocations on the heap.
 */
/* Keep this in sync with ../../rsvg-private.h:RsvgLength */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct RsvgLength {
    pub length: f64,
    pub unit: LengthUnit,
    dir: LengthDir
}

impl Default for RsvgLength {
    fn default () -> RsvgLength {
        RsvgLength {
            length: 0.0,
            unit:   LengthUnit::Default,
            dir:    LengthDir::Both
        }
    }
}

const POINTS_PER_INCH: f64 = 72.0;
const CM_PER_INCH:     f64 = 2.54;
const MM_PER_INCH:     f64 = 25.4;
const PICA_PER_INCH:   f64 = 6.0;

fn compute_named_size (name: &str) -> f64 {
    let power: f64;

    match name {
        "xx-small" => { power = -3.0; },
        "x-small"  => { power = -2.0; },
        "small"    => { power = -1.0; },
        "medium"   => { power = 0.0; },
        "large"    => { power = 1.0; },
        "x-large"  => { power = 2.0; },
        "xx-large" => { power = 3.0; },
        _          => { unreachable! (); }
    }

    12.0 * 1.2f64.powf (power) / POINTS_PER_INCH
}

#[no_mangle]
pub extern fn rsvg_length_parse (string: *const libc::c_char, dir: LengthDir) -> RsvgLength {
    let my_string = unsafe { &String::from_glib_none (string) };

    // FIXME: this ignores errors; propagate them upstream
    RsvgLength::parse (my_string, dir).unwrap_or (RsvgLength::default ())
}

/* https://www.w3.org/TR/SVG/types.html#DataTypeLength
 * https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units
 *
 * Lengths have units.  When they need to be need resolved to
 * units in the user's coordinate system, some unit types
 * need to know if they are horizontal/vertical/both.  For example,
 * a some_object.width="50%" is 50% with respect to the current
 * viewport's width.  In this case, the @dir argument is used
 * inside RsvgLength::normalize(), when it needs to know to what the
 * length refers.
 */

fn make_err () -> AttributeError {
    AttributeError::Parse (ParseError::new ("expected length: number(\"em\" | \"ex\" | \"px\" | \"in\" | \"cm\" | \"mm\" | \"pt\" | \"pc\" | \"%\")?"))
}

impl Parse for RsvgLength {
    type Data = LengthDir;
    type Err = AttributeError;

    fn parse (string: &str, dir: LengthDir) -> Result <RsvgLength, AttributeError> {
        let mut input = ParserInput::new (string);
        let mut parser = Parser::new (&mut input);

        let length = {
            let token = parser.next ()
                .map_err (|_| AttributeError::Parse (ParseError::new ("expected number and optional symbol, or number and percentage")))?;

            match *token {
                Token::Number { value, .. } => RsvgLength { length: value as f64,
                                                            unit:   LengthUnit::Default,
                                                            dir:    dir },

                Token::Percentage { unit_value, .. } => RsvgLength { length: unit_value as f64,
                                                                     unit:   LengthUnit::Percent,
                                                                     dir:    dir },

                Token::Dimension { value, ref unit, .. } => {
                    let value = value as f64;

                    match unit.as_ref () {
                        "em" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEm,
                                             dir:    dir },

                        "ex" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEx,
                                             dir:    dir },

                        "pt" => RsvgLength { length: value / POINTS_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "in" => RsvgLength { length: value,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "cm" => RsvgLength { length: value / CM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "mm" => RsvgLength { length: value / MM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "pc" => RsvgLength { length: value / PICA_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "px" => RsvgLength { length: value,
                                             unit:   LengthUnit::Default,
                                             dir:    dir },

                        _ => return Err (make_err ())
                    }
                },

                // FIXME: why are the following in Length?  They should be in FontSize
                Token::Ident (ref cow) => match cow.as_ref () {
                    "larger" => RsvgLength { length: 0.0,
                                             unit:   LengthUnit::RelativeLarger,
                                             dir:    dir },

                    "smaller" => RsvgLength { length: 0.0,
                                              unit:  LengthUnit::RelativeSmaller,
                                              dir:   dir },

                    "xx-small" |
                    "x-small" |
                    "small" |
                    "medium" |
                    "large" |
                    "x-large" |
                    "xx-large" => RsvgLength { length: compute_named_size (&*string),
                                               unit:   LengthUnit::Inch,
                                               dir:    dir },

                    _ => return Err (make_err ())
                },

                _ => return Err (make_err ())
            }
        };

        parser.expect_exhausted ().map_err (|_| make_err ())?;

        Ok (length)
    }
}

impl RsvgLength {
    pub fn check_nonnegative (self) -> Result <RsvgLength, AttributeError> {
        if self.length >= 0.0 {
            Ok (self)
        } else {
            Err (AttributeError::Value ("value must be non-negative".to_string ()))
        }
    }

    pub fn normalize (&self, draw_ctx: *const RsvgDrawingCtx) -> f64 {
        match self.unit {
            LengthUnit::Default => {
                self.length
            },

            LengthUnit::Percent => {
                let (width, height) = drawing_ctx::get_view_box_size (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * width },
                    LengthDir::Vertical   => { self.length * height },
                    LengthDir::Both       => { self.length * viewport_percentage (width, height) }
                }
            },

            LengthUnit::FontEm => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx)
            },

            LengthUnit::FontEx => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx) / 2.0
            },

            LengthUnit::Inch => {
                let (dpi_x, dpi_y) = drawing_ctx::get_dpi (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * dpi_x },
                    LengthDir::Vertical   => { self.length * dpi_y },
                    LengthDir::Both       => { self.length * viewport_percentage (dpi_x, dpi_y) }
                }
            },

            // FIXME: these are pending: https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
            LengthUnit::RelativeLarger |
            LengthUnit::RelativeSmaller => { 0.0 }
        }
    }

    pub fn hand_normalize (&self,
                           pixels_per_inch: f64,
                           width_or_height: f64,
                           font_size: f64) -> f64 {
        match self.unit {
            LengthUnit::Default => { self.length },

            LengthUnit::Percent => { self.length * width_or_height },

            LengthUnit::FontEm => { self.length * font_size },

            LengthUnit::FontEx => { self.length * font_size / 2.0 },

            LengthUnit::Inch => { self.length * pixels_per_inch },

            _ => { 0.0 }
        }
    }
}

fn viewport_percentage (x: f64, y: f64) -> f64 {
    /* https://www.w3.org/TR/SVG/coords.html#Units
     *
     * "For any other length value expressed as a percentage of the viewport, the
     * percentage is calculated as the specified percentage of
     * sqrt((actual-width)**2 + (actual-height)**2))/sqrt(2)."
     */
    return (x * x + y * y).sqrt () / SQRT_2;
}

#[no_mangle]
pub extern fn rsvg_length_normalize (raw_length: *const RsvgLength, draw_ctx: *const RsvgDrawingCtx) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.normalize (draw_ctx)
}

#[no_mangle]
pub extern fn rsvg_length_hand_normalize (raw_length: *const RsvgLength,
                                          pixels_per_inch: f64,
                                          width_or_height: f64,
                                          font_size: f64) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.hand_normalize (pixels_per_inch, width_or_height, font_size)
}

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

    #[test]
    fn parses_default () {
        assert_eq! (RsvgLength::parse ("42", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: 42.0,
                        unit:   LengthUnit::Default,
                        dir:    LengthDir::Horizontal}));

        assert_eq! (RsvgLength::parse ("-42px", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: -42.0,
                        unit:   LengthUnit::Default,
                        dir:    LengthDir::Horizontal}));
    }

    #[test]
    fn parses_percent () {
        assert_eq! (RsvgLength::parse ("50.0%", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: 0.5,
                        unit:   LengthUnit::Percent,
                        dir:    LengthDir::Horizontal}));
    }

    #[test]
    fn parses_font_em () {
        assert_eq! (RsvgLength::parse ("22.5em", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 22.5,
                        unit:   LengthUnit::FontEm,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_font_ex () {
        assert_eq! (RsvgLength::parse ("22.5ex", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 22.5,
                        unit:   LengthUnit::FontEx,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_physical_units () {
        assert_eq! (RsvgLength::parse ("72pt", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 1.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("-22.5in", LengthDir::Both),
                    Ok (RsvgLength {
                        length: -22.5,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("-254cm", LengthDir::Both),
                    Ok (RsvgLength {
                        length: -100.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("254mm", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 10.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("60pc", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 10.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));
    }

    #[test]
    fn parses_relative_larger () {
        assert_eq! (RsvgLength::parse ("larger", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 0.0,
                        unit:   LengthUnit::RelativeLarger,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_relative_smaller () {
        assert_eq! (RsvgLength::parse ("smaller", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 0.0,
                        unit:   LengthUnit::RelativeSmaller,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_named_sizes () {
        let names = vec![ "xx-small",
                           "x-small",
                           "small",
                           "medium",
                           "large",
                           "x-large",
                           "xx-large" ];

        let mut previous_value: Option<f64> = None;

        // Just ensure that the values are progressively larger; don't
        // enforce a particular sequence.

        for name in names {
            let length = RsvgLength::parse (name, LengthDir::Both).unwrap ();

            assert_eq! (length.unit, LengthUnit::Inch);
            assert_eq! (length.dir, LengthDir::Both);

            if let Some (v) = previous_value {
                assert! (length.length > v);
            } else {
                previous_value = Some (length.length);
            }

        }
    }

    #[test]
    fn empty_length_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("", LengthDir::Both)));
    }

    #[test]
    fn invalid_unit_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("8furlong", LengthDir::Both)));
    }

    #[test]
    fn invalid_font_size_yields_error () {
        // FIXME: this is intended to test the (absence of) the "larger" et al values.
        // Since they really be in FontSize, not RsvgLength, we should remember
        // to move this test to that type later.
        assert! (is_parse_error (&RsvgLength::parse ("furlong", LengthDir::Both)));
    }

    #[test]
    fn check_nonnegative_works () {
        assert! (RsvgLength::parse ("0", LengthDir::Both).and_then (|l| l.check_nonnegative ()).is_ok ());
        assert! (RsvgLength::parse ("-10", LengthDir::Both).and_then (|l| l.check_nonnegative ()).is_err ());
    }
}
<commit_msg>RsvgLength::new() - new method.<commit_after>use ::cssparser::{Parser, ParserInput, Token};
use ::glib::translate::*;
use ::libc;

use std::f64::consts::*;

use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use parsers::Parse;
use parsers::ParseError;
use error::*;

/* Keep this in sync with ../../rsvg-private.h:LengthUnit */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthUnit {
    Default,
    Percent,
    FontEm,
    FontEx,
    Inch,
    RelativeLarger,
    RelativeSmaller
}

/* Keep this in sync with ../../rsvg-private.h:LengthDir */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthDir {
    Horizontal,
    Vertical,
    Both
}

/* This is *not* an opaque struct; it is actually visible to the C code.  It is so
 * that the remaining C code can create RsvgLength values as part of existing
 * structures or objects, without allocations on the heap.
 */
/* Keep this in sync with ../../rsvg-private.h:RsvgLength */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct RsvgLength {
    pub length: f64,
    pub unit: LengthUnit,
    dir: LengthDir
}

impl Default for RsvgLength {
    fn default () -> RsvgLength {
        RsvgLength {
            length: 0.0,
            unit:   LengthUnit::Default,
            dir:    LengthDir::Both
        }
    }
}

const POINTS_PER_INCH: f64 = 72.0;
const CM_PER_INCH:     f64 = 2.54;
const MM_PER_INCH:     f64 = 25.4;
const PICA_PER_INCH:   f64 = 6.0;

fn compute_named_size (name: &str) -> f64 {
    let power: f64;

    match name {
        "xx-small" => { power = -3.0; },
        "x-small"  => { power = -2.0; },
        "small"    => { power = -1.0; },
        "medium"   => { power = 0.0; },
        "large"    => { power = 1.0; },
        "x-large"  => { power = 2.0; },
        "xx-large" => { power = 3.0; },
        _          => { unreachable! (); }
    }

    12.0 * 1.2f64.powf (power) / POINTS_PER_INCH
}

#[no_mangle]
pub extern fn rsvg_length_parse (string: *const libc::c_char, dir: LengthDir) -> RsvgLength {
    let my_string = unsafe { &String::from_glib_none (string) };

    // FIXME: this ignores errors; propagate them upstream
    RsvgLength::parse (my_string, dir).unwrap_or (RsvgLength::default ())
}

/* https://www.w3.org/TR/SVG/types.html#DataTypeLength
 * https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units
 *
 * Lengths have units.  When they need to be need resolved to
 * units in the user's coordinate system, some unit types
 * need to know if they are horizontal/vertical/both.  For example,
 * a some_object.width="50%" is 50% with respect to the current
 * viewport's width.  In this case, the @dir argument is used
 * inside RsvgLength::normalize(), when it needs to know to what the
 * length refers.
 */

fn make_err () -> AttributeError {
    AttributeError::Parse (ParseError::new ("expected length: number(\"em\" | \"ex\" | \"px\" | \"in\" | \"cm\" | \"mm\" | \"pt\" | \"pc\" | \"%\")?"))
}

impl Parse for RsvgLength {
    type Data = LengthDir;
    type Err = AttributeError;

    fn parse (string: &str, dir: LengthDir) -> Result <RsvgLength, AttributeError> {
        let mut input = ParserInput::new (string);
        let mut parser = Parser::new (&mut input);

        let length = {
            let token = parser.next ()
                .map_err (|_| AttributeError::Parse (ParseError::new ("expected number and optional symbol, or number and percentage")))?;

            match *token {
                Token::Number { value, .. } => RsvgLength { length: value as f64,
                                                            unit:   LengthUnit::Default,
                                                            dir:    dir },

                Token::Percentage { unit_value, .. } => RsvgLength { length: unit_value as f64,
                                                                     unit:   LengthUnit::Percent,
                                                                     dir:    dir },

                Token::Dimension { value, ref unit, .. } => {
                    let value = value as f64;

                    match unit.as_ref () {
                        "em" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEm,
                                             dir:    dir },

                        "ex" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEx,
                                             dir:    dir },

                        "pt" => RsvgLength { length: value / POINTS_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "in" => RsvgLength { length: value,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "cm" => RsvgLength { length: value / CM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "mm" => RsvgLength { length: value / MM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "pc" => RsvgLength { length: value / PICA_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "px" => RsvgLength { length: value,
                                             unit:   LengthUnit::Default,
                                             dir:    dir },

                        _ => return Err (make_err ())
                    }
                },

                // FIXME: why are the following in Length?  They should be in FontSize
                Token::Ident (ref cow) => match cow.as_ref () {
                    "larger" => RsvgLength { length: 0.0,
                                             unit:   LengthUnit::RelativeLarger,
                                             dir:    dir },

                    "smaller" => RsvgLength { length: 0.0,
                                              unit:  LengthUnit::RelativeSmaller,
                                              dir:   dir },

                    "xx-small" |
                    "x-small" |
                    "small" |
                    "medium" |
                    "large" |
                    "x-large" |
                    "xx-large" => RsvgLength { length: compute_named_size (&*string),
                                               unit:   LengthUnit::Inch,
                                               dir:    dir },

                    _ => return Err (make_err ())
                },

                _ => return Err (make_err ())
            }
        };

        parser.expect_exhausted ().map_err (|_| make_err ())?;

        Ok (length)
    }
}

impl RsvgLength {
    pub fn new(l: f64, unit: LengthUnit, dir: LengthDir) -> RsvgLength {
        RsvgLength {
            length: l,
            unit: unit,
            dir: dir
        }
    }

    pub fn check_nonnegative (self) -> Result <RsvgLength, AttributeError> {
        if self.length >= 0.0 {
            Ok (self)
        } else {
            Err (AttributeError::Value ("value must be non-negative".to_string ()))
        }
    }

    pub fn normalize (&self, draw_ctx: *const RsvgDrawingCtx) -> f64 {
        match self.unit {
            LengthUnit::Default => {
                self.length
            },

            LengthUnit::Percent => {
                let (width, height) = drawing_ctx::get_view_box_size (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * width },
                    LengthDir::Vertical   => { self.length * height },
                    LengthDir::Both       => { self.length * viewport_percentage (width, height) }
                }
            },

            LengthUnit::FontEm => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx)
            },

            LengthUnit::FontEx => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx) / 2.0
            },

            LengthUnit::Inch => {
                let (dpi_x, dpi_y) = drawing_ctx::get_dpi (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * dpi_x },
                    LengthDir::Vertical   => { self.length * dpi_y },
                    LengthDir::Both       => { self.length * viewport_percentage (dpi_x, dpi_y) }
                }
            },

            // FIXME: these are pending: https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
            LengthUnit::RelativeLarger |
            LengthUnit::RelativeSmaller => { 0.0 }
        }
    }

    pub fn hand_normalize (&self,
                           pixels_per_inch: f64,
                           width_or_height: f64,
                           font_size: f64) -> f64 {
        match self.unit {
            LengthUnit::Default => { self.length },

            LengthUnit::Percent => { self.length * width_or_height },

            LengthUnit::FontEm => { self.length * font_size },

            LengthUnit::FontEx => { self.length * font_size / 2.0 },

            LengthUnit::Inch => { self.length * pixels_per_inch },

            _ => { 0.0 }
        }
    }
}

fn viewport_percentage (x: f64, y: f64) -> f64 {
    /* https://www.w3.org/TR/SVG/coords.html#Units
     *
     * "For any other length value expressed as a percentage of the viewport, the
     * percentage is calculated as the specified percentage of
     * sqrt((actual-width)**2 + (actual-height)**2))/sqrt(2)."
     */
    return (x * x + y * y).sqrt () / SQRT_2;
}

#[no_mangle]
pub extern fn rsvg_length_normalize (raw_length: *const RsvgLength, draw_ctx: *const RsvgDrawingCtx) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.normalize (draw_ctx)
}

#[no_mangle]
pub extern fn rsvg_length_hand_normalize (raw_length: *const RsvgLength,
                                          pixels_per_inch: f64,
                                          width_or_height: f64,
                                          font_size: f64) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.hand_normalize (pixels_per_inch, width_or_height, font_size)
}

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

    #[test]
    fn parses_default () {
        assert_eq! (RsvgLength::parse ("42", LengthDir::Horizontal),
                    Ok (RsvgLength::new(42.0, LengthUnit::Default, LengthDir::Horizontal)));

        assert_eq! (RsvgLength::parse ("-42px", LengthDir::Horizontal),
                    Ok (RsvgLength::new(-42.0, LengthUnit::Default, LengthDir::Horizontal)));
    }

    #[test]
    fn parses_percent () {
        assert_eq! (RsvgLength::parse ("50.0%", LengthDir::Horizontal),
                    Ok (RsvgLength::new(0.5, LengthUnit::Percent, LengthDir::Horizontal)));
    }

    #[test]
    fn parses_font_em () {
        assert_eq! (RsvgLength::parse ("22.5em", LengthDir::Vertical),
                    Ok (RsvgLength::new(22.5, LengthUnit::FontEm, LengthDir::Vertical)));
    }

    #[test]
    fn parses_font_ex () {
        assert_eq! (RsvgLength::parse ("22.5ex", LengthDir::Vertical),
                    Ok (RsvgLength::new(22.5, LengthUnit::FontEx, LengthDir::Vertical)));
    }

    #[test]
    fn parses_physical_units () {
        assert_eq! (RsvgLength::parse ("72pt", LengthDir::Both),
                    Ok (RsvgLength::new(1.0, LengthUnit::Inch, LengthDir::Both)));

        assert_eq! (RsvgLength::parse ("-22.5in", LengthDir::Both),
                    Ok (RsvgLength::new(-22.5, LengthUnit::Inch, LengthDir::Both)));

        assert_eq! (RsvgLength::parse ("-254cm", LengthDir::Both),
                    Ok (RsvgLength::new(-100.0, LengthUnit::Inch, LengthDir::Both)));

        assert_eq! (RsvgLength::parse ("254mm", LengthDir::Both),
                    Ok (RsvgLength::new(10.0, LengthUnit::Inch, LengthDir::Both)));

        assert_eq! (RsvgLength::parse ("60pc", LengthDir::Both),
                    Ok (RsvgLength::new(10.0, LengthUnit::Inch, LengthDir::Both)));
    }

    #[test]
    fn parses_relative_larger () {
        assert_eq! (RsvgLength::parse ("larger", LengthDir::Vertical),
                    Ok (RsvgLength::new(0.0, LengthUnit::RelativeLarger, LengthDir::Vertical)));
    }

    #[test]
    fn parses_relative_smaller () {
        assert_eq! (RsvgLength::parse ("smaller", LengthDir::Vertical),
                    Ok (RsvgLength::new(0.0, LengthUnit::RelativeSmaller, LengthDir::Vertical)));
    }

    #[test]
    fn parses_named_sizes () {
        let names = vec![ "xx-small",
                           "x-small",
                           "small",
                           "medium",
                           "large",
                           "x-large",
                           "xx-large" ];

        let mut previous_value: Option<f64> = None;

        // Just ensure that the values are progressively larger; don't
        // enforce a particular sequence.

        for name in names {
            let length = RsvgLength::parse (name, LengthDir::Both).unwrap ();

            assert_eq! (length.unit, LengthUnit::Inch);
            assert_eq! (length.dir, LengthDir::Both);

            if let Some (v) = previous_value {
                assert! (length.length > v);
            } else {
                previous_value = Some (length.length);
            }

        }
    }

    #[test]
    fn empty_length_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("", LengthDir::Both)));
    }

    #[test]
    fn invalid_unit_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("8furlong", LengthDir::Both)));
    }

    #[test]
    fn invalid_font_size_yields_error () {
        // FIXME: this is intended to test the (absence of) the "larger" et al values.
        // Since they really be in FontSize, not RsvgLength, we should remember
        // to move this test to that type later.
        assert! (is_parse_error (&RsvgLength::parse ("furlong", LengthDir::Both)));
    }

    #[test]
    fn check_nonnegative_works () {
        assert! (RsvgLength::parse ("0", LengthDir::Both).and_then (|l| l.check_nonnegative ()).is_ok ());
        assert! (RsvgLength::parse ("-10", LengthDir::Both).and_then (|l| l.check_nonnegative ()).is_err ());
    }
}
<|endoftext|>


<commit_before>#![feature(test, core)]

extern crate test;
extern crate interpolation;

use interpolation::Ease;

macro_rules! bench_ease {
    ($name: ident, $T: ident, $f: ident) => (
        #[bench]
        fn $name(bencher: &mut test::Bencher) {
            let values = std::iter::count(0.0, 0.1).take(11).collect::<Vec<$T>>();
            bencher.iter(|| {
                for x in values.iter().map(|&x| Ease::$f(x)) {
                    test::black_box(x);
                }
            })
        }
    )
}

bench_ease!(bench_quadratic_in_f32, f32, quadratic_in);
bench_ease!(bench_quadratic_in_f64, f64, quadratic_in);

bench_ease!(bench_quadratic_out_f32, f32, quadratic_out);
bench_ease!(bench_quadratic_out_f64, f64, quadratic_out);

bench_ease!(bench_quadratic_in_out_f32, f32, quadratic_in_out);
bench_ease!(bench_quadratic_in_out_f64, f64, quadratic_in_out);

bench_ease!(bench_cubic_in_f32, f32, cubic_in);
bench_ease!(bench_cubic_in_f64, f64, cubic_in);

bench_ease!(bench_cubic_out_f32, f32, cubic_out);
bench_ease!(bench_cubic_out_f64, f64, cubic_out);

bench_ease!(bench_cubic_in_out_f32, f32, cubic_in_out);
bench_ease!(bench_cubic_in_out_f64, f64, cubic_in_out);

bench_ease!(bench_quartic_in_f32, f32, quartic_in);
bench_ease!(bench_quartic_in_f64, f64, quartic_in);

bench_ease!(bench_quartic_out_f32, f32, quartic_out);
bench_ease!(bench_quartic_out_f64, f64, quartic_out);

bench_ease!(bench_quartic_in_out_f32, f32, quartic_in_out);
bench_ease!(bench_quartic_in_out_f64, f64, quartic_in_out);

bench_ease!(bench_quintic_in_f32, f32, quintic_in);
bench_ease!(bench_quintic_in_f64, f64, quintic_in);

bench_ease!(bench_quintic_out_f32, f32, quintic_out);
bench_ease!(bench_quintic_out_f64, f64, quintic_out);

bench_ease!(bench_quintic_in_out_f32, f32, quintic_in_out);
bench_ease!(bench_quintic_in_out_f64, f64, quintic_in_out);

bench_ease!(bench_sine_in_f32, f32, sine_in);
bench_ease!(bench_sine_in_f64, f64, sine_in);

bench_ease!(bench_sine_out_f32, f32, sine_out);
bench_ease!(bench_sine_out_f64, f64, sine_out);

bench_ease!(bench_sine_in_out_f32, f32, sine_in_out);
bench_ease!(bench_sine_in_out_f64, f64, sine_in_out);

bench_ease!(bench_circular_in_f32, f32, circular_in);
bench_ease!(bench_circular_in_f64, f64, circular_in);

bench_ease!(bench_circular_out_f32, f32, circular_out);
bench_ease!(bench_circular_out_f64, f64, circular_out);

bench_ease!(bench_circular_in_out_f32, f32, circular_in_out);
bench_ease!(bench_circular_in_out_f64, f64, circular_in_out);

bench_ease!(bench_exponential_in_f32, f32, exponential_in);
bench_ease!(bench_exponential_in_f64, f64, exponential_in);

bench_ease!(bench_exponential_out_f32, f32, exponential_out);
bench_ease!(bench_exponential_out_f64, f64, exponential_out);

bench_ease!(bench_exponential_in_out_f32, f32, exponential_in_out);
bench_ease!(bench_exponential_in_out_f64, f64, exponential_in_out);

bench_ease!(bench_elastic_in_f32, f32, elastic_in);
bench_ease!(bench_elastic_in_f64, f64, elastic_in);

bench_ease!(bench_elastic_out_f32, f32, elastic_out);
bench_ease!(bench_elastic_out_f64, f64, elastic_out);

bench_ease!(bench_elastic_in_out_f32, f32, elastic_in_out);
bench_ease!(bench_elastic_in_out_f64, f64, elastic_in_out);

bench_ease!(bench_back_in_f32, f32, back_in);
bench_ease!(bench_back_in_f64, f64, back_in);

bench_ease!(bench_back_out_f32, f32, back_out);
bench_ease!(bench_back_out_f64, f64, back_out);

bench_ease!(bench_back_in_out_f32, f32, back_in_out);
bench_ease!(bench_back_in_out_f64, f64, back_in_out);

bench_ease!(bench_bounce_in_f32, f32, bounce_in);
bench_ease!(bench_bounce_in_f64, f64, bounce_in);

bench_ease!(bench_bounce_out_f32, f32, bounce_out);
bench_ease!(bench_bounce_out_f64, f64, bounce_out);

bench_ease!(bench_bounce_in_out_f32, f32, bounce_in_out);
bench_ease!(bench_bounce_in_out_f64, f64, bounce_in_out);

<commit_msg>Updated benches.<commit_after>#![feature(test)]

extern crate test;
extern crate interpolation;

use interpolation::Ease;

macro_rules! bench_ease {
    ($name: ident, $T: ident, $f: ident) => (
        #[bench]
        fn $name(bencher: &mut test::Bencher) {
            let values = (0..11).map(|x| x as $T / 10.0).collect::<Vec<$T>>();
            bencher.iter(|| {
                for x in values.iter().map(|&x| Ease::$f(x)) {
                    test::black_box(x);
                }
            })
        }
    )
}

bench_ease!(bench_quadratic_in_f32, f32, quadratic_in);
bench_ease!(bench_quadratic_in_f64, f64, quadratic_in);

bench_ease!(bench_quadratic_out_f32, f32, quadratic_out);
bench_ease!(bench_quadratic_out_f64, f64, quadratic_out);

bench_ease!(bench_quadratic_in_out_f32, f32, quadratic_in_out);
bench_ease!(bench_quadratic_in_out_f64, f64, quadratic_in_out);

bench_ease!(bench_cubic_in_f32, f32, cubic_in);
bench_ease!(bench_cubic_in_f64, f64, cubic_in);

bench_ease!(bench_cubic_out_f32, f32, cubic_out);
bench_ease!(bench_cubic_out_f64, f64, cubic_out);

bench_ease!(bench_cubic_in_out_f32, f32, cubic_in_out);
bench_ease!(bench_cubic_in_out_f64, f64, cubic_in_out);

bench_ease!(bench_quartic_in_f32, f32, quartic_in);
bench_ease!(bench_quartic_in_f64, f64, quartic_in);

bench_ease!(bench_quartic_out_f32, f32, quartic_out);
bench_ease!(bench_quartic_out_f64, f64, quartic_out);

bench_ease!(bench_quartic_in_out_f32, f32, quartic_in_out);
bench_ease!(bench_quartic_in_out_f64, f64, quartic_in_out);

bench_ease!(bench_quintic_in_f32, f32, quintic_in);
bench_ease!(bench_quintic_in_f64, f64, quintic_in);

bench_ease!(bench_quintic_out_f32, f32, quintic_out);
bench_ease!(bench_quintic_out_f64, f64, quintic_out);

bench_ease!(bench_quintic_in_out_f32, f32, quintic_in_out);
bench_ease!(bench_quintic_in_out_f64, f64, quintic_in_out);

bench_ease!(bench_sine_in_f32, f32, sine_in);
bench_ease!(bench_sine_in_f64, f64, sine_in);

bench_ease!(bench_sine_out_f32, f32, sine_out);
bench_ease!(bench_sine_out_f64, f64, sine_out);

bench_ease!(bench_sine_in_out_f32, f32, sine_in_out);
bench_ease!(bench_sine_in_out_f64, f64, sine_in_out);

bench_ease!(bench_circular_in_f32, f32, circular_in);
bench_ease!(bench_circular_in_f64, f64, circular_in);

bench_ease!(bench_circular_out_f32, f32, circular_out);
bench_ease!(bench_circular_out_f64, f64, circular_out);

bench_ease!(bench_circular_in_out_f32, f32, circular_in_out);
bench_ease!(bench_circular_in_out_f64, f64, circular_in_out);

bench_ease!(bench_exponential_in_f32, f32, exponential_in);
bench_ease!(bench_exponential_in_f64, f64, exponential_in);

bench_ease!(bench_exponential_out_f32, f32, exponential_out);
bench_ease!(bench_exponential_out_f64, f64, exponential_out);

bench_ease!(bench_exponential_in_out_f32, f32, exponential_in_out);
bench_ease!(bench_exponential_in_out_f64, f64, exponential_in_out);

bench_ease!(bench_elastic_in_f32, f32, elastic_in);
bench_ease!(bench_elastic_in_f64, f64, elastic_in);

bench_ease!(bench_elastic_out_f32, f32, elastic_out);
bench_ease!(bench_elastic_out_f64, f64, elastic_out);

bench_ease!(bench_elastic_in_out_f32, f32, elastic_in_out);
bench_ease!(bench_elastic_in_out_f64, f64, elastic_in_out);

bench_ease!(bench_back_in_f32, f32, back_in);
bench_ease!(bench_back_in_f64, f64, back_in);

bench_ease!(bench_back_out_f32, f32, back_out);
bench_ease!(bench_back_out_f64, f64, back_out);

bench_ease!(bench_back_in_out_f32, f32, back_in_out);
bench_ease!(bench_back_in_out_f64, f64, back_in_out);

bench_ease!(bench_bounce_in_f32, f32, bounce_in);
bench_ease!(bench_bounce_in_f64, f64, bounce_in);

bench_ease!(bench_bounce_out_f32, f32, bounce_out);
bench_ease!(bench_bounce_out_f64, f64, bounce_out);

bench_ease!(bench_bounce_in_out_f32, f32, bounce_in_out);
bench_ease!(bench_bounce_in_out_f64, f64, bounce_in_out);

<|endoftext|>


<commit_before>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::{debug, info};
use oak_abi::proto::oak::application::{
    node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
    WebAssemblyConfiguration,
};
use prost::Message;
use std::{collections::HashMap, path::PathBuf, process::Command};
use tonic::transport::Identity;

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    // Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
    // should end with `target` so that it gets automatically ignored by our `.gitignore`.
    let target_dir = PathBuf::from("oak_tests/target");

    Command::new("cargo")
        .args(&[
            "build",
            &format!(
                "--target-dir={}",
                target_dir.to_str().expect("invalid target dir")
            ),
            "--target=wasm32-unknown-unknown",
            &format!("--manifest-path={}", cargo_path),
        ])
        .env_remove("RUSTFLAGS")
        .spawn()?
        .wait()?;

    let mut module_path = target_dir;
    module_path.push("wasm32-unknown-unknown/debug");
    module_path.push(module_name);

    info!("compiled Wasm module path: {:?}", module_path);

    std::fs::read(module_path)
}

/// Default entrypoint name for the module under test.
const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main";

const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml";
const MODULE_WASM_SUFFIX: &str = ".wasm";

/// Convenience helper to build and run a single-Node Application with the given module name, using
/// the default name "oak_main" for its entrypoint.
pub fn run_single_module_default(
    module_config_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME)
}

/// Convenience helper to build and run a single-Node application with the given module name, using
/// the provided entrypoint name.
pub fn run_single_module(
    module_config_name: &str,
    entrypoint_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    let (app_config, runtime_config, grpc_config) =
        runtime_configs(module_config_name, entrypoint_name);
    oak_runtime::configure_and_run(app_config, runtime_config, grpc_config)
}

/// Build the set of configurations needed to launch a test Runtime instance that
/// runs a single-Node application with the given module name and entrypoint.
pub fn runtime_configs(
    module_config_name: &str,
    entrypoint_name: &str,
) -> (
    ApplicationConfiguration,
    oak_runtime::RuntimeConfiguration,
    oak_runtime::GrpcConfiguration,
) {
    let wasm: HashMap<String, Vec<u8>> = [(
        module_config_name.to_owned(),
        compile_rust_wasm(
            DEFAULT_MODULE_MANIFEST,
            &(module_config_name.to_owned() + MODULE_WASM_SUFFIX),
        )
        .expect("failed to build wasm module"),
    )]
    .iter()
    .cloned()
    .collect();

    (
        ApplicationConfiguration {
            wasm_modules: wasm,
            initial_node_configuration: Some(NodeConfiguration {
                name: "test".to_string(),
                config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                    wasm_module_name: module_config_name.to_string(),
                    wasm_entrypoint_name: entrypoint_name.to_string(),
                })),
            }),
        },
        oak_runtime::RuntimeConfiguration {
            metrics_port: Some(9090),
            introspect_port: Some(1909),
        },
        oak_runtime::GrpcConfiguration {
            grpc_server_tls_identity: Some(Identity::from_pem(
                include_str!("../certs/local.pem"),
                include_str!("../certs/local.key"),
            )),
            grpc_client_root_tls_certificate: Some(
                oak_runtime::config::load_certificate(&include_str!("../certs/ca.pem")).unwrap(),
            ),
            oidc_client_info: None,
        },
    )
}

/// Inject a gRPC request into an Oak Application under test and return the corresponding
/// response (or error).
pub fn grpc_request<R, Q>(
    proxy: &oak_runtime::RuntimeProxy,
    handle: oak_abi::Handle,
    method_name: &str,
    req: &R,
) -> oak::grpc::Result<Q>
where
    R: prost::Message,
    Q: prost::Message + Default,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req =
        oak_abi::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![],
    };

    grpc_req
        .encode(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (req_write_handle, req_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");
    proxy
        .channel_write(req_write_handle, req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (rsp_write_handle, rsp_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![req_read_handle, rsp_write_handle],
    };

    // Send the notification message (with attached handles) into the Node under test.
    proxy
        .channel_write(handle, notify_msg)
        .expect("could not write message");
    proxy
        .channel_close(req_write_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(req_read_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(rsp_write_handle)
        .expect("failed to close channel");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match proxy.channel_read(rsp_read_handle) {
            Ok(Some(r)) => r,
            Ok(None) => {
                debug!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        proxy
            .channel_close(rsp_read_handle)
            .expect("failed to close channel");
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let rsp = oak_abi::proto::oak::encap::GrpcResponse::decode(rsp.data.as_slice())
            .expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        let status = rsp.status.unwrap_or_default();
        if status.code != oak::grpc::Code::Ok as i32 {
            return Err(status);
        }
        let rsp =
            Q::decode(rsp.rsp_msg.as_slice()).expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}
<commit_msg>sdk: split out runtime_configs_wasm() helper<commit_after>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::{debug, info};
use oak_abi::proto::oak::application::{
    node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
    WebAssemblyConfiguration,
};
use prost::Message;
use std::{collections::HashMap, path::PathBuf, process::Command};
use tonic::transport::Identity;

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    // Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
    // should end with `target` so that it gets automatically ignored by our `.gitignore`.
    let target_dir = PathBuf::from("oak_tests/target");

    Command::new("cargo")
        .args(&[
            "build",
            &format!(
                "--target-dir={}",
                target_dir.to_str().expect("invalid target dir")
            ),
            "--target=wasm32-unknown-unknown",
            &format!("--manifest-path={}", cargo_path),
        ])
        .env_remove("RUSTFLAGS")
        .spawn()?
        .wait()?;

    let mut module_path = target_dir;
    module_path.push("wasm32-unknown-unknown/debug");
    module_path.push(module_name);

    info!("compiled Wasm module path: {:?}", module_path);

    std::fs::read(module_path)
}

/// Default entrypoint name for the module under test.
const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main";

const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml";
const MODULE_WASM_SUFFIX: &str = ".wasm";

/// Convenience helper to build and run a single-Node Application with the given module name, using
/// the default name "oak_main" for its entrypoint.
pub fn run_single_module_default(
    module_config_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME)
}

/// Convenience helper to build and run a single-Node application with the given module name, using
/// the provided entrypoint name.
pub fn run_single_module(
    module_config_name: &str,
    entrypoint_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    let (app_config, runtime_config, grpc_config) =
        runtime_configs(module_config_name, entrypoint_name);
    oak_runtime::configure_and_run(app_config, runtime_config, grpc_config)
}

/// Build the set of configurations needed to launch a test Runtime instance that
/// runs a single-Node application with the given module name and entrypoint.
pub fn runtime_configs(
    module_config_name: &str,
    entrypoint_name: &str,
) -> (
    ApplicationConfiguration,
    oak_runtime::RuntimeConfiguration,
    oak_runtime::GrpcConfiguration,
) {
    let wasm: HashMap<String, Vec<u8>> = [(
        module_config_name.to_owned(),
        compile_rust_wasm(
            DEFAULT_MODULE_MANIFEST,
            &(module_config_name.to_owned() + MODULE_WASM_SUFFIX),
        )
        .expect("failed to build wasm module"),
    )]
    .iter()
    .cloned()
    .collect();

    runtime_configs_wasm(wasm, module_config_name, entrypoint_name)
}

/// Build the set of configurations needed to launch a test Runtime instance that runs the given
/// collection of Wasm modules, starting with the given module name and entrypoint.
pub fn runtime_configs_wasm(
    wasm_modules: HashMap<String, Vec<u8>>,
    module_config_name: &str,
    entrypoint_name: &str,
) -> (
    ApplicationConfiguration,
    oak_runtime::RuntimeConfiguration,
    oak_runtime::GrpcConfiguration,
) {
    (
        ApplicationConfiguration {
            wasm_modules,
            initial_node_configuration: Some(NodeConfiguration {
                name: "test".to_string(),
                config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                    wasm_module_name: module_config_name.to_string(),
                    wasm_entrypoint_name: entrypoint_name.to_string(),
                })),
            }),
        },
        oak_runtime::RuntimeConfiguration {
            metrics_port: Some(9090),
            introspect_port: Some(1909),
        },
        oak_runtime::GrpcConfiguration {
            grpc_server_tls_identity: Some(Identity::from_pem(
                include_str!("../certs/local.pem"),
                include_str!("../certs/local.key"),
            )),
            grpc_client_root_tls_certificate: Some(
                oak_runtime::config::load_certificate(&include_str!("../certs/ca.pem")).unwrap(),
            ),
            oidc_client_info: None,
        },
    )
}

/// Inject a gRPC request into an Oak Application under test and return the corresponding
/// response (or error).
pub fn grpc_request<R, Q>(
    proxy: &oak_runtime::RuntimeProxy,
    handle: oak_abi::Handle,
    method_name: &str,
    req: &R,
) -> oak::grpc::Result<Q>
where
    R: prost::Message,
    Q: prost::Message + Default,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req =
        oak_abi::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![],
    };

    grpc_req
        .encode(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (req_write_handle, req_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");
    proxy
        .channel_write(req_write_handle, req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (rsp_write_handle, rsp_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![req_read_handle, rsp_write_handle],
    };

    // Send the notification message (with attached handles) into the Node under test.
    proxy
        .channel_write(handle, notify_msg)
        .expect("could not write message");
    proxy
        .channel_close(req_write_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(req_read_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(rsp_write_handle)
        .expect("failed to close channel");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match proxy.channel_read(rsp_read_handle) {
            Ok(Some(r)) => r,
            Ok(None) => {
                debug!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        proxy
            .channel_close(rsp_read_handle)
            .expect("failed to close channel");
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let rsp = oak_abi::proto::oak::encap::GrpcResponse::decode(rsp.data.as_slice())
            .expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        let status = rsp.status.unwrap_or_default();
        if status.code != oak::grpc::Code::Ok as i32 {
            return Err(status);
        }
        let rsp =
            Q::decode(rsp.rsp_msg.as_slice()).expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}
<|endoftext|>


<commit_before>use database::models::NewTemporaryRole;
use util::ParsedDuration;

use chrono::{Utc, Duration};

use diesel;
use diesel::prelude::*;

use lalafell::error::*;
use lalafell::commands::prelude::*;
use lalafell::commands::{ChannelOrId, MentionOrId};

use structopt::clap::ArgGroup;

use unicase::UniCase;

use std::sync::Arc;

#[derive(BotCommand)]
pub struct TemporaryRoleCommand;

#[derive(Debug, StructOpt)]
#[structopt(about = "Give a role to a member for a given number of messages or time")]
pub struct Params {
  #[structopt(
    short = "w",
    long = "who",
    help = "The member to assign the role to"
  )]
  who: MentionOrId,
  #[structopt(
    short = "r",
    long = "role",
    help = "The role to assign to the member"
  )]
  role: String,
  #[structopt(
    short = "m",
    long = "messages",
    help = "The amount of messages for the role to last"
  )]
  messages: Option<u32>,
  #[structopt(
    short = "c",
    long = "channel",
    help = "The channel to count messages against, if any",
    conflicts_with = "time"
  )]
  channel: Option<ChannelOrId>,
  #[structopt(
    short = "t",
    long = "time",
    help = "The amount of time for the role to last"
  )]
  time: Option<ParsedDuration>
}

impl HasParams for TemporaryRoleCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for TemporaryRoleCommand {
  fn run(&self, _: &Context, msg: &Message, guild_id: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let member = guild_id.member(&msg.author).chain_err(|| "could not get member")?;
    if !member.permissions().chain_err(|| "could not get permissions")?.manage_roles() {
      return Err(ExternalCommandFailure::default()
        .message(|e: CreateEmbed| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let params = self.params_then("temporaryrole", params, |a| a
      .setting(::structopt::clap::AppSettings::ArgRequiredElseHelp)
      .group(ArgGroup::with_name("m_or_t")
        .args(&["messages", "time"])
        .required(true)))?;

    let guild = guild_id.find().chain_err(|| "could not find guild in cache")?;

    let mut target = match guild_id.member(*params.who) {
      Ok(m) => m,
      Err(_) => return Err("That person is not in this guild.".into())
    };

    let role_name = UniCase::new(params.role);
    let role = match guild.read().roles.values().find(|r| UniCase::new(r.name.as_str()) == role_name) {
      Some(r) => r.id,
      None => return Err("No such role.".into())
    };

    let messages = params.messages.map(|m| m as i32);
    let time = params.time
      .as_ref()
      .map(|t| Utc::now() + Duration::seconds(**t as i64))
      .map(|t| t.timestamp());
    let ntr = NewTemporaryRole::new(guild_id.0, params.who.0, role.0, msg.id.0, params.channel.map(|x| x.0), messages, time);
    let temp_role = ::bot::with_connection(|c| {
      use database::schema::temporary_roles::dsl;

      diesel::insert_into(dsl::temporary_roles).values(&ntr).get_result(c)
    }).chain_err(|| "could not store new temporary role")?;

    if let Some(t) = params.time {
      if *t < 600 {
        ::std::thread::spawn(move || {
          ::std::thread::sleep(Duration::seconds(*t as i64).to_std().unwrap());
          ::tasks::temporary_roles::remove_temporary_role(&temp_role);
        });
      }
    }

    target.add_role(role).chain_err(|| "could not add role")?;
    Ok(CommandSuccess::default())
  }
}
<commit_msg>fix: allow message and time and require message with channel<commit_after>use database::models::NewTemporaryRole;
use util::ParsedDuration;

use chrono::{Utc, Duration};

use diesel;
use diesel::prelude::*;

use lalafell::error::*;
use lalafell::commands::prelude::*;
use lalafell::commands::{ChannelOrId, MentionOrId};

use structopt::clap::ArgGroup;

use unicase::UniCase;

use std::sync::Arc;

#[derive(BotCommand)]
pub struct TemporaryRoleCommand;

#[derive(Debug, StructOpt)]
#[structopt(about = "Give a role to a member for a given number of messages or time")]
pub struct Params {
  #[structopt(
    short = "w",
    long = "who",
    help = "The member to assign the role to"
  )]
  who: MentionOrId,
  #[structopt(
    short = "r",
    long = "role",
    help = "The role to assign to the member"
  )]
  role: String,
  #[structopt(
    short = "m",
    long = "messages",
    help = "The amount of messages for the role to last"
  )]
  messages: Option<u32>,
  #[structopt(
    short = "c",
    long = "channel",
    help = "The channel to count messages against, if any",
    requires = "messages"
  )]
  channel: Option<ChannelOrId>,
  #[structopt(
    short = "t",
    long = "time",
    help = "The amount of time for the role to last"
  )]
  time: Option<ParsedDuration>
}

impl HasParams for TemporaryRoleCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for TemporaryRoleCommand {
  fn run(&self, _: &Context, msg: &Message, guild_id: GuildId, _: Arc<RwLock<GuildChannel>>, params: &[&str]) -> CommandResult<'a> {
    let member = guild_id.member(&msg.author).chain_err(|| "could not get member")?;
    if !member.permissions().chain_err(|| "could not get permissions")?.manage_roles() {
      return Err(ExternalCommandFailure::default()
        .message(|e: CreateEmbed| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let params = self.params_then("temporaryrole", params, |a| a
      .setting(::structopt::clap::AppSettings::ArgRequiredElseHelp)
      .group(ArgGroup::with_name("m_or_t")
        .args(&["messages", "time"])
        .multiple(true)
        .required(true)))?;

    let guild = guild_id.find().chain_err(|| "could not find guild in cache")?;

    let mut target = match guild_id.member(*params.who) {
      Ok(m) => m,
      Err(_) => return Err("That person is not in this guild.".into())
    };

    let role_name = UniCase::new(params.role);
    let role = match guild.read().roles.values().find(|r| UniCase::new(r.name.as_str()) == role_name) {
      Some(r) => r.id,
      None => return Err("No such role.".into())
    };

    let messages = params.messages.map(|m| m as i32);
    let time = params.time
      .as_ref()
      .map(|t| Utc::now() + Duration::seconds(**t as i64))
      .map(|t| t.timestamp());
    let ntr = NewTemporaryRole::new(guild_id.0, params.who.0, role.0, msg.id.0, params.channel.map(|x| x.0), messages, time);
    let temp_role = ::bot::with_connection(|c| {
      use database::schema::temporary_roles::dsl;

      diesel::insert_into(dsl::temporary_roles).values(&ntr).get_result(c)
    }).chain_err(|| "could not store new temporary role")?;

    if let Some(t) = params.time {
      if *t < 600 {
        ::std::thread::spawn(move || {
          ::std::thread::sleep(Duration::seconds(*t as i64).to_std().unwrap());
          ::tasks::temporary_roles::remove_temporary_role(&temp_role);
        });
      }
    }

    target.add_role(role).chain_err(|| "could not add role")?;
    Ok(CommandSuccess::default())
  }
}
<|endoftext|>