<commit_before>use engine::Game;
use gui::screens::{self, Screen, ScreenChange};
use gui::{Console, Colors, Key};
use gui::chars;
use gui::Colored;
use util::units::{AsTuple, Direction, Point, Rectangle, Size};

#[allow(missing_copy_implementations)]
pub struct GameScreen {
    map_frame: Rectangle,
    map_view: Point,
    info_frame: Rectangle,
    message_frame: Rectangle,
}

impl GameScreen {
    pub fn new() -> Box<Screen> {
        Box::new(
            GameScreen {
                map_frame: Rectangle::new(Point::new(16, 1), Size::new(63, 38)),
                map_view: Point::new(0, 0),
                info_frame: Rectangle::new(Point::new(1, 1), Size::new(13, 48)),
                message_frame: Rectangle::new(Point::new(16, 41), Size::new(63, 8)),
            }
        )
    }
}

impl Screen for GameScreen {

    #[allow(unused)]
    fn input(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        if let Some(key) = console.check_for_keypress() {
            match key {
                Key::Up => {
                    game.world.walk(Direction::Up);
                },
                Key::Down => {
                    game.world.walk(Direction::Down);
                },
                Key::Left => {
                    game.world.walk(Direction::Left);
                },
                Key::Right => {
                    game.world.walk(Direction::Right);
                },
                Key::Escape => {
                    return Some(ScreenChange::AddScreen(screens::PauseScreen::new()));
                },
                _ => {}
            }
        }

        None
    }

    #[allow(unused)]
    fn update(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        None
    }

    #[allow(unused)]
    fn render(&mut self, game: &mut Game, console: &mut Console) {
        self.draw_borders(game, console);
        self.draw_map(game, console);
        self.draw_player(game, console);
    }
}

impl GameScreen {

    #[allow(unused)]
    fn draw_borders(&self, game: &mut Game, console: &mut Console) {
        self.draw_box(console, self.map_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.map_frame.move_dir(Point::new(0, -1)).location(), "Map");

        self.draw_box(console, self.info_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.info_frame.move_dir(Point::new(0, -1)).location(), "Info");

        self.draw_box(console, self.message_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.message_frame.move_dir(Point::new(0, -1)).location(), "Messages");
    }

    fn draw_box(&self, console: &mut Console, rect: Rectangle) {
        console.put_plain(rect.location() + Point::new(0, 0), chars::NW);
        console.put_plain(rect.location() + Point::new(rect.width(), 0), chars::NE);
        console.put_plain(rect.location() + Point::new(0, rect.height()), chars::SW);
        console.put_plain(rect.location() + Point::new(rect.width(), rect.height()), chars::SE);

        for x in 1..rect.width() {
            console.put_plain(rect.location() + Point::new(x, 0), chars::HLINE);
            console.put_plain(rect.location() + Point::new(x, rect.height()), chars::HLINE);
        }

        for y in 1..rect.height() {
            console.put_plain(rect.location() + Point::new(0, y), chars::VLINE);
            console.put_plain(rect.location() + Point::new(rect.width(), y), chars::VLINE);
        }
    }

    #[allow(unused)]
    fn draw_map(&self, game: &mut Game, console: &mut Console) {
        let map = &game.world.map;

        let (ux, uy): (usize, usize) = self.map_view.as_tuple();
        let (width, height) = self.map_frame.size().as_tuple();

        for (y, line) in map.tiles[uy .. uy + height].iter().enumerate() {
            for (x, cell) in line[ux .. ux + width].iter().enumerate() {
                console.put(self.map_frame.location() + Point::new(x as i32, y as i32), ' ', Colors::white, cell.bg_color());
            }
        }
    }

    #[allow(unused)]
    fn draw_player(&mut self, game: &mut Game, console: &mut Console) {
        let pos = game.world.player.pos;

        let adjusted_pos = pos + self.map_frame.location() - self.map_view;

        if adjusted_pos.x >= self.map_frame.location().x + self.map_frame.width() - 10 {
            if self.view_can_move_right(game) {
                self.map_view = self.map_view.right(1);
            }
        }

        if adjusted_pos.y >= self.map_frame.location().y + self.map_frame.height() - 5 {
            if self.view_can_move_down(game) {
                self.map_view = self.map_view.down(1);
            }
        }

        if adjusted_pos.y <= self.map_frame.location().y + 5 {
            if self.view_can_move_up(game) {
                self.map_view = self.map_view.up(1);
            }
        }

        if adjusted_pos.x <= self.map_frame.location().x + 10 {
            if self.view_can_move_left(game) {
                self.map_view = self.map_view.left(1);
            }
        }

        if adjusted_pos.x >= self.map_frame.location().x && adjusted_pos.y >= self.map_frame.location().y {
            console.put_plain(self.map_frame.location() - self.map_view + pos, '@');
        }
    }

    #[allow(unused)]
    fn view_can_move_up(&self, game: &Game) -> bool {
        self.map_view.y  > 0
    }

    #[allow(unused)]
    fn view_can_move_down(&self, game: &Game) -> bool {
        self.map_view.y + self.map_frame.height() < game.world.map.height()
    }

    #[allow(unused)]
    fn view_can_move_left(&self, game: &Game) -> bool {
        self.map_view.x > 0
    }

    #[allow(unused)]
    fn view_can_move_right(&self, game: &Game) -> bool {
        self.map_view.x + self.map_frame.width() < game.world.map.width()
    }

}
<commit_msg>Move window title drawing into draw_box()<commit_after>use engine::Game;
use gui::screens::{self, Screen, ScreenChange};
use gui::{Console, Colors, Key};
use gui::chars;
use gui::Colored;
use util::units::{AsTuple, Direction, Point, Rectangle, Size};

#[allow(missing_copy_implementations)]
pub struct GameScreen {
    map_frame: Rectangle,
    map_view: Point,
    info_frame: Rectangle,
    message_frame: Rectangle,
}

impl GameScreen {
    pub fn new() -> Box<Screen> {
        Box::new(
            GameScreen {
                map_frame: Rectangle::new(Point::new(16, 1), Size::new(63, 38)),
                map_view: Point::new(0, 0),
                info_frame: Rectangle::new(Point::new(1, 1), Size::new(13, 48)),
                message_frame: Rectangle::new(Point::new(16, 41), Size::new(63, 8)),
            }
        )
    }
}

impl Screen for GameScreen {

    #[allow(unused)]
    fn input(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        if let Some(key) = console.check_for_keypress() {
            match key {
                Key::Up => {
                    game.world.walk(Direction::Up);
                },
                Key::Down => {
                    game.world.walk(Direction::Down);
                },
                Key::Left => {
                    game.world.walk(Direction::Left);
                },
                Key::Right => {
                    game.world.walk(Direction::Right);
                },
                Key::Escape => {
                    return Some(ScreenChange::AddScreen(screens::PauseScreen::new()));
                },
                _ => {}
            }
        }

        None
    }

    #[allow(unused)]
    fn update(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        None
    }

    #[allow(unused)]
    fn render(&mut self, game: &mut Game, console: &mut Console) {
        self.draw_borders(game, console);
        self.draw_map(game, console);
        self.draw_player(game, console);
    }
}

