<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::sleep;
use std::time::Duration;

use lazy_static::lazy_static;
use libc;

use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::DeviceInfo;

// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
const GPIO_MEM_REGISTERS: usize = 41;
const GPIO_MEM_SIZE: usize = GPIO_MEM_REGISTERS * std::mem::size_of::<u32>();

const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();

// Keep track of locks across multiple GpioMem instances. This is necessary
// in situations where a pin is kept around after Gpio goes out of scope, and
// then another Gpio is created from which additional pins are retrieved. At
// some point all Gpio/GpioMem static variables should probably be combined into
// a single static struct.
lazy_static! {
    static ref GPIOMEM_LOCKS: [AtomicBool; GPIO_MEM_REGISTERS] =
        init_array!(AtomicBool::new(false), GPIO_MEM_REGISTERS);
}

pub struct GpioMem {
    mem_ptr: *mut u32,
}

impl fmt::Debug for GpioMem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("GpioMem")
            .field("mem_ptr", &self.mem_ptr)
            .finish()
    }
}

impl GpioMem {
    pub fn open() -> Result<GpioMem> {
        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        let mem_ptr = match Self::map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match Self::map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    return Err(Error::PermissionDenied)
                }
                Err(Error::UnknownSoC) => return Err(Error::UnknownSoC),
                _ => return Err(gpiomem_err),
            },
        };

        Ok(GpioMem { mem_ptr })
    }

    fn map_devgpiomem() -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/gpiomem")?;

        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };

        if gpiomem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(gpiomem_ptr as *mut u32)
    }

    fn map_devmem() -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownSoC)?;

        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/mem")?;

        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as libc::off_t,
            )
        };

        if mem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(mem_ptr as *mut u32)
    }

    fn read(&self, offset: usize) -> u32 {
        loop {
            if !GPIOMEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        let reg_value = unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) };

        GPIOMEM_LOCKS[offset].store(false, Ordering::SeqCst);

        reg_value
    }

    fn write(&self, offset: usize, value: u32) {
        loop {
            if !GPIOMEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }

        GPIOMEM_LOCKS[offset].store(false, Ordering::SeqCst);
    }

    pub fn set_high(&self, pin: u8) {
        let offset = GPSET0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }

    pub fn set_low(&self, pin: u8) {
        let offset = GPCLR0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }

    pub fn level(&self, pin: u8) -> Level {
        let offset = GPLEV0 + pin as usize / 32;
        let shift = pin % 32;

        let reg_value = self.read(offset);

        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
    }

    pub fn mode(&self, pin: u8) -> Mode {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;

        let reg_value = self.read(offset);

        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
    }

    pub fn set_mode(&self, pin: u8, mode: Mode) {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;

        loop {
            if !GPIOMEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        unsafe {
            let mem_ptr = self.mem_ptr.add(offset);
            let reg_value = ptr::read_volatile(mem_ptr);
            ptr::write_volatile(
                mem_ptr,
                (reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
            );
        }

        GPIOMEM_LOCKS[offset].store(false, Ordering::SeqCst);
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    pub fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
        let offset = GPPUDCLK0 + pin as usize / 32;
        let shift = pin % 32;

        loop {
            if !GPIOMEM_LOCKS[GPPUD].compare_and_swap(false, true, Ordering::SeqCst) {
                if !GPIOMEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                    break;
                } else {
                    GPIOMEM_LOCKS[GPPUD].store(false, Ordering::SeqCst);
                }
            }
        }

        // Set the control signal in GPPUD, while leaving the other 30
        // bits unchanged.
        unsafe {
            let mem_ptr = self.mem_ptr.add(GPPUD);
            let reg_value = ptr::read_volatile(mem_ptr);
            ptr::write_volatile(mem_ptr, (reg_value & !0b11) | ((pud as u32) & 0b11));
        }

        // Set-up time for the control signal.
        sleep(Duration::new(0, 20000)); // >= 20µs

        // Clock the control signal into the selected pin.
        unsafe {
            let mem_ptr = self.mem_ptr.add(offset);
            ptr::write_volatile(mem_ptr, 1 << shift);
        }

        // Hold time for the control signal.
        sleep(Duration::new(0, 20000)); // >= 20µs

        // Remove the control signal and clock.
        unsafe {
            let mem_ptr = self.mem_ptr.add(GPPUD);
            let reg_value = ptr::read_volatile(mem_ptr);
            ptr::write_volatile(mem_ptr, reg_value & !0b11);
        }

        unsafe {
            let mem_ptr = self.mem_ptr.add(offset);
            ptr::write_volatile(mem_ptr, 0 << shift);
        }

        GPIOMEM_LOCKS[offset].store(false, Ordering::SeqCst);
        GPIOMEM_LOCKS[GPPUD].store(false, Ordering::SeqCst);
    }
}

impl Drop for GpioMem {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(
                self.mem_ptr as *mut libc::c_void,
                GPIO_MEM_SIZE as libc::size_t,
            );
        }
    }
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}
unsafe impl Sync for GpioMem {}
<commit_msg>Rename GPIOMEM_LOCKS to GPIO_MEM_LOCKS<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::sleep;
use std::time::Duration;

use lazy_static::lazy_static;
use libc;

use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::DeviceInfo;

// The BCM2835 has 41 32-bit registers related to the GPIO (datasheet @ 6.1).
const GPIO_MEM_REGISTERS: usize = 41;
const GPIO_MEM_SIZE: usize = GPIO_MEM_REGISTERS * std::mem::size_of::<u32>();

const GPFSEL0: usize = 0x00;
const GPSET0: usize = 0x1c / std::mem::size_of::<u32>();
const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();

// Keep track of locks across multiple GpioMem instances. This is necessary
// in situations where a pin is kept around after Gpio goes out of scope, and
// then another Gpio is created from which additional pins are retrieved. At
// some point all Gpio/GpioMem static variables should probably be combined into
// a single static struct.
lazy_static! {
    static ref GPIO_MEM_LOCKS: [AtomicBool; GPIO_MEM_REGISTERS] =
        init_array!(AtomicBool::new(false), GPIO_MEM_REGISTERS);
}

pub struct GpioMem {
    mem_ptr: *mut u32,
}

impl fmt::Debug for GpioMem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("GpioMem")
            .field("mem_ptr", &self.mem_ptr)
            .finish()
    }
}

impl GpioMem {
    pub fn open() -> Result<GpioMem> {
        // Try /dev/gpiomem first. If that fails, try /dev/mem instead. If neither works,
        // report back the error that's the most relevant.
        let mem_ptr = match Self::map_devgpiomem() {
            Ok(ptr) => ptr,
            Err(gpiomem_err) => match Self::map_devmem() {
                Ok(ptr) => ptr,
                Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::PermissionDenied => {
                    return Err(Error::PermissionDenied)
                }
                Err(Error::UnknownSoC) => return Err(Error::UnknownSoC),
                _ => return Err(gpiomem_err),
            },
        };

        Ok(GpioMem { mem_ptr })
    }

