<commit_before>use nom::{
  be_u8, be_u16, be_u32, be_u64,
  le_u32,
  IResult,
  ErrorCode, Err,
  Needed,
};

use std::str::from_utf8;

use metadata::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

use utility::to_u32;

macro_rules! skip_bytes(
  ($input: expr, $length: expr) => (
    {
      match take!($input, $length) {
        IResult::Done(i, bytes)   => {
          let sum = bytes.iter().fold(0, |result, byte| result + byte);

          if sum == 0 {
            IResult::Done(i, bytes)
          } else {
            IResult::Error(Err::Position(ErrorCode::Digit as u32, $input))
          }
        }
        IResult::Error(error)     => IResult::Error(error),
        IResult::Incomplete(need) => IResult::Incomplete(need),
      }
    }
  );
);

named!(pub stream_info <&[u8], BlockData>,
  chain!(
    min_block_size: be_u16 ~
    max_block_size: be_u16 ~
    min_frame_size: map!(take!(3), to_u32) ~
    max_frame_size: map!(take!(3), to_u32) ~
    bytes: take!(8) ~
    md5_sum: take!(16),
    || {
      let sample_rate     = ((bytes[0] as u32) << 12) +
                            ((bytes[1] as u32) << 4)  +
                            ((bytes[2] as u32) >> 4);
      let channels        = (bytes[2] >> 1) & 0b0111;
      let bits_per_sample = ((bytes[2] & 0b01) << 4) +
                            bytes[3] >> 4;
      let total_samples   = (((bytes[3] as u64) & 0x0f) << 32) +
                            ((bytes[4] as u64) << 24) +
                            ((bytes[5] as u64) << 16) +
                            ((bytes[6] as u64) << 8) +
                            (bytes[7] as u64);

      BlockData::StreamInfo(StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: min_frame_size,
        max_frame_size: max_frame_size,
        sample_rate: sample_rate,
        channels: channels + 1,
        bits_per_sample: bits_per_sample + 1,
        total_samples: total_samples,
        md5_sum: md5_sum,
      })
    }
  )
);

pub fn padding(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, skip_bytes!(length), |_| BlockData::Padding(0))
}

fn application(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  chain!(input,
    id: take_str!(4) ~
    data: take!(length - 4),
    || {
      BlockData::Application(Application {
        id: id,
        data: data,
      })
    }
  )
}

named!(seek_point <&[u8], SeekPoint>,
  chain!(
    sample_number: be_u64 ~
    stream_offset: be_u64 ~
    frame_samples: be_u16,
    || {
      SeekPoint {
        sample_number: sample_number,
        stream_offset: stream_offset,
        frame_samples: frame_samples,
      }
    }
  )
);

fn seek_table(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  let seek_count = (length / 18) as usize;

  map!(input, count!(seek_point, seek_count), BlockData::SeekTable)
}

named!(vorbis_comment <&[u8], BlockData>,
  chain!(
    vendor_string_length: le_u32 ~
    vendor_string: take_str!(vendor_string_length)  ~
    number_of_comments: le_u32 ~
    comments: count!(comment_field, number_of_comments as usize),
    || {
      BlockData::VorbisComment(VorbisComment {
        vendor_string: vendor_string,
        comments: comments,
      })
    }
  )
);

named!(comment_field <&[u8], &str>,
  chain!(
    comment_length: le_u32 ~
    comment: take_str!(comment_length),
    || { comment }
  )
);

named!(cue_sheet <&[u8], BlockData>,
  chain!(
    media_catalog_number: take_str!(128) ~
    lead_in: be_u64 ~
    bytes: take!(259) ~ // TODO: last (7 + 258 * 8) bits must be 0
    num_tracks: be_u8 ~
    tracks: count!(cue_sheet_track, num_tracks as usize),
    || {
      let is_cd = ((bytes[0] >> 7) & 0b01) == 1;

      BlockData::CueSheet(CueSheet {
        media_catalog_number: media_catalog_number,
        lead_in: lead_in,
        is_cd: is_cd,
        tracks: tracks,
      })
    }
  )
);

named!(cue_sheet_track <&[u8], CueSheetTrack>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    isrc: take_str!(12) ~
    bytes: take!(14) ~ // TODO: last (6 + 13 * 8) bits must be 0
    num_indices: be_u8 ~
    indices: cond!(
      num_indices != 0,
      count!(cue_sheet_track_index, num_indices as usize)
    ),
    || {
      let isnt_audio      = ((bytes[0] >> 7) & 0b01) == 1;
      let is_pre_emphasis = ((bytes[0] >> 6) & 0b01) == 1;

      CueSheetTrack {
        offset: offset,
        number: number,
        isrc: isrc,
        isnt_audio: isnt_audio,
        is_pre_emphasis: is_pre_emphasis,
        indices: indices.unwrap_or(Vec::new()),
      }
    }
  )
);

named!(cue_sheet_track_index <&[u8], CueSheetTrackIndex>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    skip_bytes!(3),
    || {
      CueSheetTrackIndex {
        offset: offset,
        number: number,
      }
    }
  )
);

named!(picture <&[u8], BlockData>,
  chain!(
    picture_type_num: be_u32 ~
    mime_type_length:  be_u32 ~
    mime_type: take_str!(mime_type_length) ~
    description_length: be_u32 ~
    description: take_str!(description_length) ~
    width: be_u32 ~
    height: be_u32 ~
    depth: be_u32 ~
    colors: be_u32 ~
    data_length: be_u32 ~
    data: take!(data_length),
    || {
      let picture_type = match picture_type_num {
        0  => PictureType::Other,
        1  => PictureType::FileIconStandard,
        2  => PictureType::FileIcon,
        3  => PictureType::FrontCover,
        4  => PictureType::BackCover,
        5  => PictureType::LeafletPage,
        6  => PictureType::Media,
        7  => PictureType::LeadArtist,
        8  => PictureType::Artist,
        9  => PictureType::Conductor,
        10 => PictureType::Band,
        11 => PictureType::Composer,
        12 => PictureType::Lyricist,
        13 => PictureType::RecordingLocation,
        14 => PictureType::DuringRecording,
        15 => PictureType::DuringPerformace,
        16 => PictureType::VideoScreenCapture,
        17 => PictureType::Fish,
        18 => PictureType::Illustration,
        19 => PictureType::BandLogoType,
        20 => PictureType::PublisherLogoType,
        _  => PictureType::Other,
      };

      BlockData::Picture(Picture {
        picture_type: picture_type,
        mime_type: mime_type,
        description: description,
        width: width,
        height: height,
        depth: depth,
        colors: colors,
        data: data,
      })
    }
  )
);

// As of FLAC v1.3.1, there is support for up to 127 different metadata
// `Block`s but actually 7 that are implemented. When the `Block` type isn't
// recognised, this block gets skipped over with this parser.
fn unknown(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), BlockData::Unknown)
}

named!(pub header <&[u8], (bool, u8, u32)>,
  chain!(
    block_byte: be_u8 ~
    length: map!(take!(3), to_u32),
    || {
      let is_last    = (block_byte >> 7) == 1;
      let block_type = block_byte & 0b01111111;

      (is_last, block_type, length)
    }
  )
);

fn block_data(input: &[u8], block_type: u8, length: u32)
              -> IResult<&[u8], BlockData> {
  match block_type {
    0       => stream_info(input),
    1       => padding(input, length),
    2       => application(input, length),
    3       => seek_table(input, length),
    4       => vorbis_comment(input),
    5       => cue_sheet(input),
    6       => picture(input),
    7...126 => unknown(input, length),
    _       => IResult::Error(Err::Position(ErrorCode::Alt as u32, input)),
  }
}

named!(block <&[u8], Block>,
  chain!(
    block_header: header ~
    data: apply!(block_data, block_header.1, block_header.2),
    || {
      Block {
        is_last: block_header.0,
        length: block_header.2,
        data: data
      }
    }
  )
);

/// Parses one or more metadata `Block`s
///
/// The first block should always be `StreamInfo` since that is the only
/// required `Block`. At the moment `many_blocks` doesn't check that
/// requirement.
pub fn many_blocks(input: &[u8]) -> IResult<&[u8], Vec<Block>> {
  let mut is_last   = false;
  let mut blocks    = Vec::new();
  let mut start     = 0;
  let mut remaining = input.len();

  while !is_last {
    match block(&input[start..]) {
      IResult::Done(i, block) => {
        let result_len = i.len();

        if result_len == input[start..].len() {
          break;
        }

        start    += remaining - result_len;
        remaining = result_len;
        is_last   = block.is_last;

        blocks.push(block);
      }
      _                       => break,
    }
  }

  if blocks.len() == 0 {
    IResult::Error(Err::Position(ErrorCode::Many1 as u32, input))
  } else if is_last {
    IResult::Done(&input[start..], blocks)
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use metadata::{
    BlockData,
    StreamInfo,
  };
  use nom::IResult;

  #[test]
  fn test_header() {
    let inputs = [ [0x80, 0x00, 0x00, 0x22]
                 , [0x01, 0x00, 0x04, 0x00]
                 , [0x84, 0x00, 0x00, 0xf8]
                 ];

    assert!(header(&inputs[0]) == IResult::Done(&[], (true, 0, 34)),
            "Header Test #1");
    assert!(header(&inputs[1]) == IResult::Done(&[], (false, 1, 1024)),
            "Header Test #2");
    assert!(header(&inputs[2]) == IResult::Done(&[], (true, 4, 248)),
            "Header Test #3");
  }

  #[test]
  fn test_stream_info() {
    let input   = [ 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10
                  , 0x01, 0xf4, 0x02, 0x70, 0x00, 0x01, 0x38, 0x80, 0xa0, 0x42
                  , 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b, 0x94, 0xa8
                  , 0x36, 0x08, 0xd1, 0x1a
                  ];

    let md5_sum = [ 0xa0, 0x42, 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b
                  , 0x94, 0xa8, 0x36, 0x08, 0xd1, 0x1a
                  ];

    let result = BlockData::StreamInfo(StreamInfo {
      min_block_size: 4608,
      max_block_size: 4608,
      min_frame_size: 14,
      max_frame_size: 16,
      sample_rate: 8000,
      channels: 2,
      bits_per_sample: 8,
      total_samples: 80000,
      md5_sum: &md5_sum,
    });

    assert_eq!(stream_info(&input), IResult::Done(&[][..], result));
  }
}
<commit_msg>Add both a passing and failing test for padding<commit_after>use nom::{
  be_u8, be_u16, be_u32, be_u64,
  le_u32,
  IResult,
  ErrorCode, Err,
  Needed,
};

use std::str::from_utf8;

use metadata::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

use utility::to_u32;

macro_rules! skip_bytes(
  ($input: expr, $length: expr) => (
    {
      match take!($input, $length) {
        IResult::Done(i, bytes)   => {
          let sum = bytes.iter().fold(0, |result, byte| result + byte);

          if sum == 0 {
            IResult::Done(i, bytes)
          } else {
            IResult::Error(Err::Position(ErrorCode::Digit as u32, $input))
          }
        }
        IResult::Error(error)     => IResult::Error(error),
        IResult::Incomplete(need) => IResult::Incomplete(need),
      }
    }
  );
);

named!(pub stream_info <&[u8], BlockData>,
  chain!(
    min_block_size: be_u16 ~
    max_block_size: be_u16 ~
    min_frame_size: map!(take!(3), to_u32) ~
    max_frame_size: map!(take!(3), to_u32) ~
    bytes: take!(8) ~
    md5_sum: take!(16),
    || {
      let sample_rate     = ((bytes[0] as u32) << 12) +
                            ((bytes[1] as u32) << 4)  +
                            ((bytes[2] as u32) >> 4);
      let channels        = (bytes[2] >> 1) & 0b0111;
      let bits_per_sample = ((bytes[2] & 0b01) << 4) +
                            bytes[3] >> 4;
      let total_samples   = (((bytes[3] as u64) & 0x0f) << 32) +
                            ((bytes[4] as u64) << 24) +
                            ((bytes[5] as u64) << 16) +
                            ((bytes[6] as u64) << 8) +
                            (bytes[7] as u64);

      BlockData::StreamInfo(StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: min_frame_size,
        max_frame_size: max_frame_size,
        sample_rate: sample_rate,
        channels: channels + 1,
        bits_per_sample: bits_per_sample + 1,
        total_samples: total_samples,
        md5_sum: md5_sum,
      })
    }
  )
);