impl GameScreen {

    #[allow(unused)]
    fn draw_borders(&self, game: &mut Game, console: &mut Console) {
        self.draw_box_with_title(console, "Map", self.map_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));

        self.draw_box_with_title(console, "Info", self.info_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));

        self.draw_box_with_title(console, "Messages", self.message_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
    }

    fn draw_box_with_title(&self, console: &mut Console, title: &str, rect: Rectangle) {
        console.put_plain(rect.location() + Point::new(0, 0), chars::NW);
        console.put_plain(rect.location() + Point::new(rect.width(), 0), chars::NE);
        console.put_plain(rect.location() + Point::new(0, rect.height()), chars::SW);
        console.put_plain(rect.location() + Point::new(rect.width(), rect.height()), chars::SE);

        for x in 1..rect.width() {
            console.put_plain(rect.location() + Point::new(x, 0), chars::HLINE);
            console.put_plain(rect.location() + Point::new(x, rect.height()), chars::HLINE);
        }

        for y in 1..rect.height() {
            console.put_plain(rect.location() + Point::new(0, y), chars::VLINE);
            console.put_plain(rect.location() + Point::new(rect.width(), y), chars::VLINE);
        }

        console.print_plain(rect.location() + Point::new(1, 0), title);
    }

    #[allow(unused)]
    fn draw_map(&self, game: &mut Game, console: &mut Console) {
        let map = &game.world.map;

        let (ux, uy): (usize, usize) = self.map_view.as_tuple();
        let (width, height) = self.map_frame.size().as_tuple();

        for (y, line) in map.tiles[uy .. uy + height].iter().enumerate() {
            for (x, cell) in line[ux .. ux + width].iter().enumerate() {
                console.put(self.map_frame.location() + Point::new(x as i32, y as i32), ' ', Colors::white, cell.bg_color());
            }
        }
    }

    #[allow(unused)]
    fn draw_player(&mut self, game: &mut Game, console: &mut Console) {
        let pos = game.world.player.pos;

        let adjusted_pos = pos + self.map_frame.location() - self.map_view;

        if adjusted_pos.x >= self.map_frame.location().x + self.map_frame.width() - 10 {
            if self.view_can_move_right(game) {
                self.map_view = self.map_view.right(1);
            }
        }

        if adjusted_pos.y >= self.map_frame.location().y + self.map_frame.height() - 5 {
            if self.view_can_move_down(game) {
                self.map_view = self.map_view.down(1);
            }
        }

        if adjusted_pos.y <= self.map_frame.location().y + 5 {
            if self.view_can_move_up(game) {
                self.map_view = self.map_view.up(1);
            }
        }

        if adjusted_pos.x <= self.map_frame.location().x + 10 {
            if self.view_can_move_left(game) {
                self.map_view = self.map_view.left(1);
            }
        }

        if adjusted_pos.x >= self.map_frame.location().x && adjusted_pos.y >= self.map_frame.location().y {
            console.put_plain(self.map_frame.location() - self.map_view + pos, '@');
        }
    }

    #[allow(unused)]
    fn view_can_move_up(&self, game: &Game) -> bool {
        self.map_view.y  > 0
    }

    #[allow(unused)]
    fn view_can_move_down(&self, game: &Game) -> bool {
        self.map_view.y + self.map_frame.height() < game.world.map.height()
    }

    #[allow(unused)]
    fn view_can_move_left(&self, game: &Game) -> bool {
        self.map_view.x > 0
    }

    #[allow(unused)]
    fn view_can_move_right(&self, game: &Game) -> bool {
        self.map_view.x + self.map_frame.width() < game.world.map.width()
    }

}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![crate_name = "webdriver_server"]
#![crate_type = "rlib"]

#![feature(rustc_private, ip_addr)]

#[macro_use]
extern crate log;

extern crate webdriver;
extern crate msg;
extern crate png;
extern crate url;
extern crate util;
extern crate rustc_serialize;
extern crate uuid;
extern crate webdriver_traits;

use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId, NavigationDirection};
use msg::constellation_msg::Msg as ConstellationMsg;
use std::sync::mpsc::channel;
use webdriver_traits::WebDriverScriptCommand;

use url::Url;
use webdriver::command::{WebDriverMessage, WebDriverCommand};
use webdriver::command::{GetParameters, JavascriptCommandParameters, LocatorParameters};
use webdriver::common::{LocatorStrategy, WebElement};
use webdriver::response::{
    WebDriverResponse, NewSessionResponse, ValueResponse};
use webdriver::server::{self, WebDriverHandler, Session};
use webdriver::error::{WebDriverResult, WebDriverError, ErrorStatus};
use util::task::spawn_named;
use uuid::Uuid;

use std::borrow::ToOwned;
use rustc_serialize::json::{Json, ToJson};
use rustc_serialize::base64::{Config, ToBase64, CharacterSet, Newline};
use std::collections::BTreeMap;
use std::net::SocketAddr;

use std::thread::sleep_ms;

pub fn start_server(port: u16, constellation_chan: ConstellationChan) {
    let handler = Handler::new(constellation_chan);

    spawn_named("WebdriverHttpServer".to_owned(), move || {
        server::start(SocketAddr::new("0.0.0.0".parse().unwrap(), port), handler);
    });
}

struct WebdriverSession {
    id: Uuid
}

struct Handler {
    session: Option<WebdriverSession>,
    constellation_chan: ConstellationChan,
}

impl WebdriverSession {
    pub fn new() -> WebdriverSession {
        WebdriverSession {
            id: Uuid::new_v4()
        }
    }
}

impl Handler {
    pub fn new(constellation_chan: ConstellationChan) -> Handler {
        Handler {
            session: None,
            constellation_chan: constellation_chan
        }
    }

    fn get_root_pipeline(&self) -> PipelineId {
        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::GetRootPipeline(sender)).unwrap();