    fn map_devgpiomem() -> Result<*mut u32> {
        // Open /dev/gpiomem with read/write/sync flags. This might fail if
        // /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
        // doesn't have the appropriate permissions, or the current user is
        // not a member of the gpio group.
        let gpiomem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/gpiomem")?;

        // Memory-map /dev/gpiomem at offset 0
        let gpiomem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                gpiomem_file.as_raw_fd(),
                0,
            )
        };

        if gpiomem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(gpiomem_ptr as *mut u32)
    }

    fn map_devmem() -> Result<*mut u32> {
        // Identify which SoC we're using, so we know what offset to start at
        let device_info = DeviceInfo::new().map_err(|_| Error::UnknownSoC)?;

        let mem_file = OpenOptions::new()
            .read(true)
            .write(true)
            .custom_flags(libc::O_SYNC)
            .open("/dev/mem")?;

        // Memory-map /dev/mem at the appropriate offset for our SoC
        let mem_ptr = unsafe {
            libc::mmap(
                ptr::null_mut(),
                GPIO_MEM_SIZE,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                mem_file.as_raw_fd(),
                (device_info.peripheral_base() + device_info.gpio_offset()) as libc::off_t,
            )
        };

        if mem_ptr == libc::MAP_FAILED {
            return Err(Error::Io(io::Error::last_os_error()));
        }

        Ok(mem_ptr as *mut u32)
    }

    fn read(&self, offset: usize) -> u32 {
        loop {
            if !GPIO_MEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        let reg_value = unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) };

        GPIO_MEM_LOCKS[offset].store(false, Ordering::SeqCst);

        reg_value
    }

    fn write(&self, offset: usize, value: u32) {
        loop {
            if !GPIO_MEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        unsafe {
            ptr::write_volatile(self.mem_ptr.add(offset), value);
        }

        GPIO_MEM_LOCKS[offset].store(false, Ordering::SeqCst);
    }

    pub fn set_high(&self, pin: u8) {
        let offset = GPSET0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }

    pub fn set_low(&self, pin: u8) {
        let offset = GPCLR0 + pin as usize / 32;
        let shift = pin % 32;
        self.write(offset, 1 << shift);
    }

    pub fn level(&self, pin: u8) -> Level {
        let offset = GPLEV0 + pin as usize / 32;
        let shift = pin % 32;

        let reg_value = self.read(offset);

        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
    }

    pub fn mode(&self, pin: u8) -> Mode {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;

        let reg_value = self.read(offset);

        unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
    }

    pub fn set_mode(&self, pin: u8, mode: Mode) {
        let offset = GPFSEL0 + pin as usize / 10;
        let shift = (pin % 10) * 3;

        loop {
            if !GPIO_MEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                break;
            }
        }

        unsafe {
            let mem_ptr = self.mem_ptr.add(offset);
            let reg_value = ptr::read_volatile(mem_ptr);
            ptr::write_volatile(
                mem_ptr,
                (reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
            );
        }

        GPIO_MEM_LOCKS[offset].store(false, Ordering::SeqCst);
    }

    /// Configures the built-in GPIO pull-up/pull-down resistors.
    pub fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
        let offset = GPPUDCLK0 + pin as usize / 32;
        let shift = pin % 32;

        loop {
            if !GPIO_MEM_LOCKS[GPPUD].compare_and_swap(false, true, Ordering::SeqCst) {
                if !GPIO_MEM_LOCKS[offset].compare_and_swap(false, true, Ordering::SeqCst) {
                    break;
                } else {
                    GPIO_MEM_LOCKS[GPPUD].store(false, Ordering::SeqCst);
                }
            }
        }

        // Set the control signal in GPPUD, while leaving the other 30
        // bits unchanged.
        unsafe {
            let mem_ptr = self.mem_ptr.add(GPPUD);
            let reg_value = ptr::read_volatile(mem_ptr);
            ptr::write_volatile(mem_ptr, (reg_value & !0b11) | ((pud as u32) & 0b11));
        }

        // Set-up time for the control signal.
        sleep(Duration::new(0, 20000)); // >= 20µs

        // Clock the control signal into the selected pin.
        unsafe {
            let mem_ptr = self.mem_ptr.add(offset);
            ptr::write_volatile(mem_ptr, 1 << shift);
        }

        // Hold time for the control signal.
        sleep(Duration::new(0, 20000)); // >= 20µs

        // Remove the control signal and clock.
        unsafe {
            let mem_ptr = self.mem_ptr.add(GPPUD);
            let reg_value = ptr::read_volatile(mem_ptr);
            ptr::write_volatile(mem_ptr, reg_value & !0b11);
        }

        unsafe {
            let mem_ptr = self.mem_ptr.add(offset);
            ptr::write_volatile(mem_ptr, 0 << shift);
        }

        GPIO_MEM_LOCKS[offset].store(false, Ordering::SeqCst);
        GPIO_MEM_LOCKS[GPPUD].store(false, Ordering::SeqCst);
    }
}

impl Drop for GpioMem {
    fn drop(&mut self) {
        unsafe {
            libc::munmap(
                self.mem_ptr as *mut libc::c_void,
                GPIO_MEM_SIZE as libc::size_t,
            );
        }
    }
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}
unsafe impl Sync for GpioMem {}
<|endoftext|>


<commit_before>
use rayon::par_iter::*;
use self::page::{Page};
use super::{ReportMemory, PAGE_SIZE, PAGE_WIDTH};

pub use self::page::{Cell, CellType};

mod cell;
mod page;
mod zorder;

pub struct Grid {
    pages: Vec<Page>,
    dimension: u32,
    pages_per_side: u32
}

impl ReportMemory for Grid {
    fn memory(&self) -> u32 {
        self.pages.into_par_iter()
            .map(|page| page.memory())
            .sum()
    }
}

impl Grid {
    pub fn new(size: u32, density: f32, seed: &[usize]) -> Grid {
        //todo assert size
        let num_pages = size * size;

        info!("Creating grid with {} pages per side ({} pages total), each with {} cells ({} total cells)",
            size, num_pages, PAGE_SIZE, num_pages * PAGE_SIZE);

        let mut pages = Vec::with_capacity(num_pages as usize);
        for i in 0..num_pages {
            let offset_x = (i as u32 % size) * PAGE_WIDTH;
            let offset_y = (i as u32 / size) * PAGE_WIDTH;
            debug!("Offsets: ({},{})", offset_x, offset_y);
            pages.push(Page::new(density, offset_x, offset_y, seed));
        }

        Grid {
            pages: pages,
            dimension: size * PAGE_WIDTH,
            pages_per_side: size
        }
    }

    pub fn grow(&mut self) {
        loop {
            let active_cells = self.grow_step();

            if active_cells == 0 {
                break;
            }
        }
    }

    pub fn grow_step(&mut self) -> u32 {
        debug!("Growing Pages...");

        self.pages.par_iter_mut()
            .for_each(|page| page.grow());;

        let active_cells = self.pages.iter()
                            .map(|page| page.get_active_cell_count())
                            .fold(0u32, |acc, x| acc + x);

        for i in (0..self.pages.len()) {
            let changes = self.pages[i].get_remote_changes().clone();

            if changes.is_empty() {
                continue;
            }

            debug!("Remote changes to process: {}", changes.len());

            for c in changes {
                debug!("Absolute change position: ({},{})", c.x, c.y);
                if !(c.x > 0 && c.x < self.dimension && c.y > 0 && c.y < self.dimension ) {
                    debug!("x > 1 {}", c.x > 0);
                    debug!("x < dimension - 1{}", c.x < self.dimension);
                    debug!("y > 1 {}", c.y > 0);
                    debug!("y < dimension - 1 {}", c.y < self.dimension );

                    continue;
                }
                self.get_mut_page(c.x, c.y)
                    .add_change(c.x % PAGE_WIDTH, c.y % PAGE_WIDTH, c.cell, c.travel_direction, c.stim);
            }

        }

        debug!("Active cells after growth: {}", active_cells);

        debug!("Updating Pages...");
        self.pages.par_iter_mut()
            .for_each(|page| page.update());

        active_cells
    }

    pub fn signal(&mut self) {
        loop {
            let active_cells = self.signal_step();

            if active_cells == 0 {
                break;
            }
        }
    }