pub fn padding(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, skip_bytes!(length), |_| BlockData::Padding(0))
}

fn application(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  chain!(input,
    id: take_str!(4) ~
    data: take!(length - 4),
    || {
      BlockData::Application(Application {
        id: id,
        data: data,
      })
    }
  )
}

named!(seek_point <&[u8], SeekPoint>,
  chain!(
    sample_number: be_u64 ~
    stream_offset: be_u64 ~
    frame_samples: be_u16,
    || {
      SeekPoint {
        sample_number: sample_number,
        stream_offset: stream_offset,
        frame_samples: frame_samples,
      }
    }
  )
);

fn seek_table(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  let seek_count = (length / 18) as usize;

  map!(input, count!(seek_point, seek_count), BlockData::SeekTable)
}

named!(vorbis_comment <&[u8], BlockData>,
  chain!(
    vendor_string_length: le_u32 ~
    vendor_string: take_str!(vendor_string_length)  ~
    number_of_comments: le_u32 ~
    comments: count!(comment_field, number_of_comments as usize),
    || {
      BlockData::VorbisComment(VorbisComment {
        vendor_string: vendor_string,
        comments: comments,
      })
    }
  )
);

named!(comment_field <&[u8], &str>,
  chain!(
    comment_length: le_u32 ~
    comment: take_str!(comment_length),
    || { comment }
  )
);

named!(cue_sheet <&[u8], BlockData>,
  chain!(
    media_catalog_number: take_str!(128) ~
    lead_in: be_u64 ~
    bytes: take!(259) ~ // TODO: last (7 + 258 * 8) bits must be 0
    num_tracks: be_u8 ~
    tracks: count!(cue_sheet_track, num_tracks as usize),
    || {
      let is_cd = ((bytes[0] >> 7) & 0b01) == 1;

      BlockData::CueSheet(CueSheet {
        media_catalog_number: media_catalog_number,
        lead_in: lead_in,
        is_cd: is_cd,
        tracks: tracks,
      })
    }
  )
);

named!(cue_sheet_track <&[u8], CueSheetTrack>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    isrc: take_str!(12) ~
    bytes: take!(14) ~ // TODO: last (6 + 13 * 8) bits must be 0
    num_indices: be_u8 ~
    indices: cond!(
      num_indices != 0,
      count!(cue_sheet_track_index, num_indices as usize)
    ),
    || {
      let isnt_audio      = ((bytes[0] >> 7) & 0b01) == 1;
      let is_pre_emphasis = ((bytes[0] >> 6) & 0b01) == 1;

      CueSheetTrack {
        offset: offset,
        number: number,
        isrc: isrc,
        isnt_audio: isnt_audio,
        is_pre_emphasis: is_pre_emphasis,
        indices: indices.unwrap_or(Vec::new()),
      }
    }
  )
);

named!(cue_sheet_track_index <&[u8], CueSheetTrackIndex>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    skip_bytes!(3),
    || {
      CueSheetTrackIndex {
        offset: offset,
        number: number,
      }
    }
  )
);

named!(picture <&[u8], BlockData>,
  chain!(
    picture_type_num: be_u32 ~
    mime_type_length:  be_u32 ~
    mime_type: take_str!(mime_type_length) ~
    description_length: be_u32 ~
    description: take_str!(description_length) ~
    width: be_u32 ~
    height: be_u32 ~
    depth: be_u32 ~
    colors: be_u32 ~
    data_length: be_u32 ~
    data: take!(data_length),
    || {
      let picture_type = match picture_type_num {
        0  => PictureType::Other,
        1  => PictureType::FileIconStandard,
        2  => PictureType::FileIcon,
        3  => PictureType::FrontCover,
        4  => PictureType::BackCover,
        5  => PictureType::LeafletPage,
        6  => PictureType::Media,
        7  => PictureType::LeadArtist,
        8  => PictureType::Artist,
        9  => PictureType::Conductor,
        10 => PictureType::Band,
        11 => PictureType::Composer,
        12 => PictureType::Lyricist,
        13 => PictureType::RecordingLocation,
        14 => PictureType::DuringRecording,
        15 => PictureType::DuringPerformace,
        16 => PictureType::VideoScreenCapture,
        17 => PictureType::Fish,
        18 => PictureType::Illustration,
        19 => PictureType::BandLogoType,
        20 => PictureType::PublisherLogoType,
        _  => PictureType::Other,
      };

      BlockData::Picture(Picture {
        picture_type: picture_type,
        mime_type: mime_type,
        description: description,
        width: width,
        height: height,
        depth: depth,
        colors: colors,
        data: data,
      })
    }
  )
);

// As of FLAC v1.3.1, there is support for up to 127 different metadata
// `Block`s but actually 7 that are implemented. When the `Block` type isn't
// recognised, this block gets skipped over with this parser.
fn unknown(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), BlockData::Unknown)
}

named!(pub header <&[u8], (bool, u8, u32)>,
  chain!(
    block_byte: be_u8 ~
    length: map!(take!(3), to_u32),
    || {
      let is_last    = (block_byte >> 7) == 1;
      let block_type = block_byte & 0b01111111;

      (is_last, block_type, length)
    }
  )
);

fn block_data(input: &[u8], block_type: u8, length: u32)
              -> IResult<&[u8], BlockData> {
  match block_type {
    0       => stream_info(input),
    1       => padding(input, length),
    2       => application(input, length),
    3       => seek_table(input, length),
    4       => vorbis_comment(input),
    5       => cue_sheet(input),
    6       => picture(input),
    7...126 => unknown(input, length),
    _       => IResult::Error(Err::Position(ErrorCode::Alt as u32, input)),
  }
}

named!(block <&[u8], Block>,
  chain!(
    block_header: header ~
    data: apply!(block_data, block_header.1, block_header.2),
    || {
      Block {
        is_last: block_header.0,
        length: block_header.2,
        data: data
      }
    }
  )
);