        reciever.recv().unwrap().unwrap()
    }

    fn handle_new_session(&mut self) -> WebDriverResult<WebDriverResponse> {
        if self.session.is_none() {
            let session = WebdriverSession::new();
            let rv = Ok(WebDriverResponse::NewSession(
                NewSessionResponse::new(
                    session.id.to_string(),
                    Json::Object(BTreeMap::new()))));
            self.session = Some(session);
            rv
        } else {
            Err(WebDriverError::new(ErrorStatus::UnknownError,
                                    "Session already created"))
        }
    }

    fn handle_get(&self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
        let url = match Url::parse(&parameters.url[..]) {
            Ok(url) => url,
            Err(_) => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                               "Invalid URL"))
        };

        let pipeline_id = self.get_root_pipeline();

        let load_data = LoadData::new(url);
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap();
        //TODO: Now we ought to wait until we get a load event
        Ok(WebDriverResponse::Void)
    }

    fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Back)).unwrap();
        Ok(WebDriverResponse::Void)
    }

    fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Forward)).unwrap();
        Ok(WebDriverResponse::Void)
    }

    fn handle_get_title(&self) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id,
                                                           WebDriverScriptCommand::GetTitle(sender))).unwrap();
        let value = reciever.recv().unwrap();
        Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json())))
    }

    fn handle_get_window_handle(&self) -> WebDriverResult<WebDriverResponse> {
        // For now we assume there's only one window so just use the session
        // id as the window id
        let handle = self.session.as_ref().unwrap().id.to_string();
        Ok(WebDriverResponse::Generic(ValueResponse::new(handle.to_json())))
    }

    fn handle_get_window_handles(&self) -> WebDriverResult<WebDriverResponse> {
        // For now we assume there's only one window so just use the session
        // id as the window id
        let handles = vec![self.session.as_ref().unwrap().id.to_string().to_json()];
        Ok(WebDriverResponse::Generic(ValueResponse::new(handles.to_json())))
    }

    fn handle_find_element(&self, parameters: &LocatorParameters) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        if parameters.using != LocatorStrategy::CSSSelector {
            return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                           "Unsupported locator strategy"))
        }

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::FindElementCSS(parameters.value.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => {
                Ok(WebDriverResponse::Generic(ValueResponse::new(value.map(|x| WebElement::new(x).to_json()).to_json())))
            }
            Err(_) => Err(WebDriverError::new(ErrorStatus::InvalidSelector,
                                              "Invalid selector"))
        }
    }

    fn handle_find_elements(&self, parameters: &LocatorParameters) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        if parameters.using != LocatorStrategy::CSSSelector {
            return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                           "Unsupported locator strategy"))
        }

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::FindElementsCSS(parameters.value.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => {
                let resp_value: Vec<Json> = value.into_iter().map(
                    |x| WebElement::new(x).to_json()).collect();
                Ok(WebDriverResponse::Generic(ValueResponse::new(resp_value.to_json())))
            }
            Err(_) => Err(WebDriverError::new(ErrorStatus::InvalidSelector,
                                              "Invalid selector"))
        }
    }

    fn handle_get_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::GetElementText(element.id.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
            Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference,
                                              "Unable to find element in document"))
        }
    }

    fn handle_get_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::GetElementTagName(element.id.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
            Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference,
                                              "Unable to find element in document"))
        }
    }

    fn handle_execute_script(&self, parameters: &JavascriptCommandParameters)  -> WebDriverResult<WebDriverResponse> {
        // TODO: This isn't really right because it always runs the script in the
        // root window
        let pipeline_id = self.get_root_pipeline();

        let func_body = &parameters.script;
        let args_string = "";

        // This is pretty ugly; we really want something that acts like
        // new Function() and then takes the resulting function and executes
        // it with a vec of arguments.
        let script = format!("(function() {{ {} }})({})", func_body, args_string);

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id,
                                                           WebDriverScriptCommand::EvaluateJS(script, sender))).unwrap();

        match reciever.recv().unwrap() {
            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
            Err(_) => Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                              "Unsupported return type"))
        }
    }

    fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
        let mut img = None;

        let interval = 20;
        let iterations = 30_000 / interval;

        for _ in 0..iterations {
            let (sender, reciever) = channel();
            let ConstellationChan(ref const_chan) = self.constellation_chan;
            const_chan.send(ConstellationMsg::CompositePng(sender)).unwrap();

            if let Some(x) = reciever.recv().unwrap() {
                img = Some(x);
                break;
            };

            sleep_ms(interval)
        }

        if img.is_none() {
            return Err(WebDriverError::new(ErrorStatus::Timeout,
                                           "Taking screenshot timed out"));
        }

        let img_vec = match png::to_vec(&mut img.unwrap()) {
           Ok(x) => x,
           Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
                                                    "Taking screenshot failed"))
        };
        let config = Config {
            char_set:CharacterSet::Standard,
            newline: Newline::LF,
            pad: true,
            line_length: None
        };
        let encoded = img_vec.to_base64(config);
        Ok(WebDriverResponse::Generic(ValueResponse::new(encoded.to_json())))
    }
}

impl WebDriverHandler for Handler {
    fn handle_command(&mut self, _session: &Option<Session>, msg: &WebDriverMessage) -> WebDriverResult<WebDriverResponse> {

        match msg.command {
            WebDriverCommand::NewSession => self.handle_new_session(),
            WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
            WebDriverCommand::GoBack => self.handle_go_back(),
            WebDriverCommand::GoForward => self.handle_go_forward(),
            WebDriverCommand::GetTitle => self.handle_get_title(),
            WebDriverCommand::GetWindowHandle => self.handle_get_window_handle(),
            WebDriverCommand::GetWindowHandles => self.handle_get_window_handles(),
            WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
            WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
            WebDriverCommand::GetElementText(ref element) => self.handle_get_element_text(element),
            WebDriverCommand::GetElementTagName(ref element) => self.handle_get_element_tag_name(element),
            WebDriverCommand::ExecuteScript(ref x) => self.handle_execute_script(x),
            WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
            _ => Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                         "Command not implemented"))
        }
    }

    fn delete_session(&mut self, _session: &Option<Session>) {
        self.session = None;
    }
}
<commit_msg>Auto merge of #6007 - jgraham:webdriver_capabilities, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![crate_name = "webdriver_server"]
#![crate_type = "rlib"]

#![feature(rustc_private, ip_addr)]

#[macro_use]
extern crate log;

extern crate webdriver;
extern crate msg;
extern crate png;
extern crate url;
extern crate util;
extern crate rustc_serialize;
extern crate uuid;
extern crate webdriver_traits;

use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId, NavigationDirection};
use msg::constellation_msg::Msg as ConstellationMsg;
use std::sync::mpsc::channel;
use webdriver_traits::WebDriverScriptCommand;

use url::Url;
use webdriver::command::{WebDriverMessage, WebDriverCommand};
use webdriver::command::{GetParameters, JavascriptCommandParameters, LocatorParameters};
use webdriver::common::{LocatorStrategy, WebElement};
use webdriver::response::{
    WebDriverResponse, NewSessionResponse, ValueResponse};
use webdriver::server::{self, WebDriverHandler, Session};
use webdriver::error::{WebDriverResult, WebDriverError, ErrorStatus};
use util::task::spawn_named;
use uuid::Uuid;

use std::borrow::ToOwned;
use rustc_serialize::json::{Json, ToJson};
use rustc_serialize::base64::{Config, ToBase64, CharacterSet, Newline};
use std::collections::BTreeMap;
use std::net::SocketAddr;

use std::thread::sleep_ms;

pub fn start_server(port: u16, constellation_chan: ConstellationChan) {
    let handler = Handler::new(constellation_chan);

    spawn_named("WebdriverHttpServer".to_owned(), move || {
        server::start(SocketAddr::new("0.0.0.0".parse().unwrap(), port), handler);
    });
}

struct WebdriverSession {
    id: Uuid
}

struct Handler {
    session: Option<WebdriverSession>,
    constellation_chan: ConstellationChan,
}

impl WebdriverSession {
    pub fn new() -> WebdriverSession {
        WebdriverSession {
            id: Uuid::new_v4()
        }
    }
}

impl Handler {
    pub fn new(constellation_chan: ConstellationChan) -> Handler {
        Handler {
            session: None,
            constellation_chan: constellation_chan
        }
    }