    pub fn signal_step(&mut self) -> u32 {
        debug!("Processing signals...");

        let remote_signal = self.pages.par_iter_mut()
            .for_each(|page| page.signal());

        for i in 0..self.pages.len() {

            let signals = self.pages[i].get_remote_signal().clone();

            if signals.is_empty() {
                continue;
            }

            debug!("Remote signal to process: {}", signals.len());

            for s in signals {
                debug!("Absolute signal position: ({},{})", s.x, s.y);
                if !(s.x > 0 && s.x < self.dimension && s.y > 0 && s.y < self.dimension ) {
                    debug!("x > 1 {}", s.x > 0);
                    debug!("x < dimension - 1{}", s.x < self.dimension);
                    debug!("y > 1 {}", s.y > 0);
                    debug!("y < dimension - 1 {}", s.y < self.dimension );

                    continue;
                }
                self.get_mut_page(s.x, s.y)
                    .add_signal(s.x % PAGE_WIDTH, s.y % PAGE_WIDTH, s.strength, s.stim);
            }
        }

        debug!("Updating Pages...");
        self.pages.par_iter_mut()
            .map(|page| page.update_signal())
            .sum()
    }

    fn get_mut_page(&mut self, x: u32, y: u32) -> &mut Page {
        let i = x / PAGE_WIDTH + ((y / PAGE_WIDTH) * self.pages_per_side);
        debug!("get_mut_page: ({},{}) -> {}", x, y, i);
        &mut self.pages[i as usize]
    }

    pub fn get_cell(&self, x: u32, y: u32) -> &Cell {
        let i = x / PAGE_WIDTH + ((y / PAGE_WIDTH) * self.pages_per_side);
        self.pages[i as usize].get_cell(x % PAGE_WIDTH, y % PAGE_WIDTH)
    }

    fn get_mut_cell(&mut self, x: u32, y: u32) -> &mut Cell {
        let i = x / PAGE_WIDTH + ((y / PAGE_WIDTH) * self.pages_per_side);
        self.pages[i as usize].get_mut_cell(x % PAGE_WIDTH, y % PAGE_WIDTH)
    }

    pub fn set_input(&mut self, x: u32, y: u32, sig: u8) {
        self.get_mut_page(x, y).set_input(x % PAGE_WIDTH, y % PAGE_WIDTH, sig);
    }
}

impl Default for Grid {
    fn default() -> Grid {
        Grid::new(10, 0.05, &[1, 2, 3, 4])
    }
}

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

    #[test]
    fn grid_default_params() {
        let _ = Grid::default();
    }
}
<commit_msg>Weight tasks to max so that all cores are utilized<commit_after>
use rayon::par_iter::*;
use self::page::{Page};
use super::{ReportMemory, PAGE_SIZE, PAGE_WIDTH};

pub use self::page::{Cell, CellType};

mod cell;
mod page;
mod zorder;

pub struct Grid {
    pages: Vec<Page>,
    dimension: u32,
    pages_per_side: u32
}

impl ReportMemory for Grid {
    fn memory(&self) -> u32 {
        self.pages.into_par_iter()
            .map(|page| page.memory())
            .sum()
    }
}

impl Grid {
    pub fn new(size: u32, density: f32, seed: &[usize]) -> Grid {
        //todo assert size
        let num_pages = size * size;

        info!("Creating grid with {} pages per side ({} pages total), each with {} cells ({} total cells)",
            size, num_pages, PAGE_SIZE, num_pages * PAGE_SIZE);

        let mut pages = Vec::with_capacity(num_pages as usize);
        for i in 0..num_pages {
            let offset_x = (i as u32 % size) * PAGE_WIDTH;
            let offset_y = (i as u32 / size) * PAGE_WIDTH;
            debug!("Offsets: ({},{})", offset_x, offset_y);
            pages.push(Page::new(density, offset_x, offset_y, seed));
        }

        Grid {
            pages: pages,
            dimension: size * PAGE_WIDTH,
            pages_per_side: size
        }
    }

    pub fn grow(&mut self) {
        loop {
            let active_cells = self.grow_step();

            if active_cells == 0 {
                break;
            }
        }
    }

    pub fn grow_step(&mut self) -> u32 {
        debug!("Growing Pages...");

        self.pages.par_iter_mut()
            .weight_max()
            .for_each(|page| page.grow());;

        let active_cells = self.pages.iter()
                            .map(|page| page.get_active_cell_count())
                            .fold(0u32, |acc, x| acc + x);

        for i in (0..self.pages.len()) {
            let changes = self.pages[i].get_remote_changes().clone();

            if changes.is_empty() {
                continue;
            }

            debug!("Remote changes to process: {}", changes.len());

            for c in changes {
                debug!("Absolute change position: ({},{})", c.x, c.y);
                if !(c.x > 0 && c.x < self.dimension && c.y > 0 && c.y < self.dimension ) {
                    debug!("x > 1 {}", c.x > 0);
                    debug!("x < dimension - 1{}", c.x < self.dimension);
                    debug!("y > 1 {}", c.y > 0);
                    debug!("y < dimension - 1 {}", c.y < self.dimension );

                    continue;
                }
                self.get_mut_page(c.x, c.y)
                    .add_change(c.x % PAGE_WIDTH, c.y % PAGE_WIDTH, c.cell, c.travel_direction, c.stim);
            }

        }

        debug!("Active cells after growth: {}", active_cells);

        debug!("Updating Pages...");
        self.pages.par_iter_mut()
            .weight_max()
            .for_each(|page| page.update());

        active_cells
    }

    pub fn signal(&mut self) {
        loop {
            let active_cells = self.signal_step();

            if active_cells == 0 {
                break;
            }
        }
    }

    pub fn signal_step(&mut self) -> u32 {
        debug!("Processing signals...");

        let remote_signal = self.pages.par_iter_mut()
            .weight_max()
            .for_each(|page| page.signal());

        for i in 0..self.pages.len() {

            let signals = self.pages[i].get_remote_signal().clone();

            if signals.is_empty() {
                continue;
            }

            debug!("Remote signal to process: {}", signals.len());

            for s in signals {
                debug!("Absolute signal position: ({},{})", s.x, s.y);
                if !(s.x > 0 && s.x < self.dimension && s.y > 0 && s.y < self.dimension ) {
                    debug!("x > 1 {}", s.x > 0);
                    debug!("x < dimension - 1{}", s.x < self.dimension);
                    debug!("y > 1 {}", s.y > 0);
                    debug!("y < dimension - 1 {}", s.y < self.dimension );

                    continue;
                }
                self.get_mut_page(s.x, s.y)
                    .add_signal(s.x % PAGE_WIDTH, s.y % PAGE_WIDTH, s.strength, s.stim);
            }
        }

        debug!("Updating Pages...");
        self.pages.par_iter_mut()
            .weight_max()
            .map(|page| page.update_signal())
            .sum()
    }

    fn get_mut_page(&mut self, x: u32, y: u32) -> &mut Page {
        let i = x / PAGE_WIDTH + ((y / PAGE_WIDTH) * self.pages_per_side);
        debug!("get_mut_page: ({},{}) -> {}", x, y, i);
        &mut self.pages[i as usize]
    }

    pub fn get_cell(&self, x: u32, y: u32) -> &Cell {
        let i = x / PAGE_WIDTH + ((y / PAGE_WIDTH) * self.pages_per_side);
        self.pages[i as usize].get_cell(x % PAGE_WIDTH, y % PAGE_WIDTH)
    }

    fn get_mut_cell(&mut self, x: u32, y: u32) -> &mut Cell {
        let i = x / PAGE_WIDTH + ((y / PAGE_WIDTH) * self.pages_per_side);
        self.pages[i as usize].get_mut_cell(x % PAGE_WIDTH, y % PAGE_WIDTH)
    }