/// Parses one or more metadata `Block`s
///
/// The first block should always be `StreamInfo` since that is the only
/// required `Block`. At the moment `many_blocks` doesn't check that
/// requirement.
pub fn many_blocks(input: &[u8]) -> IResult<&[u8], Vec<Block>> {
  let mut is_last   = false;
  let mut blocks    = Vec::new();
  let mut start     = 0;
  let mut remaining = input.len();

  while !is_last {
    match block(&input[start..]) {
      IResult::Done(i, block) => {
        let result_len = i.len();

        if result_len == input[start..].len() {
          break;
        }

        start    += remaining - result_len;
        remaining = result_len;
        is_last   = block.is_last;

        blocks.push(block);
      }
      _                       => break,
    }
  }

  if blocks.len() == 0 {
    IResult::Error(Err::Position(ErrorCode::Many1 as u32, input))
  } else if is_last {
    IResult::Done(&input[start..], blocks)
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use metadata::{
    BlockData,
    StreamInfo,
  };
  use nom::{
    IResult,
    ErrorCode, Err,
  };

  #[test]
  fn test_header() {
    let inputs = [ [0x80, 0x00, 0x00, 0x22]
                 , [0x01, 0x00, 0x04, 0x00]
                 , [0x84, 0x00, 0x00, 0xf8]
                 ];

    assert!(header(&inputs[0]) == IResult::Done(&[], (true, 0, 34)),
            "Header Test #1");
    assert!(header(&inputs[1]) == IResult::Done(&[], (false, 1, 1024)),
            "Header Test #2");
    assert!(header(&inputs[2]) == IResult::Done(&[], (true, 4, 248)),
            "Header Test #3");
  }

  #[test]
  fn test_stream_info() {
    let input   = [ 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10
                  , 0x01, 0xf4, 0x02, 0x70, 0x00, 0x01, 0x38, 0x80, 0xa0, 0x42
                  , 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b, 0x94, 0xa8
                  , 0x36, 0x08, 0xd1, 0x1a
                  ];

    let md5_sum = [ 0xa0, 0x42, 0x23, 0x7c, 0x54, 0x93, 0xfd, 0xb9, 0x65, 0x6b
                  , 0x94, 0xa8, 0x36, 0x08, 0xd1, 0x1a
                  ];

    let result = BlockData::StreamInfo(StreamInfo {
      min_block_size: 4608,
      max_block_size: 4608,
      min_frame_size: 14,
      max_frame_size: 16,
      sample_rate: 8000,
      channels: 2,
      bits_per_sample: 8,
      total_samples: 80000,
      md5_sum: &md5_sum,
    });

    assert_eq!(stream_info(&input), IResult::Done(&[][..], result));
  }

  #[test]
  fn test_padding() {
    let input = [ [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
                , [0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
                ];

    let result_valid   = IResult::Done(&[][..], BlockData::Padding(0));
    let result_invalid = IResult::Error(Err::Position(
                           ErrorCode::Digit as u32, &input[1][..]));

    assert!(padding(&input[0], 10) == result_valid, "Valid Padding");
    assert!(padding(&input[1], 10) == result_invalid, "Invalid Padding");
  }
}
<|endoftext|>


<commit_before>extern crate mtl;
extern crate cocoa;

use mtl::{CompileOptions, LanguageVersion, SpecificLanguageVersion, Device};
use mtl::{FromRaw, FromRawError, IntoRaw};
use mtl::sys::{MTLCompileOptions, MTLLanguageVersion};
use cocoa::base::{BOOL, nil};
use cocoa::foundation::NSString;

#[test]
fn get_device() {
    let device = Device::system_default_device();
    assert!(device.is_ok());
}

#[test]
fn get_device_name() {
    let device = Device::system_default_device().unwrap();
    assert!(!device.name().into_owned().is_empty());
}

#[test]
fn set_get_command_queue_label() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();

    let dummy_command_queue_name = "foo";
    command_queue.set_label(dummy_command_queue_name);

    assert_eq!(command_queue.get_label().into_owned(),
               dummy_command_queue_name.to_string());
}

#[test]
fn insert_debug_capture_boundary_on_command_queue() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();
    command_queue.insert_debug_capture_boundary();

    // if nothing panics/fails here, everything is okay
}

#[test]
fn device_from_to_raw() {
    let device = Device::system_default_device().unwrap();
    let raw = device.into_raw();
    let device = Device::from_raw(raw);
    assert!(device.is_ok());
}

#[test]
fn device_from_wrong_type() {
    let some_string = unsafe { NSString::alloc(nil).init_str("Hello") };
    let device_result = Device::from_raw(some_string);
    match device_result {
        Result::Err(FromRawError::WrongPointerType) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn device_from_nullptr() {
    let device_result = Device::from_raw(nil);
    match device_result {
        Result::Err(FromRawError::NilPointer) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn test_compile_opts_creation_is_correct() {
    let mut options = CompileOptions::default();

    options.fast_math_enabled = true;
    options.language_version = LanguageVersion::Specific(SpecificLanguageVersion::Version_1_0);

    let native_options = options.mtl_compile_options();

    unsafe {
        assert_eq!(options.fast_math_enabled as BOOL, native_options.fastMathEnabled());
        assert_eq!(native_options.languageVersion(), MTLLanguageVersion::MTLLanguageVersion1_0);
    }
}

#[test]
fn test_device_create_library_with_valid_shader_code() {
    let mut device = Device::system_default_device().unwrap();
    // Shader source taken from http://metalbyexample.com/up-and-running-2/
    let shader = r"
        using namespace metal;

        struct ColoredVertex
        {
            float4 position [[position]];
            float4 color;
        };

        vertex ColoredVertex vertex_main(constant float4 *position [[buffer(0)]],
                                         constant float4 *color [[buffer(1)]],
                                         uint vid [[vertex_id]])
        {
            ColoredVertex vert;
            vert.position = position[vid];
            vert.color = color[vid];
            return vert;
        }

        fragment float4 fragment_main(ColoredVertex vert [[stage_in]])
        {
            return vert.color;
        }
    ";
    let library = device.new_library_with_source(shader, &Default::default());
    assert!(library.is_ok());
}
<commit_msg>Rustfmt, and add test to create an invalid shader<commit_after>extern crate mtl;
extern crate cocoa;

use mtl::{CompileOptions, Device, LanguageVersion, SpecificLanguageVersion};
use mtl::LibraryError;
use mtl::{FromRaw, FromRawError, IntoRaw};
use mtl::sys::{MTLCompileOptions, MTLLanguageVersion};
use cocoa::base::{BOOL, nil};
use cocoa::foundation::NSString;

#[test]
fn get_device() {
    let device = Device::system_default_device();
    assert!(device.is_ok());
}

#[test]
fn get_device_name() {
    let device = Device::system_default_device().unwrap();
    assert!(!device.name().into_owned().is_empty());
}

#[test]
fn set_get_command_queue_label() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();

    let dummy_command_queue_name = "foo";
    command_queue.set_label(dummy_command_queue_name);

    assert_eq!(command_queue.get_label().into_owned(),
               dummy_command_queue_name.to_string());
}

#[test]
fn insert_debug_capture_boundary_on_command_queue() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();
    command_queue.insert_debug_capture_boundary();

    // if nothing panics/fails here, everything is okay
}

#[test]
fn device_from_to_raw() {
    let device = Device::system_default_device().unwrap();
    let raw = device.into_raw();
    let device = Device::from_raw(raw);
    assert!(device.is_ok());
}

#[test]
fn device_from_wrong_type() {
    let some_string = unsafe { NSString::alloc(nil).init_str("Hello") };
    let device_result = Device::from_raw(some_string);
    match device_result {
        Result::Err(FromRawError::WrongPointerType) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn device_from_nullptr() {
    let device_result = Device::from_raw(nil);
    match device_result {
        Result::Err(FromRawError::NilPointer) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn test_compile_opts_creation_is_correct() {
    let mut options = CompileOptions::default();

    options.fast_math_enabled = true;
    options.language_version = LanguageVersion::Specific(SpecificLanguageVersion::Version_1_0);

    let native_options = options.mtl_compile_options();

    unsafe {
        assert_eq!(options.fast_math_enabled as BOOL,
                   native_options.fastMathEnabled());
        assert_eq!(native_options.languageVersion(),
                   MTLLanguageVersion::MTLLanguageVersion1_0);
    }
}

#[test]
fn create_invalid_shader() {
    let mut device = Device::system_default_device().unwrap();
    let bad_shader = r"abcdefghijklmnopqrstuvwxyz";
    match device.new_library_with_source(bad_shader, &Default::default()) {
        Ok(_) => panic!("Incorrect result: expected an error"),
        Err(LibraryError::SourceError) => assert!(true),
        _ => panic!("Incorrect result: unexpected error"),
    }
}

#[test]
fn test_device_create_library_with_valid_shader_code() {
    let mut device = Device::system_default_device().unwrap();
    // Shader source taken from http://metalbyexample.com/up-and-running-2/
    let shader = r"
        using namespace metal;

        struct ColoredVertex
        {
            float4 position [[position]];
            float4 color;
        };

        vertex ColoredVertex vertex_main(constant float4 *position [[buffer(0)]],
                                         constant float4 *color [[buffer(1)]],
                                         uint vid [[vertex_id]])
        {
            ColoredVertex vert;
            vert.position = position[vid];
            vert.color = color[vid];
            return vert;
        }

        fragment float4 fragment_main(ColoredVertex vert [[stage_in]])
        {
            return vert.color;
        }
    ";
    let library = device.new_library_with_source(shader, &Default::default());
    assert!(library.is_ok());
}
<|endoftext|>


<commit_before>#![deny(rust_2018_idioms)]

use proptest::{prelude::*, collection::vec as propvec};
use std::hash::Hasher;
#[cfg(test)]
use twox_hash::{XxHash32, XxHash64};

pub mod c_xxhash;

pub fn hash_once(mut hasher: impl Hasher, data: &[u8]) -> u64 {
    hasher.write(&data);
    hasher.finish()
}

#[cfg(test)]
fn hash_by_chunks(mut hasher: impl Hasher, mut data: &[u8], chunk_sizes: &[usize]) -> u64 {
    for &chunk_size in chunk_sizes {
        let (this_chunk, remaining) = data.split_at(chunk_size);
        hasher.write(this_chunk);
        data = remaining;
    }

    hasher.finish()
}

prop_compose! {
    fn data_and_offset
        ()
        (data in any::<Vec<u8>>())
        (index in 0..=data.len(), data in Just(data))
         -> (Vec<u8>, usize)
    {
        (data, index)
    }
}

prop_compose! {
    fn data_and_chunk_sizes
        ()
        (sizes in propvec(1..=256usize, 0..=100))
        (data in propvec(any::<u8>(), sizes.iter().sum::<usize>()), sizes in Just(sizes))
         -> (Vec<u8>, Vec<usize>)
    {
        (data, sizes)
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(100_000))]

    #[test]
    fn same_results_as_c_for_64_bit(seed: u64, data: Vec<u8>) {
        let our_result = hash_once(XxHash64::with_seed(seed), &data);
        let their_result = c_xxhash::hash64(&data, seed);

        our_result == their_result
    }

   #[test]
    fn same_results_as_c_with_offset_for_64_bit(seed: u64, (data, offset) in data_and_offset()) {
        let data = &data[offset..];
        let our_result = hash_once(XxHash64::with_seed(seed), data);
        let their_result = c_xxhash::hash64(data, seed);

        our_result == their_result
    }

    #[test]
    fn same_results_as_c_for_32_bit(seed: u32, data: Vec<u8>) {
        let our_result = hash_once(XxHash32::with_seed(seed), &data);
        let their_result = c_xxhash::hash32(&data, seed);

        our_result == their_result as u64
    }

   #[test]
    fn same_results_as_c_with_offset_for_32_bit(seed: u32, (data, offset) in data_and_offset()) {
        let data = &data[offset..];
        let our_result = hash_once(XxHash32::with_seed(seed), data);
        let their_result = c_xxhash::hash32(data, seed);

        our_result == their_result as u64
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(1_000))]

    #[test]
    fn same_results_with_many_chunks_as_one_for_64_bit(seed: u64, (data, chunk_sizes) in data_and_chunk_sizes()) {
        let chunked_result = hash_by_chunks(XxHash64::with_seed(seed), &data, &chunk_sizes);
        let monolithic_result = hash_once(XxHash64::with_seed(seed), &data);

        chunked_result == monolithic_result
    }

    #[test]
    fn same_results_with_many_chunks_as_one_for_32_bit(seed: u32, (data, chunk_sizes) in data_and_chunk_sizes()) {
        let chunked_result = hash_by_chunks(XxHash32::with_seed(seed), &data, &chunk_sizes);
        let monolithic_result = hash_once(XxHash32::with_seed(seed), &data);

        chunked_result == monolithic_result
    }
}
<commit_msg>Correctly fail proptest-based tests<commit_after>#![deny(rust_2018_idioms)]

use proptest::{prelude::*, collection::vec as propvec};
use std::hash::Hasher;
#[cfg(test)]
use twox_hash::{XxHash32, XxHash64};

pub mod c_xxhash;

pub fn hash_once(mut hasher: impl Hasher, data: &[u8]) -> u64 {
    hasher.write(&data);
    hasher.finish()
}

#[cfg(test)]
fn hash_by_chunks(mut hasher: impl Hasher, mut data: &[u8], chunk_sizes: &[usize]) -> u64 {
    for &chunk_size in chunk_sizes {
        let (this_chunk, remaining) = data.split_at(chunk_size);
        hasher.write(this_chunk);
        data = remaining;
    }

    hasher.finish()
}

prop_compose! {
    fn data_and_offset
        ()
        (data in any::<Vec<u8>>())
        (index in 0..=data.len(), data in Just(data))
         -> (Vec<u8>, usize)
    {
        (data, index)
    }
}

prop_compose! {
    fn data_and_chunk_sizes
        ()
        (sizes in propvec(1..=256usize, 0..=100))
        (data in propvec(any::<u8>(), sizes.iter().sum::<usize>()), sizes in Just(sizes))
         -> (Vec<u8>, Vec<usize>)
    {
        (data, sizes)
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(100_000))]

    #[test]
    fn same_results_as_c_for_64_bit(seed: u64, data: Vec<u8>) {
        let our_result = hash_once(XxHash64::with_seed(seed), &data);
        let their_result = c_xxhash::hash64(&data, seed);

        prop_assert_eq!(our_result, their_result);
    }

   #[test]
    fn same_results_as_c_with_offset_for_64_bit(seed: u64, (data, offset) in data_and_offset()) {
        let data = &data[offset..];
        let our_result = hash_once(XxHash64::with_seed(seed), data);
        let their_result = c_xxhash::hash64(data, seed);

        prop_assert_eq!(our_result, their_result);
    }

    #[test]
    fn same_results_as_c_for_32_bit(seed: u32, data: Vec<u8>) {
        let our_result = hash_once(XxHash32::with_seed(seed), &data);
        let their_result = c_xxhash::hash32(&data, seed);

        prop_assert_eq!(our_result, their_result as u64);
    }

   #[test]
    fn same_results_as_c_with_offset_for_32_bit(seed: u32, (data, offset) in data_and_offset()) {
        let data = &data[offset..];
        let our_result = hash_once(XxHash32::with_seed(seed), data);
        let their_result = c_xxhash::hash32(data, seed);

        prop_assert_eq!(our_result, their_result as u64);
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(1_000))]

    #[test]
    fn same_results_with_many_chunks_as_one_for_64_bit(seed: u64, (data, chunk_sizes) in data_and_chunk_sizes()) {
        let chunked_result = hash_by_chunks(XxHash64::with_seed(seed), &data, &chunk_sizes);
        let monolithic_result = hash_once(XxHash64::with_seed(seed), &data);

        prop_assert_eq!(chunked_result, monolithic_result);
    }

    #[test]
    fn same_results_with_many_chunks_as_one_for_32_bit(seed: u32, (data, chunk_sizes) in data_and_chunk_sizes()) {
        let chunked_result = hash_by_chunks(XxHash32::with_seed(seed), &data, &chunk_sizes);
        let monolithic_result = hash_once(XxHash32::with_seed(seed), &data);

        prop_assert_eq!(chunked_result, monolithic_result);
    }
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::io::{Seek, Write};
use std::iter::Iterator;
use zip::result::ZipError;
use zip::write::FileOptions;

use std::fs::File;
use std::path::Path;
use walkdir::{DirEntry, WalkDir};

fn main() {
    std::process::exit(real_main());
}

const METHOD_STORED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);

#[cfg(any(
    feature = "deflate",
    feature = "deflate-miniz",
    feature = "deflate-zlib"
))]
const METHOD_DEFLATED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
#[cfg(not(any(
    feature = "deflate",
    feature = "deflate-miniz",
    feature = "deflate-zlib"
)))]
const METHOD_DEFLATED: Option<zip::CompressionMethod> = None;