    fn get_root_pipeline(&self) -> PipelineId {
        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::GetRootPipeline(sender)).unwrap();

        reciever.recv().unwrap().unwrap()
    }

    fn handle_new_session(&mut self) -> WebDriverResult<WebDriverResponse> {
        if self.session.is_none() {
            let session = WebdriverSession::new();
            let mut capabilities = BTreeMap::new();
            capabilities.insert("browserName".to_owned(), "servo".to_json());
            capabilities.insert("browserVersion".to_owned(), "0.0.1".to_json());
            let rv = Ok(WebDriverResponse::NewSession(
                NewSessionResponse::new(
                    session.id.to_string(),
                    Json::Object(capabilities))));
            self.session = Some(session);
            rv
        } else {
            Err(WebDriverError::new(ErrorStatus::UnknownError,
                                    "Session already created"))
        }
    }

    fn handle_get(&self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
        let url = match Url::parse(&parameters.url[..]) {
            Ok(url) => url,
            Err(_) => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
                                               "Invalid URL"))
        };

        let pipeline_id = self.get_root_pipeline();

        let load_data = LoadData::new(url);
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap();
        //TODO: Now we ought to wait until we get a load event
        Ok(WebDriverResponse::Void)
    }

    fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Back)).unwrap();
        Ok(WebDriverResponse::Void)
    }

    fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Forward)).unwrap();
        Ok(WebDriverResponse::Void)
    }

    fn handle_get_title(&self) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id,
                                                           WebDriverScriptCommand::GetTitle(sender))).unwrap();
        let value = reciever.recv().unwrap();
        Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json())))
    }

    fn handle_get_window_handle(&self) -> WebDriverResult<WebDriverResponse> {
        // For now we assume there's only one window so just use the session
        // id as the window id
        let handle = self.session.as_ref().unwrap().id.to_string();
        Ok(WebDriverResponse::Generic(ValueResponse::new(handle.to_json())))
    }

    fn handle_get_window_handles(&self) -> WebDriverResult<WebDriverResponse> {
        // For now we assume there's only one window so just use the session
        // id as the window id
        let handles = vec![self.session.as_ref().unwrap().id.to_string().to_json()];
        Ok(WebDriverResponse::Generic(ValueResponse::new(handles.to_json())))
    }

    fn handle_find_element(&self, parameters: &LocatorParameters) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        if parameters.using != LocatorStrategy::CSSSelector {
            return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                           "Unsupported locator strategy"))
        }

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::FindElementCSS(parameters.value.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => {
                Ok(WebDriverResponse::Generic(ValueResponse::new(value.map(|x| WebElement::new(x).to_json()).to_json())))
            }
            Err(_) => Err(WebDriverError::new(ErrorStatus::InvalidSelector,
                                              "Invalid selector"))
        }
    }

    fn handle_find_elements(&self, parameters: &LocatorParameters) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        if parameters.using != LocatorStrategy::CSSSelector {
            return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                           "Unsupported locator strategy"))
        }

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::FindElementsCSS(parameters.value.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => {
                let resp_value: Vec<Json> = value.into_iter().map(
                    |x| WebElement::new(x).to_json()).collect();
                Ok(WebDriverResponse::Generic(ValueResponse::new(resp_value.to_json())))
            }
            Err(_) => Err(WebDriverError::new(ErrorStatus::InvalidSelector,
                                              "Invalid selector"))
        }
    }

    fn handle_get_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::GetElementText(element.id.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
            Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference,
                                              "Unable to find element in document"))
        }
    }

    fn handle_get_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
        let pipeline_id = self.get_root_pipeline();

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        let cmd = WebDriverScriptCommand::GetElementTagName(element.id.clone(), sender);
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id, cmd)).unwrap();
        match reciever.recv().unwrap() {
            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
            Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference,
                                              "Unable to find element in document"))
        }
    }

    fn handle_execute_script(&self, parameters: &JavascriptCommandParameters)  -> WebDriverResult<WebDriverResponse> {
        // TODO: This isn't really right because it always runs the script in the
        // root window
        let pipeline_id = self.get_root_pipeline();

        let func_body = &parameters.script;
        let args_string = "";

        // This is pretty ugly; we really want something that acts like
        // new Function() and then takes the resulting function and executes
        // it with a vec of arguments.
        let script = format!("(function() {{ {} }})({})", func_body, args_string);

        let (sender, reciever) = channel();
        let ConstellationChan(ref const_chan) = self.constellation_chan;
        const_chan.send(ConstellationMsg::WebDriverCommand(pipeline_id,
                                                           WebDriverScriptCommand::EvaluateJS(script, sender))).unwrap();

        match reciever.recv().unwrap() {
            Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
            Err(_) => Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                              "Unsupported return type"))
        }
    }

    fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
        let mut img = None;

        let interval = 20;
        let iterations = 30_000 / interval;

        for _ in 0..iterations {
            let (sender, reciever) = channel();
            let ConstellationChan(ref const_chan) = self.constellation_chan;
            const_chan.send(ConstellationMsg::CompositePng(sender)).unwrap();

            if let Some(x) = reciever.recv().unwrap() {
                img = Some(x);
                break;
            };

            sleep_ms(interval)
        }

        if img.is_none() {
            return Err(WebDriverError::new(ErrorStatus::Timeout,
                                           "Taking screenshot timed out"));
        }

        let img_vec = match png::to_vec(&mut img.unwrap()) {
           Ok(x) => x,
           Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
                                                    "Taking screenshot failed"))
        };
        let config = Config {
            char_set:CharacterSet::Standard,
            newline: Newline::LF,
            pad: true,
            line_length: None
        };
        let encoded = img_vec.to_base64(config);
        Ok(WebDriverResponse::Generic(ValueResponse::new(encoded.to_json())))
    }
}