    pub fn set_input(&mut self, x: u32, y: u32, sig: u8) {
        self.get_mut_page(x, y).set_input(x % PAGE_WIDTH, y % PAGE_WIDTH, sig);
    }
}

impl Default for Grid {
    fn default() -> Grid {
        Grid::new(10, 0.05, &[1, 2, 3, 4])
    }
}

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

    #[test]
    fn grid_default_params() {
        let _ = Grid::default();
    }
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]
extern crate script;
extern crate collections;
use script::lexer::Lexer;
use script::parser::Parser;
use script::exec::{Executor, Interpreter};
use script::ast::{Token, TComment};
use script::js::value::{Value, ResultValue, to_value, from_value};
use collections::treemap::TreeMap;
use std::path::posix::Path;
use std::io::fs::{File, walk_dir};
use std::io::BufferedReader;
fn find_attrs(tokens: Vec<Token>) -> TreeMap<~str, ~str> {
	let mut map = TreeMap::new();
	for tk in tokens.iter() {
		match tk.data {
			TComment(ref comm) => {
				let comm = comm.as_slice();
				for (_, to) in comm.match_indices("// @") {
					let current = comm.slice_from(to);
					let space_ind = current.find(' ').unwrap();
					let key = comm.slice_to(space_ind);
					let value = comm.slice_chars(space_ind, current.find('\n').unwrap());
					map.insert(key.into_owned(), value.into_owned());
				}
			},
			_ => ()
		}
	}
	map
}
fn assert(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let val : bool = from_value(*args.get(0)).unwrap();
	if val {
		Ok(to_value(true))
	} else {
		Err(to_value(false))
	}
}
fn main() {
	for file in walk_dir(&Path::new("../tests/")).unwrap() {
		if file.is_file() && file.extension_str() == Some("js") {
			let file_str = file.as_str();
			let mut lexer = Lexer::new(BufferedReader::new(File::open(&file).unwrap()));
			lexer.lex().unwrap();
			let mut parser = Parser::new(lexer.tokens);
			let expr = match parser.parse_all() {
				Ok(v) => v,
				Err(v) => fail!("{}: {}", file_str, v)
			};
			let mut engine : Interpreter = Executor::new();
			engine.set_global("assert".into_maybe_owned(), to_value(assert));
			let result : Result<Value, Value> = engine.run(&expr);
			match result {
				Ok(v) => println!("{}: {}", file_str, v.borrow()),
				Err(v) => fail!("{}: Failed with {}", file_str, v.borrow())
			}
		}
	}
}<commit_msg>Fix find_attrs and use it in the main function<commit_after>#![crate_type = "bin"]
extern crate script;
extern crate collections;
use script::lexer::Lexer;
use script::parser::Parser;
use script::exec::{Executor, Interpreter};
use script::ast::{Token, TComment};
use script::js::value::{Value, ResultValue, to_value, from_value};
use collections::treemap::TreeMap;
use std::path::posix::Path;
use std::io::fs::{File, walk_dir};
use std::io::BufferedReader;
fn find_attrs(tokens: Vec<Token>) -> TreeMap<~str, ~str> {
	let mut map = TreeMap::new();
	for tk in tokens.iter() {
		match tk.data {
			TComment(ref comm) => {
				let current = comm.as_slice();
				if current.starts_with(" @") {
					let space_ind = current.slice_from(1).find(' ').unwrap() + 1;
					let key = comm.slice_chars(2, space_ind);
					let value = comm.slice_from(space_ind);
					map.insert(key.into_owned(), value.into_owned());
				}
			},
			_ => ()
		}
	}
	map
}
fn assert(_:Value, _:Value, args:Vec<Value>) -> ResultValue {
	let val : bool = from_value(*args.get(0)).unwrap();
	if val {
		Ok(to_value(true))
	} else {
		Err(to_value(false))
	}
}
fn main() {
	for file in walk_dir(&Path::new("../tests/")).unwrap() {
		if file.is_file() && file.extension_str() == Some("js") {
			let file_str = file.as_str().unwrap();
			let mut lexer = Lexer::new(BufferedReader::new(File::open(&file).unwrap()));
			lexer.lex().unwrap();
			let attributes = find_attrs(lexer.tokens.clone());
			let description = match attributes.find(&"description".into_owned()) {
				Some(desc) => desc,
				None => fail!("{} does not have @description metadata", file_str)
			};
			let mut parser = Parser::new(lexer.tokens);
			let expr = match parser.parse_all() {
				Ok(v) => v,
				Err(v) => fail!("{}: {}", file_str, v)
			};
			let mut engine : Interpreter = Executor::new();
			engine.set_global("assert".into_maybe_owned(), to_value(assert));
			let result : Result<Value, Value> = engine.run(&expr);
			match result {
				Ok(_) => println!("All tests passed: {} - {}", file_str, description),
				Err(v) => fail!("Test failed: {} - {}: {}", file_str, description, v.borrow())
			}
		}
	}
}<|endoftext|>


<commit_before>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! The host wrapper for communicating with a managed host.
//!
//! # Examples
//!
//! ```no_run
//! # use inapi::{Command, Host};
#![cfg_attr(feature = "local-run", doc = "let path: Option<String> = None;")]
#![cfg_attr(feature = "local-run", doc = "let mut host = Host::local(path).unwrap();")]
#![cfg_attr(feature = "remote-run", doc = "let mut host = Host::connect(\"nodes/mynode.json\").unwrap();")]
//!
//! let cmd = Command::new("whoami");
//! let result = cmd.exec(&mut host).unwrap();
//! ```

#[macro_use]
pub mod data;
pub mod ffi;
pub mod telemetry;

pub use self::telemetry::TelemetryTarget;

#[cfg(feature = "remote-run")]
use czmq::{ZCert, ZMsg, ZSock, ZSockType};
#[cfg(feature = "remote-run")]
use error::Error;
use error::Result;
#[cfg(feature = "remote-run")]
use serde_json;
use serde_json::Value;
#[cfg(feature = "remote-run")]
use std::mem;
use std::path::Path;
use std::rc::Rc;
#[cfg(feature = "remote-run")]
use zfilexfer;

#[cfg(feature = "local-run")]
/// Representation of a managed host.
pub struct Host {
    /// Data for host, comprising data files and telemetry
    data: Rc<Value>,
}

#[cfg(feature = "remote-run")]
/// Representation of a managed host.
pub struct Host {
    /// Hostname or IP of managed host
    pub hostname: String,
    /// API socket
    api_sock: Option<ZSock>,
    /// File transfer socket
    file_sock: Option<ZSock>,
    /// Data for host, comprising data files and telemetry
    data: Rc<Value>,
}

impl Host {
    #[cfg(feature = "local-run")]
    /// Create a new Host connected to localhost.
    pub fn local<P: AsRef<Path>>(path: Option<P>) -> Result<Host> {
        let mut me = Host {
            data: Rc::new(Value::Null),
        };

        let telemetry = try!(telemetry::Telemetry::init(&mut me));

        match path {
            Some(p) => {
                let value = try!(data::open(p));
                me.data = Rc::new(try!(data::merge(value, telemetry)));
            },
            None => me.data = Rc::new(telemetry),
        }

        Ok(me)
    }

    #[cfg(feature = "remote-run")]
    /// Create a new Host connected to the endpoint specified in the
    /// data file. This function expects to find the following keys
    /// in the root namespace: "hostname", "api_port", "file_port".
    pub fn connect<P: AsRef<Path>>(path: P) -> Result<Host> {
        let value = try!(data::open(path.as_ref()));
        let mut me = try!(Self::connect_endpoint(try!(needstr!(value => "/hostname")),
                                                 try!(needu64!(value => "/api_port")) as u32,
                                                 try!(needu64!(value => "/file_port")) as u32));

        let mut telemetry = Rc::new(Value::Null);
        mem::swap(&mut telemetry, &mut me.data);
        // We can use unwrap() here safely as we can guarantee that
        // there is only one strong reference to telemetry.
        me.data = Rc::new(try!(data::merge(value, Rc::try_unwrap(telemetry).unwrap())));

        Ok(me)
    }