#[cfg(feature = "bzip2")]
const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
#[cfg(not(feature = "bzip2"))]
const METHOD_BZIP2: Option<zip::CompressionMethod> = None;

fn real_main() -> i32 {
    let args: Vec<_> = std::env::args().collect();
    if args.len() < 3 {
        println!(
            "Usage: {} <source_directory> <destination_zipfile>",
            args[0]
        );
        return 1;
    }

    let src_dir = &*args[1];
    let dst_file = &*args[2];
    for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2].iter() {
        if method.is_none() {
            continue;
        }
        match doit(src_dir, dst_file, method.unwrap()) {
            Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
            Err(e) => println!("Error: {:?}", e),
        }
    }

    return 0;
}

fn zip_dir<T>(
    it: &mut dyn Iterator<Item = DirEntry>,
    prefix: &str,
    writer: T,
    method: zip::CompressionMethod,
) -> zip::result::ZipResult<()>
where
    T: Write + Seek,
{
    let mut zip = zip::ZipWriter::new(writer);
    let options = FileOptions::default()
        .compression_method(method)
        .unix_permissions(0o755);

    let mut buffer = Vec::new();
    for entry in it {
        let path = entry.path();
        let name = path.strip_prefix(Path::new(prefix)).unwrap();

        // Write file or directory explicitly
        // Some unzip tools unzip files with directory paths correctly, some do not!
        if path.is_file() {
            println!("adding file {:?} as {:?} ...", path, name);
            zip.start_file_from_path(name, options)?;
            let mut f = File::open(path)?;

            f.read_to_end(&mut buffer)?;
            zip.write_all(&*buffer)?;
            buffer.clear();
        } else if name.as_os_str().len() != 0 {
            // Only if not root! Avoids path spec / warning
            // and mapname conversion failed error on unzip
            println!("adding dir {:?} as {:?} ...", path, name);
            zip.add_directory_from_path(name, options)?;
        }
    }
    zip.finish()?;
    Result::Ok(())
}

fn doit(
    src_dir: &str,
    dst_file: &str,
    method: zip::CompressionMethod,
) -> zip::result::ZipResult<()> {
    if !Path::new(src_dir).is_dir() {
        return Err(ZipError::FileNotFound);
    }

    let path = Path::new(dst_file);
    let file = File::create(&path).unwrap();

    let walkdir = WalkDir::new(src_dir.to_string());
    let it = walkdir.into_iter();

    zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;

    Ok(())
}
<commit_msg>refactor: use deprecated API in example<commit_after>use std::io::prelude::*;
use std::io::{Seek, Write};
use std::iter::Iterator;
use zip::result::ZipError;
use zip::write::FileOptions;

use std::fs::File;
use std::path::Path;
use walkdir::{DirEntry, WalkDir};

fn main() {
    std::process::exit(real_main());
}

const METHOD_STORED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);

#[cfg(any(
    feature = "deflate",
    feature = "deflate-miniz",
    feature = "deflate-zlib"
))]
const METHOD_DEFLATED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
#[cfg(not(any(
    feature = "deflate",
    feature = "deflate-miniz",
    feature = "deflate-zlib"
)))]
const METHOD_DEFLATED: Option<zip::CompressionMethod> = None;

#[cfg(feature = "bzip2")]
const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
#[cfg(not(feature = "bzip2"))]
const METHOD_BZIP2: Option<zip::CompressionMethod> = None;

fn real_main() -> i32 {
    let args: Vec<_> = std::env::args().collect();
    if args.len() < 3 {
        println!(
            "Usage: {} <source_directory> <destination_zipfile>",
            args[0]
        );
        return 1;
    }

    let src_dir = &*args[1];
    let dst_file = &*args[2];
    for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2].iter() {
        if method.is_none() {
            continue;
        }
        match doit(src_dir, dst_file, method.unwrap()) {
            Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
            Err(e) => println!("Error: {:?}", e),
        }
    }

    return 0;
}

fn zip_dir<T>(
    it: &mut dyn Iterator<Item = DirEntry>,
    prefix: &str,
    writer: T,
    method: zip::CompressionMethod,
) -> zip::result::ZipResult<()>
where
    T: Write + Seek,
{
    let mut zip = zip::ZipWriter::new(writer);
    let options = FileOptions::default()
        .compression_method(method)
        .unix_permissions(0o755);

    let mut buffer = Vec::new();
    for entry in it {
        let path = entry.path();
        let name = path.strip_prefix(Path::new(prefix)).unwrap();

        // Write file or directory explicitly
        // Some unzip tools unzip files with directory paths correctly, some do not!
        if path.is_file() {
            println!("adding file {:?} as {:?} ...", path, name);
            #[allow(deprecated)]
            zip.start_file_from_path(name, options)?;
            let mut f = File::open(path)?;

            f.read_to_end(&mut buffer)?;
            zip.write_all(&*buffer)?;
            buffer.clear();
        } else if name.as_os_str().len() != 0 {
            // Only if not root! Avoids path spec / warning
            // and mapname conversion failed error on unzip
            println!("adding dir {:?} as {:?} ...", path, name);
            #[allow(deprecated)]
            zip.add_directory_from_path(name, options)?;
        }
    }
    zip.finish()?;
    Result::Ok(())
}

fn doit(
    src_dir: &str,
    dst_file: &str,
    method: zip::CompressionMethod,
) -> zip::result::ZipResult<()> {
    if !Path::new(src_dir).is_dir() {
        return Err(ZipError::FileNotFound);
    }

    let path = Path::new(dst_file);
    let file = File::create(&path).unwrap();

    let walkdir = WalkDir::new(src_dir.to_string());
    let it = walkdir.into_iter();

    zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;

    Ok(())
}
<|endoftext|>


<commit_before>use super::{
    heap::{Heap, ObjectData, ObjectPointer},
    value::Value,
};
use crate::compiler::{
    hir,
    lir::{Chunk, ChunkIndex, Instruction},
};
use itertools::Itertools;
use log::{debug, trace};
use std::collections::HashMap;

/// A VM can execute some byte code.
pub struct Vm {
    pub(super) chunks: Vec<Chunk>,
    pub(super) status: Status,
    next_instruction: ByteCodePointer,
    pub(super) heap: Heap,
    pub(super) data_stack: Vec<ObjectPointer>,
    pub(super) function_stack: Vec<ByteCodePointer>,
    pub(super) debug_stack: Vec<DebugEntry>,
}

#[derive(Clone)]
pub enum Status {
    Running,
    Done(Value),
    Panicked(Value),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteCodePointer {
    chunk: ChunkIndex,
    instruction: usize,
}

#[derive(Debug, Clone)]
pub struct DebugEntry {
    pub id: hir::Id,
    pub data_stack: Vec<Value>,
}

impl Vm {
    pub fn new(chunks: Vec<Chunk>) -> Self {
        Self {
            chunks: chunks.clone(),
            status: Status::Running,
            next_instruction: ByteCodePointer {
                chunk: chunks.len() - 1,
                instruction: 0,
            },
            heap: Heap::new(),
            data_stack: vec![],
            function_stack: vec![],
            debug_stack: vec![],
        }
    }

    pub fn status(&self) -> Status {
        self.status.clone()
    }

    fn get_from_data_stack(&self, offset: usize) -> ObjectPointer {
        self.data_stack[self.data_stack.len() - 1 - offset as usize].clone()
    }