impl WebDriverHandler for Handler {
    fn handle_command(&mut self, _session: &Option<Session>, msg: &WebDriverMessage) -> WebDriverResult<WebDriverResponse> {

        match msg.command {
            WebDriverCommand::NewSession => self.handle_new_session(),
            WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
            WebDriverCommand::GoBack => self.handle_go_back(),
            WebDriverCommand::GoForward => self.handle_go_forward(),
            WebDriverCommand::GetTitle => self.handle_get_title(),
            WebDriverCommand::GetWindowHandle => self.handle_get_window_handle(),
            WebDriverCommand::GetWindowHandles => self.handle_get_window_handles(),
            WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
            WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
            WebDriverCommand::GetElementText(ref element) => self.handle_get_element_text(element),
            WebDriverCommand::GetElementTagName(ref element) => self.handle_get_element_tag_name(element),
            WebDriverCommand::ExecuteScript(ref x) => self.handle_execute_script(x),
            WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
            _ => Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
                                         "Command not implemented"))
        }
    }

    fn delete_session(&mut self, _session: &Option<Session>) {
        self.session = None;
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A pass that checks to make sure private fields and methods aren't used
//! outside their scopes. This pass will also generate a set of exported items
//! which are available for use externally when compiled as a library.

use util::nodemap::{DefIdSet, FnvHashMap};

use std::hash::Hash;
use std::fmt;
use syntax::ast::NodeId;

// Accessibility levels, sorted in ascending order
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum AccessLevel {
    // Exported items + items participating in various kinds of public interfaces,
    // but not directly nameable. For example, if function `fn f() -> T {...}` is
    // public, then type `T` is exported. Its values can be obtained by other crates
    // even if the type itseld is not nameable.
    // FIXME: Mostly unimplemented. Only `type` aliases export items currently.
    Reachable,
    // Public items + items accessible to other crates with help of `pub use` reexports
    Exported,
    // Items accessible to other crates directly, without help of reexports
    Public,
}

// Accessibility levels for reachable HIR nodes
#[derive(Clone)]
pub struct AccessLevels<Id = NodeId> {
    pub map: FnvHashMap<Id, AccessLevel>
}

impl<Id: Hash + Eq> AccessLevels<Id> {
    pub fn is_reachable(&self, id: Id) -> bool {
        self.map.contains_key(&id)
    }
    pub fn is_exported(&self, id: Id) -> bool {
        self.map.get(&id) >= Some(&AccessLevel::Exported)
    }
    pub fn is_public(&self, id: Id) -> bool {
        self.map.get(&id) >= Some(&AccessLevel::Public)
    }
}

impl<Id: Hash + Eq> Default for AccessLevels<Id> {
    fn default() -> Self {
        AccessLevels { map: Default::default() }
    }
}

impl<Id: Hash + Eq + fmt::Debug> fmt::Debug for AccessLevels<Id> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.map, f)
    }
}

/// A set containing all exported definitions from external crates.
/// The set does not contain any entries from local crates.
pub type ExternalExports = DefIdSet;
<commit_msg>rustc: Fix outdated comment<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A pass that checks to make sure private fields and methods aren't used
//! outside their scopes. This pass will also generate a set of exported items
//! which are available for use externally when compiled as a library.

use util::nodemap::{DefIdSet, FnvHashMap};

use std::hash::Hash;
use std::fmt;
use syntax::ast::NodeId;

// Accessibility levels, sorted in ascending order
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum AccessLevel {
    // Exported items + items participating in various kinds of public interfaces,
    // but not directly nameable. For example, if function `fn f() -> T {...}` is
    // public, then type `T` is reachable. Its values can be obtained by other crates
    // even if the type itself is not nameable.
    Reachable,
    // Public items + items accessible to other crates with help of `pub use` reexports
    Exported,
    // Items accessible to other crates directly, without help of reexports
    Public,
}

// Accessibility levels for reachable HIR nodes
#[derive(Clone)]
pub struct AccessLevels<Id = NodeId> {
    pub map: FnvHashMap<Id, AccessLevel>
}

impl<Id: Hash + Eq> AccessLevels<Id> {
    pub fn is_reachable(&self, id: Id) -> bool {
        self.map.contains_key(&id)
    }
    pub fn is_exported(&self, id: Id) -> bool {
        self.map.get(&id) >= Some(&AccessLevel::Exported)
    }
    pub fn is_public(&self, id: Id) -> bool {
        self.map.get(&id) >= Some(&AccessLevel::Public)
    }
}

impl<Id: Hash + Eq> Default for AccessLevels<Id> {
    fn default() -> Self {
        AccessLevels { map: Default::default() }
    }
}

impl<Id: Hash + Eq + fmt::Debug> fmt::Debug for AccessLevels<Id> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.map, f)
    }
}

/// A set containing all exported definitions from external crates.
/// The set does not contain any entries from local crates.
pub type ExternalExports = DefIdSet;
<|endoftext|>


<commit_before>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{Config, CreateContainerOptions, ListContainersOptions, StartContainerOptions},
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::{
        BuildError as TransportBuilderError, SingleNodeConnectionPool, TransportBuilder,
    },
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Elasticsearch, Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use lazy_static::lazy_static;
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use tokio::time::{sleep, Duration};
use url::Url;

lazy_static! {
    pub static ref AVAILABLE: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

pub async fn initialize() -> Result<MutexGuard<'static, ()>, Error> {
    let guard = AVAILABLE.lock().unwrap();
    let mut docker = DockerWrapper::new();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await?;
    } else {
        docker.cleanup().await?;
    }
    Ok(guard)
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    Connection { source: BollardError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerError { source: BollardError },
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_image: String,
    container_name: String, // ip: String,
}

impl Default for DockerWrapper {
    fn default() -> Self {
        DockerWrapper {
            ports: vec![(9201, 9200), (9301, 9300)],
            docker_image: String::from("docker.elastic.co/elasticsearch/elasticsearch:7.13.0"),
            container_name: String::from("mimir-test-elasticsearch"),
        }
    }
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper::default()
    }

    // Returns true if the container self.container_name is running
    // TODO Probably should run a check on Elasticsearch status
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(Connection)?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        docker.version().await.context(Version)?;

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker.list_containers(options).await.context(DockerError)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(Connection)?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker.list_containers(options).await.context(DockerError)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.container_name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env_vars = vec![String::from("discovery.type=single-node")];

            let config = Config {
                image: Some(self.docker_image.clone()),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env: Some(env_vars),
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerError)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerError)?;

            sleep(Duration::from_secs(5)).await;
        }
        let _ = docker
            .start_container(&self.container_name, None::<StartContainerOptions<String>>)
            .await
            .context(DockerError)?;

        sleep(Duration::from_secs(15)).await;

        Ok(())
    }

    async fn cleanup(&mut self) -> Result<(), Error> {
        let port = self.ports[0].0;
        // FIXME Hardcoded URL, need to extract it from self.
        let url = Url::parse(&format!("http://localhost:{}", port)).context(UrlParse)?;
        let conn_pool = SingleNodeConnectionPool::new(url);
        let transport = TransportBuilder::new(conn_pool)
            .disable_proxy()
            .build()
            .context(ElasticsearchTransport)?;
        let client = Elasticsearch::new(transport);

        let _ = client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .send()
            .await
            .context(ElasticsearchClient)?;

        println!("done with cleanup");
        sleep(Duration::from_secs(1)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.container_name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.container_name, options)
            .await
            .expect("remove container");
    }
}
<commit_msg>Add elasticsearch conn test to docker<commit_after>use bollard::service::{HostConfig, PortBinding};
use bollard::{
    container::{Config, CreateContainerOptions, ListContainersOptions, StartContainerOptions},
    errors::Error as BollardError,
    image::CreateImageOptions,
    Docker,
};
use elasticsearch::{
    http::transport::{
        BuildError as TransportBuilderError, SingleNodeConnectionPool, TransportBuilder,
    },
    indices::{IndicesDeleteAliasParts, IndicesDeleteIndexTemplateParts, IndicesDeleteParts},
    Elasticsearch, Error as ElasticsearchError,
};
use futures::stream::TryStreamExt;
use lazy_static::lazy_static;
use snafu::{ResultExt, Snafu};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, MutexGuard};
use tokio::time::{sleep, Duration};
use url::Url;

use crate::adapters::secondary::elasticsearch::remote::{self, Error as ElasticsearchRemoteError};
use crate::domain::ports::secondary::remote::{Error as RemoteError, Remote};

lazy_static! {
    pub static ref AVAILABLE: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}