    #[cfg(feature = "remote-run")]
    /// Create a new Host connected to the specified endpoint. Note
    /// that this function does not load any user data.
    pub fn connect_endpoint(hostname: &str, api_port: u32, file_port: u32) -> Result<Host> {
        let user_cert = try!(ZCert::load("user.crt"));
        let server_cert = try!(Self::lookup_server_cert(hostname, &user_cert));

        let mut api_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&mut api_sock);
        api_sock.set_curve_serverkey(server_cert.public_txt());
        api_sock.set_sndtimeo(Some(1800000));
        api_sock.set_rcvtimeo(Some(1800000));
        try!(api_sock.connect(&format!("tcp://{}:{}", hostname, api_port)));

        let mut file_sock = ZSock::new(ZSockType::DEALER);
        user_cert.apply(&mut file_sock);
        file_sock.set_curve_serverkey(server_cert.public_txt());
        file_sock.set_sndtimeo(Some(1800000));
        file_sock.set_rcvtimeo(Some(1800000));
        try!(file_sock.connect(&format!("tcp://{}:{}", hostname, file_port)));

        let mut me = Host {
            hostname: hostname.into(),
            api_sock: Some(api_sock),
            file_sock: Some(file_sock),
            data: Rc::new(Value::Null),
        };
        me.data = Rc::new(try!(telemetry::Telemetry::init(&mut me)));

        Ok(me)
    }

    #[cfg(feature = "remote-run")]
    /// Create a new Host specifically for use inside a payload.
    pub fn connect_payload(api_endpoint: &str, file_endpoint: &str) -> Result<Host> {
        let api_sock = ZSock::new(ZSockType::DEALER);
        try!(api_sock.connect(api_endpoint));

        let file_sock = ZSock::new(ZSockType::DEALER);
        try!(file_sock.connect(file_endpoint));

        let data_json = try!(api_sock.recv_str()).unwrap();
        let data = try!(serde_json::from_str(&data_json));

        Ok(Host {
            hostname: "payload".into(),
            api_sock: Some(api_sock),
            file_sock: Some(file_sock),
            data: Rc::new(data),
        })
    }

    /// Get data for Host.
    pub fn data(&self) -> &Value {
        &self.data
    }

    /// Get a reference counted version of data for Host.
    pub fn data_owned(&self) -> Rc<Value> {
        self.data.clone()
    }

    #[cfg(feature = "remote-run")]
    fn lookup_server_cert(hostname: &str, user_cert: &ZCert) -> Result<ZCert> {
        let auth_cert = try!(ZCert::load("auth.crt"));

        let mut auth_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&mut auth_sock);
        auth_sock.set_curve_serverkey(auth_cert.public_txt());
        auth_sock.set_sndtimeo(Some(10000));
        auth_sock.set_rcvtimeo(Some(10000));
        try!(auth_sock.connect(&format!("tcp://{}", ::PROJECT_CONFIG.auth_server)));

        // Get server cert from Auth server
        let msg = ZMsg::new();
        try!(msg.addstr("cert::lookup"));
        try!(msg.addstr(hostname));
        try!(msg.send(&mut auth_sock));

        let reply = try!(ZMsg::recv(&mut auth_sock));

        if reply.size() != 2 {
            return Err(Error::HostResponse);
        }

        match try!(reply.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => {
                let pk = try!(reply.popstr().unwrap().or(Err(Error::HostResponse)));
                Ok(try!(ZCert::from_txt(&pk, "0000000000000000000000000000000000000000")))
            },
            "Err" => Err(Error::Auth(try!(reply.popstr().unwrap().or(Err(Error::HostResponse))))),
            _ => Err(Error::HostResponse),
        }
    }

    #[cfg(all(test, feature = "remote-run"))]
    pub fn test_new(hostname: Option<String>, api_sock: Option<ZSock>, file_sock: Option<ZSock>, data: Option<Value>) -> Host {
        let host = Host {
            hostname: hostname.unwrap_or(String::new()),
            api_sock: api_sock,
            file_sock: file_sock,
            data: match data {
                Some(d) => Rc::new(d),
                None => Rc::new(Value::Null),
            },
        };

        host
    }
}

#[cfg(feature = "remote-run")]
pub trait HostSendRecv {
    fn send(&mut self, msg: ZMsg) -> Result<()>;
    fn send_file(&mut self, msg: ZMsg) -> Result<()>;
    fn send_fs_file<P: AsRef<Path>>(&mut self, file: &mut zfilexfer::File, remote_path: P) -> Result<()>;
    fn recv(&mut self, min: usize, max: Option<usize>) -> Result<ZMsg>;
    fn recv_raw(&mut self) -> Result<ZMsg>;
    fn recv_file_raw(&mut self) -> Result<ZMsg>;
    fn extract_header(msg: &ZMsg) -> Result<()>;
}

#[cfg(feature = "remote-run")]
impl HostSendRecv for Host {
    fn send(&mut self, msg: ZMsg) -> Result<()> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(msg.send(self.api_sock.as_mut().unwrap()));
        Ok(())
    }

    fn send_file(&mut self, msg: ZMsg) -> Result<()> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(msg.send(self.file_sock.as_mut().unwrap()));
        Ok(())
    }

    fn send_fs_file<P: AsRef<Path>>(&mut self, file: &mut zfilexfer::File, remote_path: P) -> Result<()> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(file.send(self.file_sock.as_mut().unwrap(), remote_path));
        Ok(())
    }

    fn recv(&mut self, min: usize, max: Option<usize>) -> Result<ZMsg> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        let msg = try!(ZMsg::recv(self.api_sock.as_mut().unwrap()));
        try!(Self::extract_header(&msg));

        // Check msg size
        if msg.size() < min || (max.is_some() && msg.size() > max.unwrap()) {
            Err(Error::HostResponse)
        } else {
            Ok(msg)
        }
    }

    fn recv_raw(&mut self) -> Result<ZMsg> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        Ok(try!(ZMsg::recv(self.api_sock.as_mut().unwrap())))
    }

    fn recv_file_raw(&mut self) -> Result<ZMsg> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        Ok(try!(ZMsg::recv(self.file_sock.as_mut().unwrap())))
    }

    fn extract_header(msg: &ZMsg) -> Result<()> {
        if msg.size() == 0 {
            return Err(Error::HostResponse);
        }

        match try!(msg.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => Ok(()),
            "Err" => {
                if msg.size() == 0 {
                    Err(Error::HostResponse)
                } else {
                    Err(Error::Agent(try!(msg.popstr().unwrap().or(Err(Error::HostResponse)))))
                }
            },
            _ => Err(Error::HostResponse),
        }
    }
}

#[cfg(feature = "remote-run")]
#[cfg(test)]
mod tests {
    use czmq::{ZMsg, ZSock, ZSockType, ZSys};
    use serde_json;
    use std::fs;
    use std::thread;
    use super::*;
    use tempdir::TempDir;
    use zfilexfer::File;