    pub fn run(&mut self, mut num_instructions: u16) {
        assert!(
            matches!(self.status, Status::Running),
            "Called Vm::run on a vm that is not ready to run."
        );
        while matches!(self.status, Status::Running) && num_instructions > 0 {
            num_instructions -= 1;
            let instruction = self.chunks[self.next_instruction.chunk].instructions
                [self.next_instruction.instruction]
                .clone();
            trace!("Running instruction: {:?}", &instruction);
            self.next_instruction.instruction += 1;
            self.run_instruction(instruction);

            trace!(
                "Stack: {}",
                self.data_stack
                    .iter()
                    .map(|address| format!("{}", self.heap.export_without_dropping(*address)))
                    .join(", ")
            );
            trace!("Heap: {:?}", self.heap);

            if self.next_instruction.instruction
                >= self.chunks[self.next_instruction.chunk].instructions.len()
            {
                self.status = Status::Done(Value::nothing());
            }
        }
    }
    pub fn run_instruction(&mut self, instruction: Instruction) {
        match instruction {
            Instruction::CreateInt(int) => {
                let address = self.heap.create(ObjectData::Int(int));
                self.data_stack.push(address);
            }
            Instruction::CreateText(text) => {
                let address = self.heap.create(ObjectData::Text(text));
                self.data_stack.push(address);
            }
            Instruction::CreateSymbol(symbol) => {
                let address = self.heap.create(ObjectData::Symbol(symbol));
                self.data_stack.push(address);
            }
            Instruction::CreateStruct { num_entries } => {
                let mut key_value_addresses = vec![];
                for _ in 0..(2 * num_entries) {
                    key_value_addresses.push(self.data_stack.pop().unwrap());
                }
                let mut entries = HashMap::new();
                for mut key_and_value in &key_value_addresses.into_iter().rev().chunks(2) {
                    let key = key_and_value.next().unwrap();
                    let value = key_and_value.next().unwrap();
                    assert_eq!(key_and_value.next(), None);
                    entries.insert(key, value);
                }
                let address = self.heap.create(ObjectData::Struct(entries));
                self.data_stack.push(address);
            }
            Instruction::CreateClosure(chunk) => {
                let captured = self.data_stack.clone();
                for address in &captured {
                    self.heap.dup(*address);
                }
                let address = self.heap.create(ObjectData::Closure {
                    captured,
                    body: chunk,
                });
                self.data_stack.push(address);
            }
            Instruction::CreateBuiltin(builtin) => {
                let address = self.heap.create(ObjectData::Builtin(builtin));
                self.data_stack.push(address);
            }
            Instruction::PopMultipleBelowTop(n) => {
                let top = self.data_stack.pop().unwrap();
                for _ in 0..n {
                    let address = self.data_stack.pop().unwrap();
                    self.heap.drop(address);
                }
                self.data_stack.push(top);
            }
            Instruction::PushFromStack(offset) => {
                let address = self.get_from_data_stack(offset);
                self.heap.dup(address);
                self.data_stack.push(address);
            }
            Instruction::Call { num_args } => {
                let closure_address = self.data_stack.pop().unwrap();
                let mut args = vec![];
                for _ in 0..num_args {
                    args.push(self.data_stack.pop().unwrap());
                }
                args.reverse();

                match self.heap.get(closure_address).data.clone() {
                    ObjectData::Closure { captured, body } => {
                        let expected_num_args = self.chunks[body].num_args;
                        if num_args != expected_num_args {
                            self.panic(format!("Closure expects {} parameters, but you called it with {} arguments.", expected_num_args, num_args));
                            return;
                        }

                        self.function_stack.push(self.next_instruction);
                        self.data_stack.append(&mut captured.clone());
                        for captured in captured {
                            self.heap.dup(captured);
                        }
                        self.data_stack.append(&mut args);
                        self.heap.drop(closure_address);
                        self.next_instruction = ByteCodePointer {
                            chunk: body,
                            instruction: 0,
                        };
                    }
                    ObjectData::Builtin(builtin) => {
                        self.run_builtin_function(&builtin, &args);
                    }
                    _ => panic!("Can only call closures and builtins."),
                };
            }
            Instruction::Return => {
                let caller = self.function_stack.pop().unwrap();
                self.next_instruction = caller;
            }
            Instruction::DebugValueEvaluated(id) => {
                let address = *self.data_stack.last().unwrap();
                let value = self.heap.export_without_dropping(address);
                debug!("{} = {}", id, value);
            }
            Instruction::DebugClosureEntered(id) => {
                let mut stack = vec![];
                for entry in &self.data_stack {
                    stack.push(self.heap.export_without_dropping(*entry));
                }

                self.debug_stack.push(DebugEntry {
                    id,
                    data_stack: stack,
                });
            }
            Instruction::DebugClosureExited => {
                let entry = self.debug_stack.pop().unwrap();
                trace!("Exited closure {}.", entry.id);
                trace!(
                    "Stack before: {:?}",
                    entry
                        .data_stack
                        .iter()
                        .map(|value| format!("{}", value))
                        .join(", ")
                );
                trace!(
                    "Stack after:  {:?}",
                    self.data_stack
                        .iter()
                        .map(|address| format!("{}", self.heap.export_without_dropping(*address)))
                        .join(", ")
                );
            }
            Instruction::Error(_) => {
                self.panic(
                    "The VM crashed because there was an error in previous compilation stages."
                        .to_string(),
                );
            }
        }
    }

    pub fn panic(&mut self, message: String) -> Value {
        self.status = Status::Panicked(Value::Text(message));
        Value::Symbol("Never".to_string())
    }

    pub fn current_stack_trace(&self) -> Vec<DebugEntry> {
        self.debug_stack.clone()
    }
}
<commit_msg>Make error more helpful<commit_after>use super::{
    heap::{Heap, ObjectData, ObjectPointer},
    value::Value,
};
use crate::compiler::{
    hir,
    lir::{Chunk, ChunkIndex, Instruction},
};
use itertools::Itertools;
use log::{debug, trace};
use std::collections::HashMap;

/// A VM can execute some byte code.
pub struct Vm {
    pub(super) chunks: Vec<Chunk>,
    pub(super) status: Status,
    next_instruction: ByteCodePointer,
    pub(super) heap: Heap,
    pub(super) data_stack: Vec<ObjectPointer>,
    pub(super) function_stack: Vec<ByteCodePointer>,
    pub(super) debug_stack: Vec<DebugEntry>,
}

#[derive(Clone)]
pub enum Status {
    Running,
    Done(Value),
    Panicked(Value),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteCodePointer {
    chunk: ChunkIndex,
    instruction: usize,
}

#[derive(Debug, Clone)]
pub struct DebugEntry {
    pub id: hir::Id,
    pub data_stack: Vec<Value>,
}

impl Vm {
    pub fn new(chunks: Vec<Chunk>) -> Self {
        Self {
            chunks: chunks.clone(),
            status: Status::Running,
            next_instruction: ByteCodePointer {
                chunk: chunks.len() - 1,
                instruction: 0,
            },
            heap: Heap::new(),
            data_stack: vec![],
            function_stack: vec![],
            debug_stack: vec![],
        }
    }

    pub fn status(&self) -> Status {
        self.status.clone()
    }

    fn get_from_data_stack(&self, offset: usize) -> ObjectPointer {
        self.data_stack[self.data_stack.len() - 1 - offset as usize].clone()
    }

    pub fn run(&mut self, mut num_instructions: u16) {
        assert!(
            matches!(self.status, Status::Running),
            "Called Vm::run on a vm that is not ready to run."
        );
        while matches!(self.status, Status::Running) && num_instructions > 0 {
            num_instructions -= 1;
            let instruction = self.chunks[self.next_instruction.chunk].instructions
                [self.next_instruction.instruction]
                .clone();
            trace!("Running instruction: {:?}", &instruction);
            self.next_instruction.instruction += 1;
            self.run_instruction(instruction);

            trace!(
                "Stack: {}",
                self.data_stack
                    .iter()
                    .map(|address| format!("{}", self.heap.export_without_dropping(*address)))
                    .join(", ")
            );
            trace!("Heap: {:?}", self.heap);

            if self.next_instruction.instruction
                >= self.chunks[self.next_instruction.chunk].instructions.len()
            {
                self.status = Status::Done(Value::nothing());
            }
        }
    }
    pub fn run_instruction(&mut self, instruction: Instruction) {
        match instruction {
            Instruction::CreateInt(int) => {
                let address = self.heap.create(ObjectData::Int(int));
                self.data_stack.push(address);
            }
            Instruction::CreateText(text) => {
                let address = self.heap.create(ObjectData::Text(text));
                self.data_stack.push(address);
            }
            Instruction::CreateSymbol(symbol) => {
                let address = self.heap.create(ObjectData::Symbol(symbol));
                self.data_stack.push(address);
            }
            Instruction::CreateStruct { num_entries } => {
                let mut key_value_addresses = vec![];
                for _ in 0..(2 * num_entries) {
                    key_value_addresses.push(self.data_stack.pop().unwrap());
                }
                let mut entries = HashMap::new();
                for mut key_and_value in &key_value_addresses.into_iter().rev().chunks(2) {
                    let key = key_and_value.next().unwrap();
                    let value = key_and_value.next().unwrap();
                    assert_eq!(key_and_value.next(), None);
                    entries.insert(key, value);
                }
                let address = self.heap.create(ObjectData::Struct(entries));
                self.data_stack.push(address);
            }
            Instruction::CreateClosure(chunk) => {
                let captured = self.data_stack.clone();
                for address in &captured {
                    self.heap.dup(*address);
                }
                let address = self.heap.create(ObjectData::Closure {
                    captured,
                    body: chunk,
                });
                self.data_stack.push(address);
            }
            Instruction::CreateBuiltin(builtin) => {
                let address = self.heap.create(ObjectData::Builtin(builtin));
                self.data_stack.push(address);
            }
            Instruction::PopMultipleBelowTop(n) => {
                let top = self.data_stack.pop().unwrap();
                for _ in 0..n {
                    let address = self.data_stack.pop().unwrap();
                    self.heap.drop(address);
                }
                self.data_stack.push(top);
            }
            Instruction::PushFromStack(offset) => {
                let address = self.get_from_data_stack(offset);
                self.heap.dup(address);
                self.data_stack.push(address);
            }
            Instruction::Call { num_args } => {
                let closure_address = self.data_stack.pop().unwrap();
                let mut args = vec![];
                for _ in 0..num_args {
                    args.push(self.data_stack.pop().unwrap());
                }
                args.reverse();

                match self.heap.get(closure_address).data.clone() {
                    ObjectData::Closure { captured, body } => {
                        let expected_num_args = self.chunks[body].num_args;
                        if num_args != expected_num_args {
                            self.panic(format!("Closure expects {} parameters, but you called it with {} arguments.", expected_num_args, num_args));
                            return;
                        }

                        self.function_stack.push(self.next_instruction);
                        self.data_stack.append(&mut captured.clone());
                        for captured in captured {
                            self.heap.dup(captured);
                        }
                        self.data_stack.append(&mut args);
                        self.heap.drop(closure_address);
                        self.next_instruction = ByteCodePointer {
                            chunk: body,
                            instruction: 0,
                        };
                    }
                    ObjectData::Builtin(builtin) => {
                        self.run_builtin_function(&builtin, &args);
                    }
                    _ => panic!("Can only call closures and builtins."),
                };
            }
            Instruction::Return => {
                let caller = self.function_stack.pop().unwrap();
                self.next_instruction = caller;
            }
            Instruction::DebugValueEvaluated(id) => {
                let address = *self.data_stack.last().unwrap();
                let value = self.heap.export_without_dropping(address);
                debug!("{} = {}", id, value);
            }
            Instruction::DebugClosureEntered(id) => {
                let mut stack = vec![];
                for entry in &self.data_stack {
                    stack.push(self.heap.export_without_dropping(*entry));
                }

                self.debug_stack.push(DebugEntry {
                    id,
                    data_stack: stack,
                });
            }
            Instruction::DebugClosureExited => {
                let entry = self.debug_stack.pop().unwrap();
                trace!("Exited closure {}.", entry.id);
                trace!(
                    "Stack before: {:?}",
                    entry
                        .data_stack
                        .iter()
                        .map(|value| format!("{}", value))
                        .join(", ")
                );
                trace!(
                    "Stack after:  {:?}",
                    self.data_stack
                        .iter()
                        .map(|address| format!("{}", self.heap.export_without_dropping(*address)))
                        .join(", ")
                );
            }
            Instruction::Error(error) => {
                self.panic(
                    format!("The VM crashed because there was an error in previous compilation stages: {:?}", error),
                );
            }
        }
    }