// This function takes the mutex related to the availability of the elasticsearch docker container,
// and then creates or reset the container depending on its availability.
// Finally, it tests if elasticsearch is available, before returning
// the mutex guard.
// TODO Document the usage of the mutexguard, how you should avoid running asserts
// while holding the mutex.
pub async fn initialize() -> Result<MutexGuard<'static, ()>, Error> {
    let guard = AVAILABLE.lock().unwrap();
    let mut docker = DockerWrapper::new();
    let is_available = docker.is_container_available().await?;
    if !is_available {
        docker.create_container().await?;
    } else {
        docker.cleanup().await?;
    }
    let is_available = docker.is_container_available().await?;
    if !is_available {
        return Err(Error::Misc {
            msg: format!("Cannot get docker {} available", docker.docker_image),
        });
    }
    let pool = remote::connection_test_pool()
        .await
        .context(ElasticsearchPoolCreation)?;
    let _client = pool.conn().await.context(ElasticsearchConnection)?;

    Ok(guard)
}

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Connection to docker socket: {}", source))]
    DockerConnection { source: BollardError },

    #[snafu(display("Creation of elasticsearch pool: {}", source))]
    ElasticsearchPoolCreation { source: ElasticsearchRemoteError },

    #[snafu(display("Connection to elasticsearch: {}", source))]
    ElasticsearchConnection { source: RemoteError },

    #[snafu(display("docker version: {}", source))]
    Version { source: BollardError },

    #[snafu(display("url parsing error: {}", source))]
    UrlParse { source: url::ParseError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchTransport { source: TransportBuilderError },

    #[snafu(display("elasticsearch transport error: {}", source))]
    ElasticsearchClient { source: ElasticsearchError },

    #[snafu(display("docker error: {}", source))]
    DockerEngine { source: BollardError },

    #[snafu(display("error: {}", msg))]
    Misc { msg: String },
}

pub struct DockerWrapper {
    ports: Vec<(u32, u32)>, // list of ports to publish (host port, container port)
    docker_image: String,
    container_name: String, // ip: String,
}

impl Default for DockerWrapper {
    fn default() -> Self {
        DockerWrapper {
            ports: vec![(9201, 9200), (9301, 9300)],
            docker_image: String::from("docker.elastic.co/elasticsearch/elasticsearch:7.13.0"),
            container_name: String::from("mimir-test-elasticsearch"),
        }
    }
}

impl DockerWrapper {
    pub fn new() -> DockerWrapper {
        DockerWrapper::default()
    }

    // Returns true if the container self.container_name is running
    // TODO Probably should run a check on Elasticsearch status
    pub async fn is_container_available(&mut self) -> Result<bool, Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(DockerConnection)?;

        let docker = &docker.negotiate_version().await.context(Version)?;