    #[test]
    fn test_connect_payload() {
        let api = ZSock::new(ZSockType::DEALER);
        let port = api.bind("tcp://127.0.0.1:*").unwrap();
        let api_endpoint = format!("tcp://127.0.0.1:{}", port);

        let handle = thread::spawn(move || {
            api.send_str("{
                \"key\": \"value\"
            }").unwrap();

            api.recv_str().unwrap().unwrap();
        });

        let mut host = Host::connect_payload(&api_endpoint, "inproc://file_endpoint").unwrap();
        assert_eq!(host.data().find("key"), Some(&serde_json::to_value("value")));

        let msg = ZMsg::new();
        msg.addstr("test").unwrap();
        host.send(msg).unwrap();

        handle.join().unwrap();
    }

    #[test]
    fn test_send_recv() {
        let _ = ::_MOCK_ENV.init();

        let (client, server) = ZSys::create_pipe().unwrap();

        let mut host1 = Host::test_new(None, Some(client), None, None);
        let mut host2 = Host::test_new(None, Some(server), None, None);

        let msg = ZMsg::new();
        msg.addstr("Ok").unwrap();
        msg.addstr("moo").unwrap();
        msg.addstr("cow").unwrap();
        host1.send(msg).unwrap();

        let reply = host2.recv(2, Some(2)).unwrap();
        assert_eq!(reply.popstr().unwrap().unwrap(), "moo");
        assert_eq!(reply.popstr().unwrap().unwrap(), "cow");

        let msg = ZMsg::new();
        msg.addstr("No header").unwrap();
        host2.send(msg).unwrap();

        assert!(host1.recv(0, None).is_err());
        let msg = ZMsg::new();
        msg.addstr("Err").unwrap();
        host1.send(msg).unwrap();

        assert!(host2.recv(0, None).is_err());
    }

    #[test]
    fn test_send_file() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("host_test_send_file").unwrap();
        let path = format!("{}/file.txt", tempdir.path().to_str().unwrap());
        fs::File::create(&path).unwrap();
        let mut file = File::open(&path, None).unwrap();

        let (client, mut server) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));
        server.set_rcvtimeo(Some(500));

        let handle = thread::spawn(move|| {
            let msg = ZMsg::recv(&mut server).unwrap();
            assert_eq!(msg.popstr().unwrap().unwrap(), "NEW");

            server.flush();
            server.send_str("Ok").unwrap();
        });

        let mut host = Host::test_new(None, None, Some(client), None);
        assert!(host.send_file(&mut file, &path).is_ok());

        handle.join().unwrap();
    }
}
<commit_msg>Fixed bug where test referenced deprecated method<commit_after>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! The host wrapper for communicating with a managed host.
//!
//! # Examples
//!
//! ```no_run
//! # use inapi::{Command, Host};
#![cfg_attr(feature = "local-run", doc = "let path: Option<String> = None;")]
#![cfg_attr(feature = "local-run", doc = "let mut host = Host::local(path).unwrap();")]
#![cfg_attr(feature = "remote-run", doc = "let mut host = Host::connect(\"nodes/mynode.json\").unwrap();")]
//!
//! let cmd = Command::new("whoami");
//! let result = cmd.exec(&mut host).unwrap();
//! ```

#[macro_use]
pub mod data;
pub mod ffi;
pub mod telemetry;

pub use self::telemetry::TelemetryTarget;

#[cfg(feature = "remote-run")]
use czmq::{ZCert, ZMsg, ZSock, ZSockType};
#[cfg(feature = "remote-run")]
use error::Error;
use error::Result;
#[cfg(feature = "remote-run")]
use serde_json;
use serde_json::Value;
#[cfg(feature = "remote-run")]
use std::mem;
use std::path::Path;
use std::rc::Rc;
#[cfg(feature = "remote-run")]
use zfilexfer;

#[cfg(feature = "local-run")]
/// Representation of a managed host.
pub struct Host {
    /// Data for host, comprising data files and telemetry
    data: Rc<Value>,
}

#[cfg(feature = "remote-run")]
/// Representation of a managed host.
pub struct Host {
    /// Hostname or IP of managed host
    pub hostname: String,
    /// API socket
    api_sock: Option<ZSock>,
    /// File transfer socket
    file_sock: Option<ZSock>,
    /// Data for host, comprising data files and telemetry
    data: Rc<Value>,
}

impl Host {
    #[cfg(feature = "local-run")]
    /// Create a new Host connected to localhost.
    pub fn local<P: AsRef<Path>>(path: Option<P>) -> Result<Host> {
        let mut me = Host {
            data: Rc::new(Value::Null),
        };

        let telemetry = try!(telemetry::Telemetry::init(&mut me));

        match path {
            Some(p) => {
                let value = try!(data::open(p));
                me.data = Rc::new(try!(data::merge(value, telemetry)));
            },
            None => me.data = Rc::new(telemetry),
        }

        Ok(me)
    }

    #[cfg(feature = "remote-run")]
    /// Create a new Host connected to the endpoint specified in the
    /// data file. This function expects to find the following keys
    /// in the root namespace: "hostname", "api_port", "file_port".
    pub fn connect<P: AsRef<Path>>(path: P) -> Result<Host> {
        let value = try!(data::open(path.as_ref()));
        let mut me = try!(Self::connect_endpoint(try!(needstr!(value => "/hostname")),
                                                 try!(needu64!(value => "/api_port")) as u32,
                                                 try!(needu64!(value => "/file_port")) as u32));

        let mut telemetry = Rc::new(Value::Null);
        mem::swap(&mut telemetry, &mut me.data);
        // We can use unwrap() here safely as we can guarantee that
        // there is only one strong reference to telemetry.
        me.data = Rc::new(try!(data::merge(value, Rc::try_unwrap(telemetry).unwrap())));

        Ok(me)
    }

    #[cfg(feature = "remote-run")]
    /// Create a new Host connected to the specified endpoint. Note
    /// that this function does not load any user data.
    pub fn connect_endpoint(hostname: &str, api_port: u32, file_port: u32) -> Result<Host> {
        let user_cert = try!(ZCert::load("user.crt"));
        let server_cert = try!(Self::lookup_server_cert(hostname, &user_cert));

        let mut api_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&mut api_sock);
        api_sock.set_curve_serverkey(server_cert.public_txt());
        api_sock.set_sndtimeo(Some(1800000));
        api_sock.set_rcvtimeo(Some(1800000));
        try!(api_sock.connect(&format!("tcp://{}:{}", hostname, api_port)));

        let mut file_sock = ZSock::new(ZSockType::DEALER);
        user_cert.apply(&mut file_sock);
        file_sock.set_curve_serverkey(server_cert.public_txt());
        file_sock.set_sndtimeo(Some(1800000));
        file_sock.set_rcvtimeo(Some(1800000));
        try!(file_sock.connect(&format!("tcp://{}:{}", hostname, file_port)));

        let mut me = Host {
            hostname: hostname.into(),
            api_sock: Some(api_sock),
            file_sock: Some(file_sock),
            data: Rc::new(Value::Null),
        };
        me.data = Rc::new(try!(telemetry::Telemetry::init(&mut me)));