    pub fn panic(&mut self, message: String) -> Value {
        self.status = Status::Panicked(Value::Text(message));
        Value::Symbol("Never".to_string())
    }

    pub fn current_stack_trace(&self) -> Vec<DebugEntry> {
        self.debug_stack.clone()
    }
}
<|endoftext|>


<commit_before>use chrono::*;
use std::io::prelude::*;
use render::Renderer;
use result;

pub struct GraphiteRenderer<'a> {
    time: DateTime<UTC>,
    prefix: Option<String>,
    stream: &'a mut Write,
}

impl<'a> GraphiteRenderer<'a> {
    pub fn new(time: DateTime<UTC>,
               prefix: Option<String>,
               stream: &'a mut Write)
               -> GraphiteRenderer<'a> {
        GraphiteRenderer {
            time: time,
            prefix: prefix,
            stream: stream,
        }
    }
}

impl<'a> Renderer for GraphiteRenderer<'a> {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        let prefix_text: String;
        let prefix_separator: &str;

        match self.prefix {
            Some(ref p) => {
                prefix_text = p.clone();
                prefix_separator = ".";
            }
            None => {
                prefix_text = String::from("");
                prefix_separator = "";
            }
        };

        let mut write = |text: String| {
            let _ = self.stream.write(format!("{}{}{} {}\n",
                                              prefix_text,
                                              prefix_separator,
                                              text,
                                              self.time.timestamp())
                .as_bytes());
        };

        write(format!("requests.count {}", result.count));

        match result.timing {
            Some(timing) => {
                write(format!("requests.time.max {}", timing.max));
                write(format!("requests.time.min {}", timing.min));
                write(format!("requests.time.avg {}", timing.avg));
                write(format!("requests.time.median {}", timing.median));
                write(format!("requests.time.90percent {}", timing.percentile90));
            }
            None => warn!("No matching log lines in file."),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::prelude::*;
    use std::str;
    use chrono::*;
    use timing_analyzer;
    use error_analyzer;
    use super::*;

    struct MockTcpStream {
        write_calls: Vec<String>,
    }

    impl Write for MockTcpStream {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
            Ok(1)
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    fn get_result_fixture() -> result::RequestLogAnalyzerResult {
        result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(timing_analyzer::TimingResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            }),
            error: Some(error_analyzer::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        }
    }

    fn get_time_fixture() -> DateTime<UTC> {
        let time: DateTime<UTC> = DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200",
                                                           "%d/%b/%Y:%H:%M:%S %z")
            .unwrap()
            .with_timezone(&UTC);

        time
    }

    #[test]
    fn test_render_graphite() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer =
                GraphiteRenderer::new(get_time_fixture(), None, &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "requests.time.90percent 100 1474576919\n");
    }

    #[test]
    fn test_render_graphite_with_prefix() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer = GraphiteRenderer::new(get_time_fixture(),
                                                     Some(String::from("my_prefix")),
                                                     &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "my_prefix.requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "my_prefix.requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "my_prefix.requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "my_prefix.requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "my_prefix.requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "my_prefix.requests.time.90percent 100 1474576919\n");
    }
}
<commit_msg>Render new error metrics via Graphite<commit_after>use chrono::*;
use std::io::prelude::*;
use render::Renderer;
use result;

pub struct GraphiteRenderer<'a> {
    time: DateTime<UTC>,
    prefix: Option<String>,
    stream: &'a mut Write,
}

impl<'a> GraphiteRenderer<'a> {
    pub fn new(time: DateTime<UTC>,
               prefix: Option<String>,
               stream: &'a mut Write)
               -> GraphiteRenderer<'a> {
        GraphiteRenderer {
            time: time,
            prefix: prefix,
            stream: stream,
        }
    }
}

impl<'a> Renderer for GraphiteRenderer<'a> {
    fn render(&mut self, result: result::RequestLogAnalyzerResult) -> () {
        let prefix_text: String;
        let prefix_separator: &str;

        match self.prefix {
            Some(ref p) => {
                prefix_text = p.clone();
                prefix_separator = ".";
            }
            None => {
                prefix_text = String::from("");
                prefix_separator = "";
            }
        };

        let mut write = |text: String| {
            let _ = self.stream.write(format!("{}{}{} {}\n",
                                              prefix_text,
                                              prefix_separator,
                                              text,
                                              self.time.timestamp())
                .as_bytes());
        };

        write(format!("requests.count {}", result.count));

        match result.timing {
            Some(timing) => {
                write(format!("requests.time.max {}", timing.max));
                write(format!("requests.time.min {}", timing.min));
                write(format!("requests.time.avg {}", timing.avg));
                write(format!("requests.time.median {}", timing.median));
                write(format!("requests.time.90percent {}", timing.percentile90));
            }
            None => warn!("No matching log lines in file."),
        }

        match result.error {
            Some(error) => {
                write(format!("requests.error.client_error_4xx_rate {}",
                              error.client_error_4xx));
                write(format!("requests.error.server_error_5xx_rate {}",
                              error.server_error_5xx));
            }
            None => warn!("No matching log lines in file."),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::prelude::*;
    use std::str;
    use chrono::*;
    use timing_analyzer;
    use error_analyzer;
    use super::*;

    struct MockTcpStream {
        write_calls: Vec<String>,
    }

    impl Write for MockTcpStream {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.write_calls.push(str::from_utf8(buf).unwrap().to_string());
            Ok(1)
        }

        fn flush(&mut self) -> io::Result<()> {
            Ok(())
        }
    }

    fn get_result_fixture() -> result::RequestLogAnalyzerResult {
        result::RequestLogAnalyzerResult {
            count: 3,
            timing: Some(timing_analyzer::TimingResult {
                max: 100,
                min: 1,
                avg: 37,
                median: 10,
                percentile90: 100,
            }),
            error: Some(error_analyzer::ErrorRatesResult {
                client_error_4xx: 0.1,
                server_error_5xx: 0.2,
            }),
        }
    }

    fn get_time_fixture() -> DateTime<UTC> {
        let time: DateTime<UTC> = DateTime::parse_from_str("22/Sep/2016:22:41:59 +0200",
                                                           "%d/%b/%Y:%H:%M:%S %z")
            .unwrap()
            .with_timezone(&UTC);

        time
    }

    #[test]
    fn test_render_graphite() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer =
                GraphiteRenderer::new(get_time_fixture(), None, &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "requests.time.90percent 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[6],
                   "requests.error.client_error_4xx_rate 0.1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[7],
                   "requests.error.server_error_5xx_rate 0.2 1474576919\n");
    }

    #[test]
    fn test_render_graphite_with_prefix() {
        let mut mock_tcp_stream = MockTcpStream { write_calls: vec![] };

        {
            let mut renderer = GraphiteRenderer::new(get_time_fixture(),
                                                     Some(String::from("my_prefix")),
                                                     &mut mock_tcp_stream);
            renderer.render(get_result_fixture());
        }

        assert_eq!(&mock_tcp_stream.write_calls[0],
                   "my_prefix.requests.count 3 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[1],
                   "my_prefix.requests.time.max 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[2],
                   "my_prefix.requests.time.min 1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[3],
                   "my_prefix.requests.time.avg 37 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[4],
                   "my_prefix.requests.time.median 10 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[5],
                   "my_prefix.requests.time.90percent 100 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[6],
                   "my_prefix.requests.error.client_error_4xx_rate 0.1 1474576919\n");
        assert_eq!(&mock_tcp_stream.write_calls[7],
                   "my_prefix.requests.error.server_error_5xx_rate 0.2 1474576919\n");
    }
}
<|endoftext|>


<commit_before>use crate::error::Error;
use clap::ArgMatches;
use std::{collections::HashMap,
          fmt,
          io,
          net::{IpAddr,
                Ipv4Addr,
                SocketAddr,
                SocketAddrV4,
                ToSocketAddrs},
          ops::{Deref,
                DerefMut},
          option,
          result,
          str::FromStr};

/// Bundles up information about the user and group that a supervised
/// service should be run as. If the Supervisor itself is running with
/// root-like permissions, then these will be for `SVC_USER` and
/// `SVC_GROUP` for a service. If not, it will be for the user the
/// Supervisor itself is running as.
///
/// On Windows, all but `username` will be `None`. On Linux,
/// `username` and `groupname` may legitimately be `None`, but `uid`
/// and `gid` should always be `Some`.
#[derive(Debug, Default)]
pub struct UserInfo {
    /// Windows required, Linux optional
    pub username: Option<String>,
    /// Linux preferred
    pub uid: Option<u32>,
    /// Linux optional
    pub groupname: Option<String>,
    /// Linux preferred
    pub gid: Option<u32>,
}