        docker.version().await.context(Version)?;

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: false, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        Ok(!containers.is_empty())
    }

    // If the container is already created, then start it.
    // If it is not created, then create it and start it.
    pub async fn create_container(&mut self) -> Result<(), Error> {
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .context(DockerConnection)?;

        let docker = docker.negotiate_version().await.context(Version)?;

        let _ = docker.version().await.context(Version);

        let mut filters = HashMap::new();
        filters.insert("name", vec![self.container_name.as_str()]);

        let options = Some(ListContainersOptions {
            all: true, // only running containers
            filters,
            ..Default::default()
        });

        let containers = docker
            .list_containers(options)
            .await
            .context(DockerEngine)?;

        if containers.is_empty() {
            let options = CreateContainerOptions {
                name: &self.container_name,
            };

            let mut port_bindings = HashMap::new();
            for (host_port, container_port) in self.ports.iter() {
                port_bindings.insert(
                    format!("{}/tcp", &container_port),
                    Some(vec![PortBinding {
                        host_ip: Some(String::from("0.0.0.0")),
                        host_port: Some(host_port.to_string()),
                    }]),
                );
            }

            let host_config = HostConfig {
                port_bindings: Some(port_bindings),
                ..Default::default()
            };

            let mut exposed_ports = HashMap::new();
            self.ports.iter().for_each(|(_, container)| {
                let v: HashMap<(), ()> = HashMap::new();
                exposed_ports.insert(format!("{}/tcp", container), v);
            });

            let env_vars = vec![String::from("discovery.type=single-node")];

            let config = Config {
                image: Some(self.docker_image.clone()),
                exposed_ports: Some(exposed_ports),
                host_config: Some(host_config),
                env: Some(env_vars),
                ..Default::default()
            };

            docker
                .create_image(
                    Some(CreateImageOptions {
                        from_image: self.docker_image.clone(),
                        ..Default::default()
                    }),
                    None,
                    None,
                )
                .try_collect::<Vec<_>>()
                .await
                .context(DockerEngine)?;

            let _ = docker
                .create_container(Some(options), config)
                .await
                .context(DockerEngine)?;

            sleep(Duration::from_secs(5)).await;
        }
        let _ = docker
            .start_container(&self.container_name, None::<StartContainerOptions<String>>)
            .await
            .context(DockerEngine)?;

        sleep(Duration::from_secs(15)).await;

        Ok(())
    }

    async fn cleanup(&mut self) -> Result<(), Error> {
        let port = self.ports[0].0;
        // FIXME Hardcoded URL, need to extract it from self.
        let url = Url::parse(&format!("http://localhost:{}", port)).context(UrlParse)?;
        let conn_pool = SingleNodeConnectionPool::new(url);
        let transport = TransportBuilder::new(conn_pool)
            .disable_proxy()
            .build()
            .context(ElasticsearchTransport)?;
        let client = Elasticsearch::new(transport);

        let _ = client
            .indices()
            .delete(IndicesDeleteParts::Index(&["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_alias(IndicesDeleteAliasParts::IndexName(&["*"], &["*"]))
            .send()
            .await
            .context(ElasticsearchClient)?;

        let _ = client
            .indices()
            .delete_index_template(IndicesDeleteIndexTemplateParts::Name("*"))
            .send()
            .await
            .context(ElasticsearchClient)?;

        println!("done with cleanup");
        sleep(Duration::from_secs(1)).await;
        Ok(())
    }

    async fn _drop(&mut self) {
        if std::env::var("DONT_KILL_THE_WHALE") == Ok("1".to_string()) {
            println!(
                "the docker won't be stoped at the end, you can debug it.
                Note: ES has been mapped to the port 9242 in you localhost
                manually stop and rm the container mimirsbrunn_tests after debug"
            );
            return;
        }
        let docker = Docker::connect_with_unix(
            "unix:///var/run/docker.sock",
            120,
            &bollard::ClientVersion {
                major_version: 1,
                minor_version: 24,
            },
        )
        .expect("docker connection");

        let options = Some(bollard::container::StopContainerOptions { t: 0 });
        docker
            .stop_container(&self.container_name, options)
            .await
            .expect("stop container");

        let options = Some(bollard::container::RemoveContainerOptions {
            force: true,
            ..Default::default()
        });

        let _res = docker
            .remove_container(&self.container_name, options)
            .await
            .expect("remove container");
    }
}
<|endoftext|>


<commit_before>

use serde_yaml as yaml;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct Config {
    pub docs_dir: PathBuf,
    pub cmds_src: PathBuf,
    pub version: String,
    pub menu: Vec<Command>,
}

#[derive(Debug, Clone, Deserialize)]
struct Menu {
    section: Section,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Section {
    pub name: String,
    pub commands: Vec<Command>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Command {
    pub name: String,
    #[serde(default)]
    pub section: String,
    pub permalink: String,
    pub method: Option<String>,
    pub typ: Option<String>,
}

impl Config {
    pub fn new() -> Config {
        let lang = "javascript";

        let root_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
        let docs_dir = format!("{}/build/docs/api/{}", root_dir, lang);
        let menu_file = format!("{}/build/docs/_jekyll/_data/api_{}.yml", root_dir, lang);
        let cmds_src = format!("{}/src/commands/mod.rs", root_dir);
        let version = env::var("CARGO_PKG_VERSION").unwrap();

        let menu = build_menu(&menu_file).into_iter()
            // We are only interested in actual commands
            // that are contained in the sections
            .map(|menu| menu.section)
            // Rename sections
            .map(|mut section| {
                let sections = vec![
                    ("Geospatial commands", "Geospatial"),
                    ("Accessing ReQL", "Accessing RQL"),
                ];
                for (old, new) in sections {
                    if section.name == old {
                        section.name = new.to_owned();
                    }
                }
                section
            })
            // Match filesystem formating of section directories
            .map(|mut section| {
                section.name = section.name
                    .replace(" ", "-")
                    .to_lowercase();
                section
            });

        let mut commands = Vec::new();
        for section in menu {
            for mut command in section.commands {
                command.section = section.name.clone();
                commands.push(command);
            }
        }
        commands = commands.into_iter()
            // Drop blacklisted sections
            .filter(|command| {
                let blacklist = vec!["cursors"];
                for section in blacklist {
                    if section == command.section {
                        return false;
                    }
                }
                true
            })
        // Drop blacklisted commands
        .filter(|command| {
            let blacklist = vec![
                "r", "args", "use", "row", "opt_arg", "array", "object", "close", "reconnect",
                "noreply_wait", "server", "event_emitter", "run",
            ];
            for cmd in blacklist {
                if cmd == command.permalink {
                    return false;
                }
            }
            true
        })
        // Rename special keywords
        .map(|mut command| {
            let keywords = vec!["mod", "match", "do"];
            for cmd in keywords {
                if cmd == command.permalink {
                    command.method = Some(format!("{}_", command.permalink));
                }
            }
            command
        })
        // Rename commands
        .map(|mut command| {
            let names = vec![("to_json_string", "to_json")];
            for (old, new) in names {
                if old == command.permalink {
                    command.method = Some(new.into());
                }
            }
            command
        })
        // Commands with different types
        .map(|mut command| {
            let types = vec![("js", "javascript"), ("do", "funcall")];
            for (cmd, typ) in types {
                if cmd == command.permalink {
                    command.typ = Some(typ.into());
                }
            }
            command
        })
        // Commands in different sections
        .map(|mut command| {
            let menu = vec![("changes", "manipulating-tables")];
            for (cmd, section) in menu {
                if cmd == command.permalink {
                    command.section = section.to_owned();
                }
            }
            command
        })
        .collect();

        Config {
            docs_dir: PathBuf::from(&docs_dir),
            cmds_src: PathBuf::from(&cmds_src),
            version: version,
            menu: commands,
        }
    }
}

fn build_menu(menu_file: &str) -> Vec<Menu> {
    let file = File::open(&menu_file).unwrap();
    let buf = BufReader::new(file);
    yaml::from_reader(buf).unwrap()
}
<commit_msg>delete leading blank lines<commit_after>use serde_yaml as yaml;
use std::env;
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct Config {
    pub docs_dir: PathBuf,
    pub cmds_src: PathBuf,
    pub version: String,
    pub menu: Vec<Command>,
}

#[derive(Debug, Clone, Deserialize)]
struct Menu {
    section: Section,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Section {
    pub name: String,
    pub commands: Vec<Command>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Command {
    pub name: String,
    #[serde(default)]
    pub section: String,
    pub permalink: String,
    pub method: Option<String>,
    pub typ: Option<String>,
}

impl Config {
    pub fn new() -> Config {
        let lang = "javascript";

        let root_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
        let docs_dir = format!("{}/build/docs/api/{}", root_dir, lang);
        let menu_file = format!("{}/build/docs/_jekyll/_data/api_{}.yml", root_dir, lang);
        let cmds_src = format!("{}/src/commands/mod.rs", root_dir);
        let version = env::var("CARGO_PKG_VERSION").unwrap();

        let menu = build_menu(&menu_file).into_iter()
            // We are only interested in actual commands
            // that are contained in the sections
            .map(|menu| menu.section)
            // Rename sections
            .map(|mut section| {
                let sections = vec![
                    ("Geospatial commands", "Geospatial"),
                    ("Accessing ReQL", "Accessing RQL"),
                ];
                for (old, new) in sections {
                    if section.name == old {
                        section.name = new.to_owned();
                    }
                }
                section
            })
            // Match filesystem formating of section directories
            .map(|mut section| {
                section.name = section.name
                    .replace(" ", "-")
                    .to_lowercase();
                section
            });

        let mut commands = Vec::new();
        for section in menu {
            for mut command in section.commands {
                command.section = section.name.clone();
                commands.push(command);
            }
        }
        commands = commands.into_iter()
            // Drop blacklisted sections
            .filter(|command| {
                let blacklist = vec!["cursors"];
                for section in blacklist {
                    if section == command.section {
                        return false;
                    }
                }
                true
            })
        // Drop blacklisted commands
        .filter(|command| {
            let blacklist = vec![
                "r", "args", "use", "row", "opt_arg", "array", "object", "close", "reconnect",
                "noreply_wait", "server", "event_emitter", "run",
            ];
            for cmd in blacklist {
                if cmd == command.permalink {
                    return false;
                }
            }
            true
        })
        // Rename special keywords
        .map(|mut command| {
            let keywords = vec!["mod", "match", "do"];
            for cmd in keywords {
                if cmd == command.permalink {
                    command.method = Some(format!("{}_", command.permalink));
                }
            }
            command
        })
        // Rename commands
        .map(|mut command| {
            let names = vec![("to_json_string", "to_json")];
            for (old, new) in names {
                if old == command.permalink {
                    command.method = Some(new.into());
                }
            }
            command
        })
        // Commands with different types
        .map(|mut command| {
            let types = vec![("js", "javascript"), ("do", "funcall")];
            for (cmd, typ) in types {
                if cmd == command.permalink {
                    command.typ = Some(typ.into());
                }
            }
            command
        })
        // Commands in different sections
        .map(|mut command| {
            let menu = vec![("changes", "manipulating-tables")];
            for (cmd, section) in menu {
                if cmd == command.permalink {
                    command.section = section.to_owned();
                }
            }
            command
        })
        .collect();

        Config {
            docs_dir: PathBuf::from(&docs_dir),
            cmds_src: PathBuf::from(&cmds_src),
            version: version,
            menu: commands,
        }
    }
}

fn build_menu(menu_file: &str) -> Vec<Menu> {
    let file = File::open(&menu_file).unwrap();
    let buf = BufReader::new(file);
    yaml::from_reader(buf).unwrap()
}
<|endoftext|>


<commit_before>#![allow(non_snake_case)]

extern crate livesplit_core;
extern crate libc;

use libc::c_char;
use std::ffi::CStr;
use std::cell::{Cell, RefCell};
use std::mem;

pub mod timer;
pub mod hotkey_system;
pub mod run;
pub mod segment;
pub mod time;
pub mod time_span;
pub mod attempt;
pub mod run_metadata;
pub mod segment_history;
pub mod segment_history_iter;
pub mod segment_history_element;
pub mod title_component;
pub mod title_component_state;
pub mod splits_component;
pub mod splits_component_state;
pub mod timer_component;
pub mod timer_component_state;
pub mod previous_segment_component;
pub mod previous_segment_component_state;
pub mod sum_of_best_component;
pub mod sum_of_best_component_state;
pub mod possible_time_save_component;
pub mod possible_time_save_component_state;
pub mod graph_component;
pub mod graph_component_state;
pub mod run_editor;
pub mod shared_timer;
pub mod timer_read_lock;
pub mod timer_write_lock;

use segment_history_element::SegmentHistoryElement;
use livesplit_core::{Time, TimeSpan};

pub type Json = *const c_char;

thread_local! {
    static OUTPUT_STR: RefCell<String> = RefCell::new(String::new());
    static OUTPUT_VEC: RefCell<Vec<u8>> = RefCell::new(Vec::new());
    static TIME_SPAN: Cell<TimeSpan> = Cell::default();
    static TIME: Cell<Time> = Cell::default();
    static SEGMENT_HISTORY_ELEMENT: Cell<SegmentHistoryElement> = Cell::new(unsafe { mem::uninitialized() });
}

fn output_time_span(time_span: TimeSpan) -> *const TimeSpan {
    TIME_SPAN.with(|output| {
                       output.set(time_span);
                       output.as_ptr() as *const TimeSpan
                   })
}

fn output_time(time: Time) -> *const Time {
    TIME.with(|output| {
                  output.set(time);
                  output.as_ptr() as *const Time
              })
}

fn output_str<S: AsRef<str>>(s: S) -> *const c_char {
    output_str_with(|o| { o.push_str(s.as_ref()); })
}

fn output_str_with<F>(f: F) -> *const c_char
    where F: FnOnce(&mut String)
{
    OUTPUT_STR.with(|output| {
                        let mut output = output.borrow_mut();
                        output.clear();
                        f(&mut output);
                        output.push('\0');
                        output.as_ptr() as *const c_char
                    })
}

fn output_vec<F>(f: F) -> *const c_char
    where F: FnOnce(&mut Vec<u8>)
{
    OUTPUT_VEC.with(|output| {
                        let mut output = output.borrow_mut();
                        output.clear();
                        f(&mut output);
                        output.push(0);
                        output.as_ptr() as *const c_char
                    })
}

unsafe fn str(s: *const c_char) -> &'static str {
    CStr::from_ptr(s).to_str().unwrap()
}

unsafe fn alloc<T>(data: T) -> *mut T {
    Box::into_raw(Box::new(data))
}

unsafe fn own<T>(data: *mut T) -> T {
    *Box::from_raw(data)
}

unsafe fn own_drop<T>(data: *mut T) {
    Box::from_raw(data);
}

unsafe fn acc_mut<T>(data: *mut T) -> &'static mut T {
    &mut *data
}

unsafe fn acc<T>(data: *const T) -> &'static T {
    &*data
}
<commit_msg>Fix Compiler Error on i686 Android<commit_after>#![allow(non_snake_case)]

extern crate livesplit_core;
extern crate libc;

use libc::c_char;
use std::ffi::CStr;
use std::cell::{Cell, RefCell};
use std::mem;

pub mod timer;
pub mod hotkey_system;
pub mod run;
pub mod segment;
pub mod time;
pub mod time_span;
pub mod attempt;
pub mod run_metadata;
pub mod segment_history;
pub mod segment_history_iter;
pub mod segment_history_element;
pub mod title_component;
pub mod title_component_state;
pub mod splits_component;
pub mod splits_component_state;
pub mod timer_component;
pub mod timer_component_state;
pub mod previous_segment_component;
pub mod previous_segment_component_state;
pub mod sum_of_best_component;
pub mod sum_of_best_component_state;
pub mod possible_time_save_component;
pub mod possible_time_save_component_state;
pub mod graph_component;
pub mod graph_component_state;
pub mod run_editor;
pub mod shared_timer;
pub mod timer_read_lock;
pub mod timer_write_lock;

use segment_history_element::SegmentHistoryElement;
use livesplit_core::{Time, TimeSpan};

pub type Json = *const c_char;

thread_local! {
    static OUTPUT_STR: RefCell<String> = RefCell::new(String::new());
    static OUTPUT_VEC: RefCell<Vec<u8>> = RefCell::new(Vec::new());
    static TIME_SPAN: Cell<TimeSpan> = Cell::default();
    static TIME: Cell<Time> = Cell::default();
    static SEGMENT_HISTORY_ELEMENT: Cell<SegmentHistoryElement> = Cell::new(unsafe { mem::uninitialized() });
}

fn output_time_span(time_span: TimeSpan) -> *const TimeSpan {
    TIME_SPAN.with(|output| {
                       output.set(time_span);
                       output.as_ptr() as *const TimeSpan
                   })
}

fn output_time(time: Time) -> *const Time {
    TIME.with(|output| {
                  output.set(time);
                  output.as_ptr() as *const Time
              })
}

fn output_str<S: AsRef<str>>(s: S) -> *const c_char {
    output_str_with(|o| { o.push_str(s.as_ref()); })
}

fn output_str_with<F>(f: F) -> *const c_char
    where F: FnOnce(&mut String)
{
    OUTPUT_STR.with(|output| {
                        let mut output = output.borrow_mut();
                        output.clear();
                        f(&mut output);
                        output.push('\0');
                        output.as_ptr() as *const c_char
                    })
}

fn output_vec<F>(f: F) -> *const c_char
    where F: FnOnce(&mut Vec<u8>)
{
    OUTPUT_VEC.with(|output| {
                        let mut output = output.borrow_mut();
                        output.clear();
                        f(&mut output);
                        output.push(0);
                        output.as_ptr() as *const c_char
                    })
}

unsafe fn str(s: *const c_char) -> &'static str {
    CStr::from_ptr(s as _).to_str().unwrap()
}

unsafe fn alloc<T>(data: T) -> *mut T {
    Box::into_raw(Box::new(data))
}

unsafe fn own<T>(data: *mut T) -> T {
    *Box::from_raw(data)
}

unsafe fn own_drop<T>(data: *mut T) {
    Box::from_raw(data);
}

unsafe fn acc_mut<T>(data: *mut T) -> &'static mut T {
    &mut *data
}

unsafe fn acc<T>(data: *const T) -> &'static T {
    &*data
}
<|endoftext|>