        Ok(me)
    }

    #[cfg(feature = "remote-run")]
    /// Create a new Host specifically for use inside a payload.
    pub fn connect_payload(api_endpoint: &str, file_endpoint: &str) -> Result<Host> {
        let api_sock = ZSock::new(ZSockType::DEALER);
        try!(api_sock.connect(api_endpoint));

        let file_sock = ZSock::new(ZSockType::DEALER);
        try!(file_sock.connect(file_endpoint));

        let data_json = try!(api_sock.recv_str()).unwrap();
        let data = try!(serde_json::from_str(&data_json));

        Ok(Host {
            hostname: "payload".into(),
            api_sock: Some(api_sock),
            file_sock: Some(file_sock),
            data: Rc::new(data),
        })
    }

    /// Get data for Host.
    pub fn data(&self) -> &Value {
        &self.data
    }

    /// Get a reference counted version of data for Host.
    pub fn data_owned(&self) -> Rc<Value> {
        self.data.clone()
    }

    #[cfg(feature = "remote-run")]
    fn lookup_server_cert(hostname: &str, user_cert: &ZCert) -> Result<ZCert> {
        let auth_cert = try!(ZCert::load("auth.crt"));

        let mut auth_sock = ZSock::new(ZSockType::REQ);
        user_cert.apply(&mut auth_sock);
        auth_sock.set_curve_serverkey(auth_cert.public_txt());
        auth_sock.set_sndtimeo(Some(10000));
        auth_sock.set_rcvtimeo(Some(10000));
        try!(auth_sock.connect(&format!("tcp://{}", ::PROJECT_CONFIG.auth_server)));

        // Get server cert from Auth server
        let msg = ZMsg::new();
        try!(msg.addstr("cert::lookup"));
        try!(msg.addstr(hostname));
        try!(msg.send(&mut auth_sock));

        let reply = try!(ZMsg::recv(&mut auth_sock));

        if reply.size() != 2 {
            return Err(Error::HostResponse);
        }

        match try!(reply.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => {
                let pk = try!(reply.popstr().unwrap().or(Err(Error::HostResponse)));
                Ok(try!(ZCert::from_txt(&pk, "0000000000000000000000000000000000000000")))
            },
            "Err" => Err(Error::Auth(try!(reply.popstr().unwrap().or(Err(Error::HostResponse))))),
            _ => Err(Error::HostResponse),
        }
    }

    #[cfg(all(test, feature = "remote-run"))]
    pub fn test_new(hostname: Option<String>, api_sock: Option<ZSock>, file_sock: Option<ZSock>, data: Option<Value>) -> Host {
        let host = Host {
            hostname: hostname.unwrap_or(String::new()),
            api_sock: api_sock,
            file_sock: file_sock,
            data: match data {
                Some(d) => Rc::new(d),
                None => Rc::new(Value::Null),
            },
        };

        host
    }
}

#[cfg(feature = "remote-run")]
pub trait HostSendRecv {
    fn send(&mut self, msg: ZMsg) -> Result<()>;
    fn send_file(&mut self, msg: ZMsg) -> Result<()>;
    fn send_fs_file<P: AsRef<Path>>(&mut self, file: &mut zfilexfer::File, remote_path: P) -> Result<()>;
    fn recv(&mut self, min: usize, max: Option<usize>) -> Result<ZMsg>;
    fn recv_raw(&mut self) -> Result<ZMsg>;
    fn recv_file_raw(&mut self) -> Result<ZMsg>;
    fn extract_header(msg: &ZMsg) -> Result<()>;
}

#[cfg(feature = "remote-run")]
impl HostSendRecv for Host {
    fn send(&mut self, msg: ZMsg) -> Result<()> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(msg.send(self.api_sock.as_mut().unwrap()));
        Ok(())
    }

    fn send_file(&mut self, msg: ZMsg) -> Result<()> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(msg.send(self.file_sock.as_mut().unwrap()));
        Ok(())
    }

    fn send_fs_file<P: AsRef<Path>>(&mut self, file: &mut zfilexfer::File, remote_path: P) -> Result<()> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        try!(file.send(self.file_sock.as_mut().unwrap(), remote_path));
        Ok(())
    }

    fn recv(&mut self, min: usize, max: Option<usize>) -> Result<ZMsg> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        let msg = try!(ZMsg::recv(self.api_sock.as_mut().unwrap()));
        try!(Self::extract_header(&msg));

        // Check msg size
        if msg.size() < min || (max.is_some() && msg.size() > max.unwrap()) {
            Err(Error::HostResponse)
        } else {
            Ok(msg)
        }
    }

    fn recv_raw(&mut self) -> Result<ZMsg> {
        if self.api_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        Ok(try!(ZMsg::recv(self.api_sock.as_mut().unwrap())))
    }

    fn recv_file_raw(&mut self) -> Result<ZMsg> {
        if self.file_sock.is_none() {
            return Err(Error::HostDisconnected);
        }

        Ok(try!(ZMsg::recv(self.file_sock.as_mut().unwrap())))
    }

    fn extract_header(msg: &ZMsg) -> Result<()> {
        if msg.size() == 0 {
            return Err(Error::HostResponse);
        }

        match try!(msg.popstr().unwrap().or(Err(Error::HostResponse))).as_ref() {
            "Ok" => Ok(()),
            "Err" => {
                if msg.size() == 0 {
                    Err(Error::HostResponse)
                } else {
                    Err(Error::Agent(try!(msg.popstr().unwrap().or(Err(Error::HostResponse)))))
                }
            },
            _ => Err(Error::HostResponse),
        }
    }
}

#[cfg(feature = "remote-run")]
#[cfg(test)]
mod tests {
    use czmq::{ZMsg, ZSock, ZSockType, ZSys};
    use serde_json;
    use std::fs;
    use std::thread;
    use super::*;
    use tempdir::TempDir;
    use zfilexfer::File;

    #[test]
    fn test_connect_payload() {
        let api = ZSock::new(ZSockType::DEALER);
        let port = api.bind("tcp://127.0.0.1:*").unwrap();
        let api_endpoint = format!("tcp://127.0.0.1:{}", port);

        let handle = thread::spawn(move || {
            api.send_str("{
                \"key\": \"value\"
            }").unwrap();

            api.recv_str().unwrap().unwrap();
        });

        let mut host = Host::connect_payload(&api_endpoint, "inproc://file_endpoint").unwrap();
        assert_eq!(host.data().find("key"), Some(&serde_json::to_value("value")));

        let msg = ZMsg::new();
        msg.addstr("test").unwrap();
        host.send(msg).unwrap();

        handle.join().unwrap();
    }

    #[test]
    fn test_send_recv() {
        let _ = ::_MOCK_ENV.init();

        let (client, server) = ZSys::create_pipe().unwrap();

        let mut host1 = Host::test_new(None, Some(client), None, None);
        let mut host2 = Host::test_new(None, Some(server), None, None);

        let msg = ZMsg::new();
        msg.addstr("Ok").unwrap();
        msg.addstr("moo").unwrap();
        msg.addstr("cow").unwrap();
        host1.send(msg).unwrap();

        let reply = host2.recv(2, Some(2)).unwrap();
        assert_eq!(reply.popstr().unwrap().unwrap(), "moo");
        assert_eq!(reply.popstr().unwrap().unwrap(), "cow");

        let msg = ZMsg::new();
        msg.addstr("No header").unwrap();
        host2.send(msg).unwrap();

        assert!(host1.recv(0, None).is_err());
        let msg = ZMsg::new();
        msg.addstr("Err").unwrap();
        host1.send(msg).unwrap();

        assert!(host2.recv(0, None).is_err());
    }

    #[test]
    fn test_send_file() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("host_test_send_file").unwrap();
        let path = format!("{}/file.txt", tempdir.path().to_str().unwrap());
        fs::File::create(&path).unwrap();
        let mut file = File::open(&path, None).unwrap();

        let (client, mut server) = ZSys::create_pipe().unwrap();
        client.set_rcvtimeo(Some(500));
        server.set_rcvtimeo(Some(500));

        let handle = thread::spawn(move|| {
            let msg = ZMsg::recv(&mut server).unwrap();
            assert_eq!(msg.popstr().unwrap().unwrap(), "NEW");

            server.flush();
            server.send_str("Ok").unwrap();
        });

        let mut host = Host::test_new(None, None, Some(client), None);
        assert!(host.send_fs_file(&mut file, &path).is_ok());