/// Captures arbitrary key-value pair metadata to attach to all events
/// generated by the Supervisor.
#[derive(Clone, Debug, Default)]
pub struct EventStreamMetadata(HashMap<String, String>);

impl Into<HashMap<String, String>> for EventStreamMetadata {
    fn into(self) -> HashMap<String, String> { self.0 }
}

impl EventStreamMetadata {
    /// The name of the Clap argument we'll use for arguments of this type.
    pub const ARG_NAME: &'static str = "EVENT_STREAM_METADATA";

    /// Ensure that user input from Clap can be converted into a
    /// key-value pair we can consume.
    ///
    /// Note: this validates each value given by the user, not all the
    /// values given at once.
    #[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
    pub fn validate(value: String) -> result::Result<(), String> {
        Self::split_raw(&value).map(|_| ())
    }

    /// Utility function to create a key-value pair tuple from a
    /// user-provided value in Clap.
    fn split_raw(raw: &str) -> result::Result<(String, String), String> {
        match raw.split('=').collect::<Vec<_>>().as_slice() {
            [key, value] if !key.is_empty() && !value.is_empty() => {
                Ok((key.to_string(), value.to_string()))
            }
            _ => {
                Err(format!("Invalid key-value pair given (must be \
                             '='-delimited pair of non-empty strings): {}",
                            raw))
            }
        }
    }

    /// Same as `split_raw`, but for running on already-validated
    /// input (thus, this function cannot fail).
    fn split_validated(validated_input: &str) -> (String, String) {
        Self::split_raw(validated_input).expect("EVENT_STREAM_METADATA should be validated at \
                                                 this point")
    }
}

impl<'a> From<&'a ArgMatches<'a>> for EventStreamMetadata {
    /// Create an instance of `EventStreamMetadata` from validated
    /// user input.
    fn from(m: &ArgMatches) -> Self {
        let raw_meta = m.values_of(Self::ARG_NAME).unwrap_or_default();
        Self(raw_meta.map(Self::split_validated).collect())
    }
}

/// This represents an environment variable that holds an authentication token which enables
/// integration with Automate. Supervisors use this token to connect to the messaging server
/// on the Automate side in order to send data about the services they're running via event
/// messages. If the environment variable is present, its value is the auth token. If it's not
/// present and the feature flag for the Event Stream is enabled, initialization of the Event
/// Stream will fail.
#[derive(Clone, Debug)]
pub struct AutomateAuthToken(String);

impl AutomateAuthToken {
    /// The name of the Clap argument we'll use for arguments of this type.
    pub const ARG_NAME: &'static str = "EVENT_STREAM_TOKEN";
    // Ideally, we'd like to take advantage of
    // `habitat_core::env::Config` trait, but that currently requires
    // a `Default` implementation, and there isn't really a legitimate
    // default value right now.
    pub const ENVVAR: &'static str = "HAB_AUTOMATE_AUTH_TOKEN";
}

impl AutomateAuthToken {
    /// Ensure that user input from Clap can be converted an instance
    /// of a token.
    #[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
    pub fn validate(value: String) -> result::Result<(), String> {
        value.parse::<Self>().map(|_| ()).map_err(|e| e.to_string())
    }
}

impl<'a> From<&'a ArgMatches<'a>> for AutomateAuthToken {
    /// Create an instance of `AutomateAuthToken` from validated
    /// user input.
    fn from(m: &ArgMatches) -> Self {
        m.value_of(Self::ARG_NAME)
         .expect("HAB_AUTOMATE_AUTH_TOKEN should be set")
         .parse()
         .expect("HAB_AUTOMATE_AUTH_TOKEN should be validated at this point")
    }
}

impl FromStr for AutomateAuthToken {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        if s.is_empty() {
            Err(Error::InvalidEventStreamToken(s.to_string()))
        } else {
            Ok(AutomateAuthToken(s.to_string()))
        }
    }
}

impl fmt::Display for AutomateAuthToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
}

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

    mod auth_token {
        use super::*;

        #[test]
        fn cannot_parse_from_empty_string() { assert!("".parse::<AutomateAuthToken>().is_err()) }

    }
}

habitat_core::env_config_socketaddr!(#[derive(Clone, Copy, PartialEq, Eq, Debug)],
                                     pub GossipListenAddr,
                                     HAB_LISTEN_GOSSIP,
                                     0, 0, 0, 0, Self::DEFAULT_PORT);

impl GossipListenAddr {
    pub const DEFAULT_PORT: u16 = 9638;

    /// When local gossip mode is used we still startup the gossip layer but set
    /// it to listen on 127.0.0.2 to scope it to the local node but ignore connections from
    /// 127.0.0.1. This turns out to be much simpler than the cascade of changes that would
    /// be involved in not setting up a gossip listener at all.
    pub fn local_only() -> Self {
        GossipListenAddr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 2),
                                                          Self::DEFAULT_PORT)))
    }

    /// Generate an address at which a server configured with this
    /// GossipListenAddr can communicate with itself.
    ///
    /// In particular, a server configured to listen on `0.0.0.0` vs
    /// `192.168.1.1` should be contacted via `127.0.0.1` in the
    /// former case, but `192.168.1.1` in the latter.
    pub fn local_addr(&self) -> Self {
        let mut addr = *self;
        if addr.0.ip().is_unspecified() {
            // TODO (CM): Use Ipv4Addr::loopback() when it's no longer experimental
            // TODO (CM): Support IPV6, once we do that more broadly
            addr.0.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
        }
        addr
    }
}

impl Deref for GossipListenAddr {
    type Target = SocketAddr;

    fn deref(&self) -> &SocketAddr { &self.0 }
}

impl DerefMut for GossipListenAddr {
    fn deref_mut(&mut self) -> &mut SocketAddr { &mut self.0 }
}

impl ToSocketAddrs for GossipListenAddr {
    type Iter = option::IntoIter<SocketAddr>;

    fn to_socket_addrs(&self) -> io::Result<Self::Iter> { self.0.to_socket_addrs() }
}

habitat_core::env_config_socketaddr!(#[derive(PartialEq, Eq, Debug, Clone, Copy)],
                                     pub HttpListenAddr,
                                     HAB_LISTEN_HTTP,
                                     0, 0, 0, 0, 9631);
impl HttpListenAddr {
    pub fn new(ip: IpAddr, port: u16) -> Self { Self(SocketAddr::new(ip, port)) }
}

impl Deref for HttpListenAddr {
    type Target = SocketAddr;

    fn deref(&self) -> &SocketAddr { &self.0 }
}

impl DerefMut for HttpListenAddr {
    fn deref_mut(&mut self) -> &mut SocketAddr { &mut self.0 }
}

impl ToSocketAddrs for HttpListenAddr {
    type Iter = option::IntoIter<SocketAddr>;

    fn to_socket_addrs(&self) -> io::Result<Self::Iter> { self.0.to_socket_addrs() }
}

habitat_core::env_config_socketaddr!(#[derive(Clone, Copy, PartialEq, Eq, Debug)],
                                     pub ListenCtlAddr,
                                     HAB_LISTEN_CTL,
                                     Ipv4Addr::LOCALHOST, Self::DEFAULT_PORT);

impl ListenCtlAddr {
    pub const DEFAULT_PORT: u16 = 9632;

    pub fn new(ip: Ipv4Addr, port: u16) -> Self {
        ListenCtlAddr(SocketAddr::V4(SocketAddrV4::new(ip, port)))
    }

    pub fn ip(&self) -> IpAddr { self.0.ip() }

    pub fn port(&self) -> u16 { self.0.port() }
}

impl AsRef<SocketAddr> for ListenCtlAddr {
    fn as_ref(&self) -> &SocketAddr { &self.0 }
}

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

    mod gossip_listen_addr {
        use super::*;
        #[test]
        fn local_addr_for_gossip_listen_addr_works_for_unspecified_address() {
            let listen_addr = GossipListenAddr::default();
            assert!(listen_addr.0.ip().is_unspecified());

            let local_addr = listen_addr.local_addr();
            assert!(local_addr.0.ip().is_loopback());
        }

        #[test]
        fn local_addr_for_gossip_listen_addr_returns_same_ip_for_a_specified_address() {
            let mut listen_addr = GossipListenAddr::default();
            listen_addr.0
                       .set_ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
            assert!(!listen_addr.0.ip().is_loopback());

            let local_addr = listen_addr.local_addr();
            assert_eq!(listen_addr, local_addr);
        }
    }

    mod env_config {
        use habitat_core::env::Config as EnvConfig;
        use std::{env,
                  num::ParseIntError,
                  result,
                  str::FromStr};

        #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq)]
        struct Thingie(u64);

        impl Default for Thingie {
            fn default() -> Self { Thingie(2112) }
        }

        impl FromStr for Thingie {
            type Err = ParseIntError;

            fn from_str(s: &str) -> result::Result<Self, Self::Err> {
                let raw = s.parse::<u64>()?;
                Ok(Thingie(raw))
            }
        }

        crate::locked_env_var!(HAB_TESTING_THINGIE, lock_hab_testing_thingie);

        impl EnvConfig for Thingie {
            const ENVVAR: &'static str = "HAB_TESTING_THINGIE";
        }

        #[test]
        fn no_env_var_yields_default() {
            let _envvar = lock_hab_testing_thingie();
            assert!(env::var("HAB_TESTING_THINGIE").is_err()); // it's not set
            assert_eq!(Thingie::configured_value(), Thingie(2112));
            assert_eq!(Thingie::configured_value(), Thingie::default());
        }

        #[test]
        fn parsable_env_var_yields_parsed_value() {
            let envvar = lock_hab_testing_thingie();
            envvar.set("123");
            assert_eq!(Thingie::configured_value(), Thingie(123));
            assert_ne!(Thingie::configured_value(), Thingie::default());
        }

        #[test]
        fn unparsable_env_var_yields_default() {
            let envvar = lock_hab_testing_thingie();
            envvar.set("I'm not a number");
            assert_eq!(Thingie::configured_value(), Thingie::default());
        }
    }
}
<commit_msg>Consolidate test modules<commit_after>use crate::error::Error;
use clap::ArgMatches;
use std::{collections::HashMap,
          fmt,
          io,
          net::{IpAddr,
                Ipv4Addr,
                SocketAddr,
                SocketAddrV4,
                ToSocketAddrs},
          ops::{Deref,
                DerefMut},
          option,
          result,
          str::FromStr};