        handle.join().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate forth;

use forth::{Error, Forth, Value};

#[test]
fn no_input_no_stack() {
    assert_eq!(Vec::<Value>::new(), Forth::new().stack());
}

#[test]
#[ignore]
fn numbers_just_get_pushed_onto_the_stack() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 4 5").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5], f.stack());
}

#[test]
#[ignore]
fn non_word_characters_are_separators() {
    let mut f = Forth::new();
    assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack());
}

#[test]
#[ignore]
fn can_add_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 +").is_ok());
    assert_eq!(vec![3], f.stack());
}

#[test]
#[ignore]
fn addition_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("+"));
}

#[test]
#[ignore]
fn can_subtract_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("3 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn subtraction_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("-"));
}

#[test]
#[ignore]
fn can_multiply_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 *").is_ok());
    assert_eq!(vec![8], f.stack());
}

#[test]
#[ignore]
fn multiplication_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("*"));
}

#[test]
#[ignore]
fn can_divide_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("12 3 /").is_ok());
    assert_eq!(vec![4], f.stack());
}

#[test]
#[ignore]
fn performs_integer_division() {
    let mut f = Forth::new();
    assert!(f.eval("8 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn division_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("/"));
}

#[test]
#[ignore]
fn division_by_zero() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::DivisionByZero), f.eval("4 2 2 - /"));
}

#[test]
#[ignore]
fn addition_and_subtraction() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 + 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn multiplication_and_division() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 * 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn dup() {
    let mut f = Forth::new();
    assert!(f.eval("1 DUP").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_case_insensitive() {
    let mut f = Forth::new();
    assert!(f.eval("1 Dup").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("dup"));
}

#[test]
#[ignore]
fn drop() {
    let mut f = Forth::new();
    assert!(f.eval("1 drop").is_ok());
    assert_eq!(Vec::<Value>::new(), f.stack());
}

#[test]
#[ignore]
fn drop_with_two() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 drop").is_ok());
    assert_eq!(vec![1], f.stack());
}

#[test]
#[ignore]
fn drop_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("drop"));
}

#[test]
#[ignore]
fn swap() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 swap").is_ok());
    assert_eq!(vec![2, 1], f.stack());
}

#[test]
#[ignore]
fn swap_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 swap").is_ok());
    assert_eq!(vec![1, 3, 2], f.stack());
}

#[test]
#[ignore]
fn swap_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("swap"));
}

#[test]
#[ignore]
fn over() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 over").is_ok());
    assert_eq!(vec![1, 2, 1], f.stack());
}

#[test]
#[ignore]
fn over_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 over").is_ok());
    assert_eq!(vec![1, 2, 3, 2], f.stack());
}

#[test]
#[ignore]
fn over_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 over"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("over"));
}

#[test]
#[ignore]
fn defining_a_new_word() {
    let mut f = Forth::new();
    assert!(f.eval(": CoUnT 1 2 3 ;").is_ok());
    assert!(f.eval("count COUNT").is_ok());
    assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_word() {
    let mut f = Forth::new();
    assert!(f.eval(": foo dup ;").is_ok());
    assert!(f.eval(": foo dup dup ;").is_ok());
    assert!(f.eval("1 foo").is_ok());
    assert_eq!(vec![1, 1, 1], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_built_in_word() {
    let mut f = Forth::new();
    assert!(f.eval(": swap dup ;").is_ok());
    assert!(f.eval("1 swap").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn defining_words_with_odd_characters() {
    let mut f = Forth::new();
    assert!(f.eval(": € 220371 ; €").is_ok());
    assert_eq!(vec![220371], f.stack());
}

#[test]
#[ignore]
fn defining_a_number() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;"));
}

#[test]
#[ignore]
fn malformed_word_definition() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(":"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1"));
}

#[test]
#[ignore]
fn calling_non_existing_word() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::UnknownWord), f.eval("1 foo"));
}
<commit_msg>forth: Be more direct w/ division by zero<commit_after>extern crate forth;

use forth::{Error, Forth, Value};

#[test]
fn no_input_no_stack() {
    assert_eq!(Vec::<Value>::new(), Forth::new().stack());
}

#[test]
#[ignore]
fn numbers_just_get_pushed_onto_the_stack() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 4 5").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5], f.stack());
}

#[test]
#[ignore]
fn non_word_characters_are_separators() {
    let mut f = Forth::new();
    assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack());
}

#[test]
#[ignore]
fn can_add_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 +").is_ok());
    assert_eq!(vec![3], f.stack());
}

#[test]
#[ignore]
fn addition_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("+"));
}

#[test]
#[ignore]
fn can_subtract_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("3 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn subtraction_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("-"));
}

#[test]
#[ignore]
fn can_multiply_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 *").is_ok());
    assert_eq!(vec![8], f.stack());
}

#[test]
#[ignore]
fn multiplication_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("*"));
}

#[test]
#[ignore]
fn can_divide_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("12 3 /").is_ok());
    assert_eq!(vec![4], f.stack());
}

#[test]
#[ignore]
fn performs_integer_division() {
    let mut f = Forth::new();
    assert!(f.eval("8 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn division_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("/"));
}

#[test]
#[ignore]
fn errors_if_dividing_by_zero() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::DivisionByZero), f.eval("4 0 /"));
}

#[test]
#[ignore]
fn addition_and_subtraction() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 + 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn multiplication_and_division() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 * 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn dup() {
    let mut f = Forth::new();
    assert!(f.eval("1 DUP").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_case_insensitive() {
    let mut f = Forth::new();
    assert!(f.eval("1 Dup").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("dup"));
}

#[test]
#[ignore]
fn drop() {
    let mut f = Forth::new();
    assert!(f.eval("1 drop").is_ok());
    assert_eq!(Vec::<Value>::new(), f.stack());
}

#[test]
#[ignore]
fn drop_with_two() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 drop").is_ok());
    assert_eq!(vec![1], f.stack());
}

#[test]
#[ignore]
fn drop_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("drop"));
}

#[test]
#[ignore]
fn swap() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 swap").is_ok());
    assert_eq!(vec![2, 1], f.stack());
}

#[test]
#[ignore]
fn swap_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 swap").is_ok());
    assert_eq!(vec![1, 3, 2], f.stack());
}

#[test]
#[ignore]
fn swap_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("swap"));
}

#[test]
#[ignore]
fn over() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 over").is_ok());
    assert_eq!(vec![1, 2, 1], f.stack());
}

#[test]
#[ignore]
fn over_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 over").is_ok());
    assert_eq!(vec![1, 2, 3, 2], f.stack());
}

#[test]
#[ignore]
fn over_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 over"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("over"));
}

#[test]
#[ignore]
fn defining_a_new_word() {
    let mut f = Forth::new();
    assert!(f.eval(": CoUnT 1 2 3 ;").is_ok());
    assert!(f.eval("count COUNT").is_ok());
    assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_word() {
    let mut f = Forth::new();
    assert!(f.eval(": foo dup ;").is_ok());
    assert!(f.eval(": foo dup dup ;").is_ok());
    assert!(f.eval("1 foo").is_ok());
    assert_eq!(vec![1, 1, 1], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_built_in_word() {
    let mut f = Forth::new();
    assert!(f.eval(": swap dup ;").is_ok());
    assert!(f.eval("1 swap").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn defining_words_with_odd_characters() {
    let mut f = Forth::new();
    assert!(f.eval(": € 220371 ; €").is_ok());
    assert_eq!(vec![220371], f.stack());
}

#[test]
#[ignore]
fn defining_a_number() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;"));
}

#[test]
#[ignore]
fn malformed_word_definition() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(":"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1"));
}

#[test]
#[ignore]
fn calling_non_existing_word() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::UnknownWord), f.eval("1 foo"));
}
<|endoftext|>