/// Bundles up information about the user and group that a supervised
/// service should be run as. If the Supervisor itself is running with
/// root-like permissions, then these will be for `SVC_USER` and
/// `SVC_GROUP` for a service. If not, it will be for the user the
/// Supervisor itself is running as.
///
/// On Windows, all but `username` will be `None`. On Linux,
/// `username` and `groupname` may legitimately be `None`, but `uid`
/// and `gid` should always be `Some`.
#[derive(Debug, Default)]
pub struct UserInfo {
    /// Windows required, Linux optional
    pub username: Option<String>,
    /// Linux preferred
    pub uid: Option<u32>,
    /// Linux optional
    pub groupname: Option<String>,
    /// Linux preferred
    pub gid: Option<u32>,
}

/// Captures arbitrary key-value pair metadata to attach to all events
/// generated by the Supervisor.
#[derive(Clone, Debug, Default)]
pub struct EventStreamMetadata(HashMap<String, String>);

impl Into<HashMap<String, String>> for EventStreamMetadata {
    fn into(self) -> HashMap<String, String> { self.0 }
}

impl EventStreamMetadata {
    /// The name of the Clap argument we'll use for arguments of this type.
    pub const ARG_NAME: &'static str = "EVENT_STREAM_METADATA";

    /// Ensure that user input from Clap can be converted into a
    /// key-value pair we can consume.
    ///
    /// Note: this validates each value given by the user, not all the
    /// values given at once.
    #[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
    pub fn validate(value: String) -> result::Result<(), String> {
        Self::split_raw(&value).map(|_| ())
    }

    /// Utility function to create a key-value pair tuple from a
    /// user-provided value in Clap.
    fn split_raw(raw: &str) -> result::Result<(String, String), String> {
        match raw.split('=').collect::<Vec<_>>().as_slice() {
            [key, value] if !key.is_empty() && !value.is_empty() => {
                Ok((key.to_string(), value.to_string()))
            }
            _ => {
                Err(format!("Invalid key-value pair given (must be \
                             '='-delimited pair of non-empty strings): {}",
                            raw))
            }
        }
    }

    /// Same as `split_raw`, but for running on already-validated
    /// input (thus, this function cannot fail).
    fn split_validated(validated_input: &str) -> (String, String) {
        Self::split_raw(validated_input).expect("EVENT_STREAM_METADATA should be validated at \
                                                 this point")
    }
}

impl<'a> From<&'a ArgMatches<'a>> for EventStreamMetadata {
    /// Create an instance of `EventStreamMetadata` from validated
    /// user input.
    fn from(m: &ArgMatches) -> Self {
        let raw_meta = m.values_of(Self::ARG_NAME).unwrap_or_default();
        Self(raw_meta.map(Self::split_validated).collect())
    }
}

/// This represents an environment variable that holds an authentication token which enables
/// integration with Automate. Supervisors use this token to connect to the messaging server
/// on the Automate side in order to send data about the services they're running via event
/// messages. If the environment variable is present, its value is the auth token. If it's not
/// present and the feature flag for the Event Stream is enabled, initialization of the Event
/// Stream will fail.
#[derive(Clone, Debug)]
pub struct AutomateAuthToken(String);

impl AutomateAuthToken {
    /// The name of the Clap argument we'll use for arguments of this type.
    pub const ARG_NAME: &'static str = "EVENT_STREAM_TOKEN";
    // Ideally, we'd like to take advantage of
    // `habitat_core::env::Config` trait, but that currently requires
    // a `Default` implementation, and there isn't really a legitimate
    // default value right now.
    pub const ENVVAR: &'static str = "HAB_AUTOMATE_AUTH_TOKEN";
}

impl AutomateAuthToken {
    /// Ensure that user input from Clap can be converted an instance
    /// of a token.
    #[allow(clippy::needless_pass_by_value)] // Signature required by CLAP
    pub fn validate(value: String) -> result::Result<(), String> {
        value.parse::<Self>().map(|_| ()).map_err(|e| e.to_string())
    }
}

impl<'a> From<&'a ArgMatches<'a>> for AutomateAuthToken {
    /// Create an instance of `AutomateAuthToken` from validated
    /// user input.
    fn from(m: &ArgMatches) -> Self {
        m.value_of(Self::ARG_NAME)
         .expect("HAB_AUTOMATE_AUTH_TOKEN should be set")
         .parse()
         .expect("HAB_AUTOMATE_AUTH_TOKEN should be validated at this point")
    }
}

impl FromStr for AutomateAuthToken {
    type Err = Error;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        if s.is_empty() {
            Err(Error::InvalidEventStreamToken(s.to_string()))
        } else {
            Ok(AutomateAuthToken(s.to_string()))
        }
    }
}

impl fmt::Display for AutomateAuthToken {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
}

habitat_core::env_config_socketaddr!(#[derive(Clone, Copy, PartialEq, Eq, Debug)],
                                     pub GossipListenAddr,
                                     HAB_LISTEN_GOSSIP,
                                     0, 0, 0, 0, Self::DEFAULT_PORT);

impl GossipListenAddr {
    pub const DEFAULT_PORT: u16 = 9638;

    /// When local gossip mode is used we still startup the gossip layer but set
    /// it to listen on 127.0.0.2 to scope it to the local node but ignore connections from
    /// 127.0.0.1. This turns out to be much simpler than the cascade of changes that would
    /// be involved in not setting up a gossip listener at all.
    pub fn local_only() -> Self {
        GossipListenAddr(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 2),
                                                          Self::DEFAULT_PORT)))
    }

    /// Generate an address at which a server configured with this
    /// GossipListenAddr can communicate with itself.
    ///
    /// In particular, a server configured to listen on `0.0.0.0` vs
    /// `192.168.1.1` should be contacted via `127.0.0.1` in the
    /// former case, but `192.168.1.1` in the latter.
    pub fn local_addr(&self) -> Self {
        let mut addr = *self;
        if addr.0.ip().is_unspecified() {
            // TODO (CM): Use Ipv4Addr::loopback() when it's no longer experimental
            // TODO (CM): Support IPV6, once we do that more broadly
            addr.0.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
        }
        addr
    }
}

impl Deref for GossipListenAddr {
    type Target = SocketAddr;

    fn deref(&self) -> &SocketAddr { &self.0 }
}

impl DerefMut for GossipListenAddr {
    fn deref_mut(&mut self) -> &mut SocketAddr { &mut self.0 }
}

impl ToSocketAddrs for GossipListenAddr {
    type Iter = option::IntoIter<SocketAddr>;

    fn to_socket_addrs(&self) -> io::Result<Self::Iter> { self.0.to_socket_addrs() }
}

habitat_core::env_config_socketaddr!(#[derive(PartialEq, Eq, Debug, Clone, Copy)],
                                     pub HttpListenAddr,
                                     HAB_LISTEN_HTTP,
                                     0, 0, 0, 0, 9631);
impl HttpListenAddr {
    pub fn new(ip: IpAddr, port: u16) -> Self { Self(SocketAddr::new(ip, port)) }
}

impl Deref for HttpListenAddr {
    type Target = SocketAddr;

    fn deref(&self) -> &SocketAddr { &self.0 }
}

impl DerefMut for HttpListenAddr {
    fn deref_mut(&mut self) -> &mut SocketAddr { &mut self.0 }
}

impl ToSocketAddrs for HttpListenAddr {
    type Iter = option::IntoIter<SocketAddr>;

    fn to_socket_addrs(&self) -> io::Result<Self::Iter> { self.0.to_socket_addrs() }
}

habitat_core::env_config_socketaddr!(#[derive(Clone, Copy, PartialEq, Eq, Debug)],
                                     pub ListenCtlAddr,
                                     HAB_LISTEN_CTL,
                                     Ipv4Addr::LOCALHOST, Self::DEFAULT_PORT);

impl ListenCtlAddr {
    pub const DEFAULT_PORT: u16 = 9632;

    pub fn new(ip: Ipv4Addr, port: u16) -> Self {
        ListenCtlAddr(SocketAddr::V4(SocketAddrV4::new(ip, port)))
    }

    pub fn ip(&self) -> IpAddr { self.0.ip() }

    pub fn port(&self) -> u16 { self.0.port() }
}

impl AsRef<SocketAddr> for ListenCtlAddr {
    fn as_ref(&self) -> &SocketAddr { &self.0 }
}

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

    mod auth_token {
        use super::*;

        #[test]
        fn cannot_parse_from_empty_string() { assert!("".parse::<AutomateAuthToken>().is_err()) }

    }

    mod gossip_listen_addr {
        use super::*;
        #[test]
        fn local_addr_for_gossip_listen_addr_works_for_unspecified_address() {
            let listen_addr = GossipListenAddr::default();
            assert!(listen_addr.0.ip().is_unspecified());

            let local_addr = listen_addr.local_addr();
            assert!(local_addr.0.ip().is_loopback());
        }

        #[test]
        fn local_addr_for_gossip_listen_addr_returns_same_ip_for_a_specified_address() {
            let mut listen_addr = GossipListenAddr::default();
            listen_addr.0
                       .set_ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
            assert!(!listen_addr.0.ip().is_loopback());

            let local_addr = listen_addr.local_addr();
            assert_eq!(listen_addr, local_addr);
        }
    }

    mod env_config {
        use habitat_core::env::Config as EnvConfig;
        use std::{env,
                  num::ParseIntError,
                  result,
                  str::FromStr};

        #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq)]
        struct Thingie(u64);

        impl Default for Thingie {
            fn default() -> Self { Thingie(2112) }
        }

        impl FromStr for Thingie {
            type Err = ParseIntError;

            fn from_str(s: &str) -> result::Result<Self, Self::Err> {
                let raw = s.parse::<u64>()?;
                Ok(Thingie(raw))
            }
        }

        crate::locked_env_var!(HAB_TESTING_THINGIE, lock_hab_testing_thingie);

        impl EnvConfig for Thingie {
            const ENVVAR: &'static str = "HAB_TESTING_THINGIE";
        }

        #[test]
        fn no_env_var_yields_default() {
            let _envvar = lock_hab_testing_thingie();
            assert!(env::var("HAB_TESTING_THINGIE").is_err()); // it's not set
            assert_eq!(Thingie::configured_value(), Thingie(2112));
            assert_eq!(Thingie::configured_value(), Thingie::default());
        }

        #[test]
        fn parsable_env_var_yields_parsed_value() {
            let envvar = lock_hab_testing_thingie();
            envvar.set("123");
            assert_eq!(Thingie::configured_value(), Thingie(123));
            assert_ne!(Thingie::configured_value(), Thingie::default());
        }

        #[test]
        fn unparsable_env_var_yields_default() {
            let envvar = lock_hab_testing_thingie();
            envvar.set("I'm not a number");
            assert_eq!(Thingie::configured_value(), Thingie::default());
        }
    }
}
<|endoftext|>