<commit_before>//! Various helpers for Actix applications to use during testing.

use std::{net, thread};
use std::rc::Rc;
use std::sync::mpsc;
use std::str::FromStr;
use std::collections::HashMap;

use actix::{Arbiter, Addr, Syn, System, SystemRunner, msgs};
use bytes::Bytes;
use cookie::Cookie;
use http::{Uri, Method, Version, HeaderMap, HttpTryFrom};
use http::header::{HeaderName, HeaderValue};
use futures::Future;
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;
use net2::TcpBuilder;

use error::Error;
use handler::{Handler, Responder, ReplyItem};
use middleware::Middleware;
use application::{Application, HttpApplication};
use param::Params;
use router::Router;
use payload::Payload;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use server::{HttpServer, IntoHttpHandler, ServerSettings};
use ws::{WsClient, WsClientError, WsClientReader, WsClientWriter};
use client::{ClientRequest, ClientRequestBuilder};

/// The `TestServer` type.
///
/// `TestServer` is very simple test server that simplify process of writing
/// integration tests cases for actix web applications.
///
/// # Examples
///
/// ```rust
/// # extern crate actix;
/// # extern crate actix_web;
/// # use actix_web::*;
/// #
/// # fn my_handler(req: HttpRequest) -> HttpResponse {
/// #     httpcodes::HTTPOk.into()
/// # }
/// #
/// # fn main() {
/// use actix_web::test::TestServer;
///
/// let mut srv = TestServer::new(|app| app.handler(my_handler));
///
/// let req = srv.get().finish().unwrap();
/// let response = srv.execute(req.send()).unwrap();
/// assert!(response.status().is_success());
/// # }
/// ```
pub struct TestServer {
    addr: net::SocketAddr,
    thread: Option<thread::JoinHandle<()>>,
    system: SystemRunner,
    server_sys: Addr<Syn, System>,
}

impl TestServer {

    /// Start new test server
    ///
    /// This method accepts configuration method. You can add
    /// middlewares or set handlers for test application.
    pub fn new<F>(config: F) -> Self
        where F: Sync + Send + 'static + Fn(&mut TestApp<()>),
    {
        TestServer::with_state(||(), config)
    }

    /// Start new test server with application factory
    pub fn with_factory<F, U, H>(factory: F) -> Self
        where F: Fn() -> U + Sync + Send + 'static,
              U: IntoIterator<Item=H> + 'static,
              H: IntoHttpHandler + 'static,
    {
        let (tx, rx) = mpsc::channel();

        // run server in separate thread
        let join = thread::spawn(move || {
            let sys = System::new("actix-test-server");
            let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
            let local_addr = tcp.local_addr().unwrap();
            let tcp = TcpListener::from_listener(tcp, &local_addr, Arbiter::handle()).unwrap();

            HttpServer::new(factory).disable_signals().start_incoming(tcp.incoming(), false);

            tx.send((Arbiter::system(), local_addr)).unwrap();
            let _ = sys.run();
        });

        let (sys, addr) = rx.recv().unwrap();
        TestServer {
            addr: addr,
            thread: Some(join),
            system: System::new("actix-test"),
            server_sys: sys,
        }
    }

    /// Start new test server with custom application state
    ///
    /// This method accepts state factory and configuration method.
    pub fn with_state<S, FS, F>(state: FS, config: F) -> Self
        where S: 'static,
              FS: Sync + Send + 'static + Fn() -> S,
              F: Sync + Send + 'static + Fn(&mut TestApp<S>),
    {
        let (tx, rx) = mpsc::channel();

        // run server in separate thread
        let join = thread::spawn(move || {
            let sys = System::new("actix-test-server");

            let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
            let local_addr = tcp.local_addr().unwrap();
            let tcp = TcpListener::from_listener(tcp, &local_addr, Arbiter::handle()).unwrap();

            HttpServer::new(move || {
                let mut app = TestApp::new(state());
                config(&mut app);
                vec![app]}
            ).disable_signals().start_incoming(tcp.incoming(), false);

            tx.send((Arbiter::system(), local_addr)).unwrap();
            let _ = sys.run();
        });

        let (sys, addr) = rx.recv().unwrap();
        TestServer {
            addr: addr,
            thread: Some(join),
            system: System::new("actix-test"),
            server_sys: sys,
        }
    }

    /// Get firat available unused address
    pub fn unused_addr() -> net::SocketAddr {
        let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
        let socket = TcpBuilder::new_v4().unwrap();
        socket.bind(&addr).unwrap();
        socket.reuse_address(true).unwrap();
        let tcp = socket.to_tcp_listener().unwrap();
        tcp.local_addr().unwrap()
    }

    /// Construct test server url
    pub fn addr(&self) -> net::SocketAddr {
        self.addr
    }

    /// Construct test server url
    pub fn url(&self, uri: &str) -> String {
        if uri.starts_with('/') {
            format!("http://{}{}", self.addr, uri)
        } else {
            format!("http://{}/{}", self.addr, uri)
        }
    }

    /// Stop http server
    fn stop(&mut self) {
        if let Some(handle) = self.thread.take() {
            self.server_sys.do_send(msgs::SystemExit(0));
            let _ = handle.join();
        }
    }

    /// Execute future on current core
    pub fn execute<F, I, E>(&mut self, fut: F) -> Result<I, E>
        where F: Future<Item=I, Error=E>
    {
        self.system.run_until_complete(fut)
    }

    /// Connect to websocket server
    pub fn ws(&mut self) -> Result<(WsClientReader, WsClientWriter), WsClientError> {
        let url = self.url("/");
        self.system.run_until_complete(WsClient::new(url).connect().unwrap())
    }

    /// Create `GET` request
    pub fn get(&self) -> ClientRequestBuilder {
        ClientRequest::get(self.url("/").as_str())
    }

    /// Create `POST` request
    pub fn post(&self) -> ClientRequestBuilder {
        ClientRequest::get(self.url("/").as_str())
    }

    /// Create `HEAD` request
    pub fn head(&self) -> ClientRequestBuilder {
        ClientRequest::head(self.url("/").as_str())
    }

    /// Connect to test http server
    pub fn client(&self, meth: Method, path: &str) -> ClientRequestBuilder {
        ClientRequest::build()
            .method(meth)
            .uri(self.url(path).as_str()).take()
    }
}

impl Drop for TestServer {
    fn drop(&mut self) {
        self.stop()
    }
}

/// Test application helper for testing request handlers.
pub struct TestApp<S=()> {
    app: Option<Application<S>>,
}

impl<S: 'static> TestApp<S> {
    fn new(state: S) -> TestApp<S> {
        let app = Application::with_state(state);
        TestApp{app: Some(app)}
    }

    /// Register handler for "/"
    pub fn handler<H: Handler<S>>(&mut self, handler: H) {
        self.app = Some(self.app.take().unwrap().resource("/", |r| r.h(handler)));
    }

    /// Register handler for "/" with resource middleware
    pub fn handler2<H, M>(&mut self, handler: H, mw: M)
        where H: Handler<S>, M: Middleware<S>
    {
        self.app = Some(self.app.take().unwrap()
                        .resource("/", |r| {
                            r.middleware(mw);
                            r.h(handler)}));
    }

    /// Register middleware
    pub fn middleware<T>(&mut self, mw: T) -> &mut TestApp<S>
        where T: Middleware<S> + 'static
    {
        self.app = Some(self.app.take().unwrap().middleware(mw));
        self
    }
}

impl<S: 'static> IntoHttpHandler for TestApp<S> {
    type Handler = HttpApplication<S>;

    fn into_handler(mut self, settings: ServerSettings) -> HttpApplication<S> {
        self.app.take().unwrap().into_handler(settings)
    }
}

#[doc(hidden)]
impl<S: 'static> Iterator for TestApp<S> {
    type Item = HttpApplication<S>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(mut app) = self.app.take() {
            Some(app.finish())
        } else {
            None
        }
    }
}

/// Test `HttpRequest` builder
///
/// ```rust
/// # extern crate http;
/// # extern crate actix_web;
/// # use http::{header, StatusCode};
/// # use actix_web::*;
/// use actix_web::test::TestRequest;
///
/// fn index(req: HttpRequest) -> HttpResponse {
///     if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
///         httpcodes::HTTPOk.into()
///     } else {
///         httpcodes::HTTPBadRequest.into()
///     }
/// }
///
/// fn main() {
///     let resp = TestRequest::with_header("content-type", "text/plain")
///         .run(index).unwrap();
///     assert_eq!(resp.status(), StatusCode::OK);
///
///     let resp = TestRequest::default()
///         .run(index).unwrap();
///     assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// }
/// ```
pub struct TestRequest<S> {
    state: S,
    version: Version,
    method: Method,
    uri: Uri,
    headers: HeaderMap,
    params: Params<'static>,
    cookies: Option<Vec<Cookie<'static>>>,
    payload: Option<Payload>,
}

impl Default for TestRequest<()> {

    fn default() -> TestRequest<()> {
        TestRequest {
            state: (),
            method: Method::GET,
            uri: Uri::from_str("/").unwrap(),
            version: Version::HTTP_11,
            headers: HeaderMap::new(),
            params: Params::new(),
            cookies: None,
            payload: None,
        }
    }
}

impl TestRequest<()> {

    /// Create TestRequest and set request uri
    pub fn with_uri(path: &str) -> TestRequest<()> {
        TestRequest::default().uri(path)
    }

    /// Create TestRequest and set header
    pub fn with_header<K, V>(key: K, value: V) -> TestRequest<()>
        where HeaderName: HttpTryFrom<K>,
              HeaderValue: HttpTryFrom<V>
    {
        TestRequest::default().header(key, value)
    }
}

impl<S> TestRequest<S> {

    /// Start HttpRequest build process with application state
    pub fn with_state(state: S) -> TestRequest<S> {
        TestRequest {
            state: state,
            method: Method::GET,
            uri: Uri::from_str("/").unwrap(),
            version: Version::HTTP_11,
            headers: HeaderMap::new(),
            params: Params::new(),
            cookies: None,
            payload: None,
        }
    }

    /// Set HTTP version of this request
    pub fn version(mut self, ver: Version) -> Self {
        self.version = ver;
        self
    }

    /// Set HTTP method of this request
    pub fn method(mut self, meth: Method) -> Self {
        self.method = meth;
        self
    }

    /// Set HTTP Uri of this request
    pub fn uri(mut self, path: &str) -> Self {
        self.uri = Uri::from_str(path).unwrap();
        self
    }

    /// Set a header
    pub fn header<K, V>(mut self, key: K, value: V) -> Self
        where HeaderName: HttpTryFrom<K>,
              HeaderValue: HttpTryFrom<V>
    {
        if let Ok(key) = HeaderName::try_from(key) {
            if let Ok(value) = HeaderValue::try_from(value) {
                self.headers.append(key, value);
                return self
            }
        }
        panic!("Can not create header");
    }

    /// Set request path pattern parameter
    pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
        self.params.add(name, value);
        self
    }

    /// Set request payload
    pub fn set_payload(mut self, data: Bytes) -> Self {
        let mut payload = Payload::empty();
        payload.unread_data(data);
        self.payload = Some(payload);
        self
    }

    /// Complete request creation and generate `HttpRequest` instance
    pub fn finish(self) -> HttpRequest<S> {
        let TestRequest { state, method, uri, version, headers, params, cookies, payload } = self;
        let req = HttpRequest::new(method, uri, version, headers, payload);
        req.as_mut().cookies = cookies;
        req.as_mut().params = params;
        let (router, _) = Router::new::<S>("/", ServerSettings::default(), HashMap::new());
        req.with_state(Rc::new(state), router)
    }

    #[cfg(test)]
    /// Complete request creation and generate `HttpRequest` instance
    pub(crate) fn finish_no_router(self) -> HttpRequest<S> {
        let TestRequest { state, method, uri, version, headers, params, cookies, payload } = self;
        let req = HttpRequest::new(method, uri, version, headers, payload);
        req.as_mut().cookies = cookies;
        req.as_mut().params = params;
        req.with_state_no_router(Rc::new(state))
    }

    /// This method generates `HttpRequest` instance and runs handler
    /// with generated request.
    ///
    /// This method panics is handler returns actor or async result.
    pub fn run<H: Handler<S>>(self, mut h: H) ->
        Result<HttpResponse, <<H as Handler<S>>::Result as Responder>::Error>
    {
        let req = self.finish();
        let resp = h.handle(req.clone());

        match resp.respond_to(req.clone_without_state()) {
            Ok(resp) => {
                match resp.into().into() {
                    ReplyItem::Message(resp) => Ok(resp),
                    ReplyItem::Future(_) => panic!("Async handler is not supported."),
                }
            },
            Err(err) => Err(err),
        }
    }

    /// This method generates `HttpRequest` instance and runs handler
    /// with generated request.
    ///
    /// This method panics is handler returns actor.
    pub fn run_async<H, R, F, E>(self, h: H) -> Result<HttpResponse, E>
        where H: Fn(HttpRequest<S>) -> F + 'static,
              F: Future<Item=R, Error=E> + 'static,
              R: Responder<Error=E> + 'static,
              E: Into<Error> + 'static
    {
        let req = self.finish();
        let fut = h(req.clone());

        let mut core = Core::new().unwrap();
        match core.run(fut) {
            Ok(r) => {
                match r.respond_to(req.clone_without_state()) {
                    Ok(reply) => match reply.into().into() {
                        ReplyItem::Message(resp) => Ok(resp),
                        _ => panic!("Nested async replies are not supported"),
                    },
                    Err(e) => Err(e),
                }
            },
            Err(err) => Err(err),
        }
    }
}
<commit_msg>allow to pass different binary data<commit_after>//! Various helpers for Actix applications to use during testing.

use std::{net, thread};
use std::rc::Rc;
use std::sync::mpsc;
use std::str::FromStr;
use std::collections::HashMap;

use actix::{Arbiter, Addr, Syn, System, SystemRunner, msgs};
use cookie::Cookie;
use http::{Uri, Method, Version, HeaderMap, HttpTryFrom};
use http::header::{HeaderName, HeaderValue};
use futures::Future;
use tokio_core::net::TcpListener;
use tokio_core::reactor::Core;
use net2::TcpBuilder;

use body::Binary;
use error::Error;
use handler::{Handler, Responder, ReplyItem};
use middleware::Middleware;
use application::{Application, HttpApplication};
use param::Params;
use router::Router;
use payload::Payload;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use server::{HttpServer, IntoHttpHandler, ServerSettings};
use ws::{WsClient, WsClientError, WsClientReader, WsClientWriter};
use client::{ClientRequest, ClientRequestBuilder};

/// The `TestServer` type.
///
/// `TestServer` is very simple test server that simplify process of writing
/// integration tests cases for actix web applications.
///
/// # Examples
///
/// ```rust
/// # extern crate actix;
/// # extern crate actix_web;
/// # use actix_web::*;
/// #
/// # fn my_handler(req: HttpRequest) -> HttpResponse {
/// #     httpcodes::HTTPOk.into()
/// # }
/// #
/// # fn main() {
/// use actix_web::test::TestServer;
///
/// let mut srv = TestServer::new(|app| app.handler(my_handler));
///
/// let req = srv.get().finish().unwrap();
/// let response = srv.execute(req.send()).unwrap();
/// assert!(response.status().is_success());
/// # }
/// ```
pub struct TestServer {
    addr: net::SocketAddr,
    thread: Option<thread::JoinHandle<()>>,
    system: SystemRunner,
    server_sys: Addr<Syn, System>,
}

impl TestServer {

    /// Start new test server
    ///
    /// This method accepts configuration method. You can add
    /// middlewares or set handlers for test application.
    pub fn new<F>(config: F) -> Self
        where F: Sync + Send + 'static + Fn(&mut TestApp<()>),
    {
        TestServer::with_state(||(), config)
    }

    /// Start new test server with application factory
    pub fn with_factory<F, U, H>(factory: F) -> Self
        where F: Fn() -> U + Sync + Send + 'static,
              U: IntoIterator<Item=H> + 'static,
              H: IntoHttpHandler + 'static,
    {
        let (tx, rx) = mpsc::channel();

        // run server in separate thread
        let join = thread::spawn(move || {
            let sys = System::new("actix-test-server");
            let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
            let local_addr = tcp.local_addr().unwrap();
            let tcp = TcpListener::from_listener(tcp, &local_addr, Arbiter::handle()).unwrap();

            HttpServer::new(factory).disable_signals().start_incoming(tcp.incoming(), false);

            tx.send((Arbiter::system(), local_addr)).unwrap();
            let _ = sys.run();
        });

        let (sys, addr) = rx.recv().unwrap();
        TestServer {
            addr: addr,
            thread: Some(join),
            system: System::new("actix-test"),
            server_sys: sys,
        }
    }

    /// Start new test server with custom application state
    ///
    /// This method accepts state factory and configuration method.
    pub fn with_state<S, FS, F>(state: FS, config: F) -> Self
        where S: 'static,
              FS: Sync + Send + 'static + Fn() -> S,
              F: Sync + Send + 'static + Fn(&mut TestApp<S>),
    {
        let (tx, rx) = mpsc::channel();

        // run server in separate thread
        let join = thread::spawn(move || {
            let sys = System::new("actix-test-server");

            let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
            let local_addr = tcp.local_addr().unwrap();
            let tcp = TcpListener::from_listener(tcp, &local_addr, Arbiter::handle()).unwrap();

            HttpServer::new(move || {
                let mut app = TestApp::new(state());
                config(&mut app);
                vec![app]}
            ).disable_signals().start_incoming(tcp.incoming(), false);

            tx.send((Arbiter::system(), local_addr)).unwrap();
            let _ = sys.run();
        });

        let (sys, addr) = rx.recv().unwrap();
        TestServer {
            addr: addr,
            thread: Some(join),
            system: System::new("actix-test"),
            server_sys: sys,
        }
    }

    /// Get firat available unused address
    pub fn unused_addr() -> net::SocketAddr {
        let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
        let socket = TcpBuilder::new_v4().unwrap();
        socket.bind(&addr).unwrap();
        socket.reuse_address(true).unwrap();
        let tcp = socket.to_tcp_listener().unwrap();
        tcp.local_addr().unwrap()
    }

    /// Construct test server url
    pub fn addr(&self) -> net::SocketAddr {
        self.addr
    }

    /// Construct test server url
    pub fn url(&self, uri: &str) -> String {
        if uri.starts_with('/') {
            format!("http://{}{}", self.addr, uri)
        } else {
            format!("http://{}/{}", self.addr, uri)
        }
    }

    /// Stop http server
    fn stop(&mut self) {
        if let Some(handle) = self.thread.take() {
            self.server_sys.do_send(msgs::SystemExit(0));
            let _ = handle.join();
        }
    }

    /// Execute future on current core
    pub fn execute<F, I, E>(&mut self, fut: F) -> Result<I, E>
        where F: Future<Item=I, Error=E>
    {
        self.system.run_until_complete(fut)
    }

    /// Connect to websocket server
    pub fn ws(&mut self) -> Result<(WsClientReader, WsClientWriter), WsClientError> {
        let url = self.url("/");
        self.system.run_until_complete(WsClient::new(url).connect().unwrap())
    }

    /// Create `GET` request
    pub fn get(&self) -> ClientRequestBuilder {
        ClientRequest::get(self.url("/").as_str())
    }

    /// Create `POST` request
    pub fn post(&self) -> ClientRequestBuilder {
        ClientRequest::get(self.url("/").as_str())
    }

    /// Create `HEAD` request
    pub fn head(&self) -> ClientRequestBuilder {
        ClientRequest::head(self.url("/").as_str())
    }

    /// Connect to test http server
    pub fn client(&self, meth: Method, path: &str) -> ClientRequestBuilder {
        ClientRequest::build()
            .method(meth)
            .uri(self.url(path).as_str()).take()
    }
}

impl Drop for TestServer {
    fn drop(&mut self) {
        self.stop()
    }
}

/// Test application helper for testing request handlers.
pub struct TestApp<S=()> {
    app: Option<Application<S>>,
}

impl<S: 'static> TestApp<S> {
    fn new(state: S) -> TestApp<S> {
        let app = Application::with_state(state);
        TestApp{app: Some(app)}
    }

    /// Register handler for "/"
    pub fn handler<H: Handler<S>>(&mut self, handler: H) {
        self.app = Some(self.app.take().unwrap().resource("/", |r| r.h(handler)));
    }

    /// Register handler for "/" with resource middleware
    pub fn handler2<H, M>(&mut self, handler: H, mw: M)
        where H: Handler<S>, M: Middleware<S>
    {
        self.app = Some(self.app.take().unwrap()
                        .resource("/", |r| {
                            r.middleware(mw);
                            r.h(handler)}));
    }

    /// Register middleware
    pub fn middleware<T>(&mut self, mw: T) -> &mut TestApp<S>
        where T: Middleware<S> + 'static
    {
        self.app = Some(self.app.take().unwrap().middleware(mw));
        self
    }
}

impl<S: 'static> IntoHttpHandler for TestApp<S> {
    type Handler = HttpApplication<S>;

    fn into_handler(mut self, settings: ServerSettings) -> HttpApplication<S> {
        self.app.take().unwrap().into_handler(settings)
    }
}

#[doc(hidden)]
impl<S: 'static> Iterator for TestApp<S> {
    type Item = HttpApplication<S>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(mut app) = self.app.take() {
            Some(app.finish())
        } else {
            None
        }
    }
}

/// Test `HttpRequest` builder
///
/// ```rust
/// # extern crate http;
/// # extern crate actix_web;
/// # use http::{header, StatusCode};
/// # use actix_web::*;
/// use actix_web::test::TestRequest;
///
/// fn index(req: HttpRequest) -> HttpResponse {
///     if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
///         httpcodes::HTTPOk.into()
///     } else {
///         httpcodes::HTTPBadRequest.into()
///     }
/// }
///
/// fn main() {
///     let resp = TestRequest::with_header("content-type", "text/plain")
///         .run(index).unwrap();
///     assert_eq!(resp.status(), StatusCode::OK);
///
///     let resp = TestRequest::default()
///         .run(index).unwrap();
///     assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// }
/// ```
pub struct TestRequest<S> {
    state: S,
    version: Version,
    method: Method,
    uri: Uri,
    headers: HeaderMap,
    params: Params<'static>,
    cookies: Option<Vec<Cookie<'static>>>,
    payload: Option<Payload>,
}

impl Default for TestRequest<()> {

    fn default() -> TestRequest<()> {
        TestRequest {
            state: (),
            method: Method::GET,
            uri: Uri::from_str("/").unwrap(),
            version: Version::HTTP_11,
            headers: HeaderMap::new(),
            params: Params::new(),
            cookies: None,
            payload: None,
        }
    }
}

impl TestRequest<()> {

    /// Create TestRequest and set request uri
    pub fn with_uri(path: &str) -> TestRequest<()> {
        TestRequest::default().uri(path)
    }

    /// Create TestRequest and set header
    pub fn with_header<K, V>(key: K, value: V) -> TestRequest<()>
        where HeaderName: HttpTryFrom<K>,
              HeaderValue: HttpTryFrom<V>
    {
        TestRequest::default().header(key, value)
    }
}

impl<S> TestRequest<S> {

    /// Start HttpRequest build process with application state
    pub fn with_state(state: S) -> TestRequest<S> {
        TestRequest {
            state: state,
            method: Method::GET,
            uri: Uri::from_str("/").unwrap(),
            version: Version::HTTP_11,
            headers: HeaderMap::new(),
            params: Params::new(),
            cookies: None,
            payload: None,
        }
    }

    /// Set HTTP version of this request
    pub fn version(mut self, ver: Version) -> Self {
        self.version = ver;
        self
    }

    /// Set HTTP method of this request
    pub fn method(mut self, meth: Method) -> Self {
        self.method = meth;
        self
    }

    /// Set HTTP Uri of this request
    pub fn uri(mut self, path: &str) -> Self {
        self.uri = Uri::from_str(path).unwrap();
        self
    }

    /// Set a header
    pub fn header<K, V>(mut self, key: K, value: V) -> Self
        where HeaderName: HttpTryFrom<K>,
              HeaderValue: HttpTryFrom<V>
    {
        if let Ok(key) = HeaderName::try_from(key) {
            if let Ok(value) = HeaderValue::try_from(value) {
                self.headers.append(key, value);
                return self
            }
        }
        panic!("Can not create header");
    }

    /// Set request path pattern parameter
    pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
        self.params.add(name, value);
        self
    }

    /// Set request payload
    pub fn set_payload<B: Into<Binary>>(mut self, data: B) -> Self {
        let mut data = data.into();
        let mut payload = Payload::empty();
        payload.unread_data(data.take());
        self.payload = Some(payload);
        self
    }

    /// Complete request creation and generate `HttpRequest` instance
    pub fn finish(self) -> HttpRequest<S> {
        let TestRequest { state, method, uri, version, headers, params, cookies, payload } = self;
        let req = HttpRequest::new(method, uri, version, headers, payload);
        req.as_mut().cookies = cookies;
        req.as_mut().params = params;
        let (router, _) = Router::new::<S>("/", ServerSettings::default(), HashMap::new());
        req.with_state(Rc::new(state), router)
    }

    #[cfg(test)]
    /// Complete request creation and generate `HttpRequest` instance
    pub(crate) fn finish_no_router(self) -> HttpRequest<S> {
        let TestRequest { state, method, uri, version, headers, params, cookies, payload } = self;
        let req = HttpRequest::new(method, uri, version, headers, payload);
        req.as_mut().cookies = cookies;
        req.as_mut().params = params;
        req.with_state_no_router(Rc::new(state))
    }

    /// This method generates `HttpRequest` instance and runs handler
    /// with generated request.
    ///
    /// This method panics is handler returns actor or async result.
    pub fn run<H: Handler<S>>(self, mut h: H) ->
        Result<HttpResponse, <<H as Handler<S>>::Result as Responder>::Error>
    {
        let req = self.finish();
        let resp = h.handle(req.clone());

        match resp.respond_to(req.clone_without_state()) {
            Ok(resp) => {
                match resp.into().into() {
                    ReplyItem::Message(resp) => Ok(resp),
                    ReplyItem::Future(_) => panic!("Async handler is not supported."),
                }
            },
            Err(err) => Err(err),
        }
    }

    /// This method generates `HttpRequest` instance and runs handler
    /// with generated request.
    ///
    /// This method panics is handler returns actor.
    pub fn run_async<H, R, F, E>(self, h: H) -> Result<HttpResponse, E>
        where H: Fn(HttpRequest<S>) -> F + 'static,
              F: Future<Item=R, Error=E> + 'static,
              R: Responder<Error=E> + 'static,
              E: Into<Error> + 'static
    {
        let req = self.finish();
        let fut = h(req.clone());

        let mut core = Core::new().unwrap();
        match core.run(fut) {
            Ok(r) => {
                match r.respond_to(req.clone_without_state()) {
                    Ok(reply) => match reply.into().into() {
                        ReplyItem::Message(resp) => Ok(resp),
                        _ => panic!("Nested async replies are not supported"),
                    },
                    Err(e) => Err(e),
                }
            },
            Err(err) => Err(err),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Patrick Walton <pcwalton@mozilla.com>
// 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.

// src/test.rs

use std::io::MemWriter;
use std::str;
use syntax::parse::lexer;
use syntax::parse;
use syntax::parse::token;
use token::extract_tokens;

use transform::transform_tokens;
use format::{LineToken, Formatter};

fn test_rustfmt(source: &str) -> String {
    // nothing special
    let session = parse::new_parse_sess();
    let filemap = parse::string_to_filemap(&session, source.to_string(), "<stdin>".to_string());
    let mut lexer = lexer::StringReader::new(&session.span_diagnostic, filemap);
    let mut output = MemWriter::new();
    {
        let all_tokens = extract_tokens(&mut lexer);
        match transform_tokens(all_tokens.as_slice(), &session.span_diagnostic) {
            Ok(out_tokens) => {
                let formatter = Formatter::new(out_tokens.as_slice(), &mut output);
                formatter.process();
            },
            Err(e) => fail!("Error in trasformer: {}", e)
        }
    }
    str::from_utf8(output.unwrap().as_slice()).unwrap().to_string()
}

#[test]
fn can_format_a_basic_function() {
    let result = test_rustfmt("fn main() {}");
    assert_eq!(result,
"fn main() {
}
".to_string());
}

#[test]
fn adds_newline_after_attributes() {
    let result = test_rustfmt("#[foo]fn main() {}");
    assert_eq!(result,
"#[foo]
fn main() {
}
".to_string());
}

#[test]
fn adds_newline_after_doc_comments() {
    let result = test_rustfmt("/// The Main function
fn main() {}");
    assert_eq!("/// The Main function
fn main() {
}
".to_string(), result);
}

#[test]
fn adds_newline_after_multiline_doc_comment() {
    let result = test_rustfmt(
"/*! The Main function
* some neat info goes here
* ````
* bleh();
* ````
*/
fn main() {}");
    assert_eq!(result,
"/*! The Main function
* some neat info goes here
* ````
* bleh();
* ````
*/
fn main() {
}
".to_string());
}

#[test]
fn indent_regression_from_port_to_result_api() {
    let input = "#![feature(macro_rules)]
extern crate syntax;
use foo;
mod rustfmt;
#[cfg(test)]
mod test;
pub fn main() {
    foo();
}
";

    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn should_preserve_empty_blocks() {
    let input = "match foo {
    _ => {}
}
";

    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn full_regression() {
    let input = "#![feature(macro_rules)]
extern crate syntax;
use foo;
mod rustfmt;
#[cfg(test)]
mod test;
/// The Main Function
pub fn main() {
    let source = io::stdin().read_to_end().unwrap();
    let source = str::from_utf8(source.as_slice()).unwrap();
    let session = parse::new_parse_sess();
    let filemap = parse::string_to_filemap(&session, source.to_string(), foo.to_string());
    let lexer = lexer::StringReader::new(&session.span_diagnostic, filemap);
    let mut output = io::MemWriter::new();
    {
        let mut formatter = rustfmt::Formatter::new(lexer, &mut output);
        loop {
            match formatter.next_token() {
                Ok(true) => {
                    match formatter.parse_production() {
                        Err(e) => fail!(e),
                        _ => {}
                    }
                },
                Ok(false) => break,
                Err(e) => fail!(e)
            }
        }
    }
    let output = str::from_utf8(output.unwrap().as_slice()).unwrap().to_string();
    print!(bar, output);
}
";

    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn should_preserve_single_empty_lines() {
    let input = "use foo;

fn foo() {

}
";
    assert_eq!("use foo;

fn foo() {

}
".to_string(), test_rustfmt(input));
}

#[test]
fn should_collapse_multiple_blank_lines_into_one() {
    let input = "fn foo() {

}
";
    assert_eq!("fn foo() {

}
".to_string(), test_rustfmt(input));
}

#[test]
fn has_blank_line_should_return_true_for_ws_with_more_than_one_unix_line_ending() {
    use transform::has_blank_line;
    let ws_str = "  \n  \n";
    assert_eq!(true, has_blank_line(ws_str));
}

#[test]
fn has_blank_line_should_return_false_for_ws_with_single_unix_line_ending() {
    use transform::has_blank_line;
    let ws_str = "  \n";
    assert_eq!(false, has_blank_line(ws_str));
}

#[test]
fn has_blank_line_should_return_false_for_just_spaces_and_tabs() {
    use transform::has_blank_line;
    let ws_str = "  \t ";
    assert_eq!(false, has_blank_line(ws_str));
}

#[test]
fn is_token_works() {
    let source = "{}";
    let session = parse::new_parse_sess();
    let filemap = parse::string_to_filemap(&session, source.to_string(), "<stdin>".to_string());
    let mut lexer = lexer::StringReader::new(&session.span_diagnostic, filemap);
    let all_tokens = extract_tokens(&mut lexer);
    let left_brace = LineToken::new(all_tokens[0].clone());
    assert!(left_brace.is_token(&token::LBRACE) == true);
    assert!(left_brace.is_token(&token::RBRACE) == false);
}

#[test]
fn regular_line_comments_on_their_own_line_are_preserved() {
    let input = "// standalone.. ends line
pub fn main() {
    /* blah
     * blah blah blah
     * blah blah blah
     * ends line
    */
    foo(); // ends line
    1 + /* doesn't end line! */ 42
}
";
    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn contains_newline_returns_false_for_ws_token_without_newlines() {
}
<commit_msg>remove un-impl'd test<commit_after>// Copyright (c) 2014 Patrick Walton <pcwalton@mozilla.com>
// 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.

// src/test.rs

use std::io::MemWriter;
use std::str;
use syntax::parse::lexer;
use syntax::parse;
use syntax::parse::token;
use token::extract_tokens;

use transform::transform_tokens;
use format::{LineToken, Formatter};

fn test_rustfmt(source: &str) -> String {
    // nothing special
    let session = parse::new_parse_sess();
    let filemap = parse::string_to_filemap(&session, source.to_string(), "<stdin>".to_string());
    let mut lexer = lexer::StringReader::new(&session.span_diagnostic, filemap);
    let mut output = MemWriter::new();
    {
        let all_tokens = extract_tokens(&mut lexer);
        match transform_tokens(all_tokens.as_slice(), &session.span_diagnostic) {
            Ok(out_tokens) => {
                let formatter = Formatter::new(out_tokens.as_slice(), &mut output);
                formatter.process();
            },
            Err(e) => fail!("Error in trasformer: {}", e)
        }
    }
    str::from_utf8(output.unwrap().as_slice()).unwrap().to_string()
}

#[test]
fn can_format_a_basic_function() {
    let result = test_rustfmt("fn main() {}");
    assert_eq!(result,
"fn main() {
}
".to_string());
}

#[test]
fn adds_newline_after_attributes() {
    let result = test_rustfmt("#[foo]fn main() {}");
    assert_eq!(result,
"#[foo]
fn main() {
}
".to_string());
}

#[test]
fn adds_newline_after_doc_comments() {
    let result = test_rustfmt("/// The Main function
fn main() {}");
    assert_eq!("/// The Main function
fn main() {
}
".to_string(), result);
}

#[test]
fn adds_newline_after_multiline_doc_comment() {
    let result = test_rustfmt(
"/*! The Main function
* some neat info goes here
* ````
* bleh();
* ````
*/
fn main() {}");
    assert_eq!(result,
"/*! The Main function
* some neat info goes here
* ````
* bleh();
* ````
*/
fn main() {
}
".to_string());
}

#[test]
fn indent_regression_from_port_to_result_api() {
    let input = "#![feature(macro_rules)]
extern crate syntax;
use foo;
mod rustfmt;
#[cfg(test)]
mod test;
pub fn main() {
    foo();
}
";

    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn should_preserve_empty_blocks() {
    let input = "match foo {
    _ => {}
}
";

    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn full_regression() {
    let input = "#![feature(macro_rules)]
extern crate syntax;
use foo;
mod rustfmt;
#[cfg(test)]
mod test;
/// The Main Function
pub fn main() {
    let source = io::stdin().read_to_end().unwrap();
    let source = str::from_utf8(source.as_slice()).unwrap();
    let session = parse::new_parse_sess();
    let filemap = parse::string_to_filemap(&session, source.to_string(), foo.to_string());
    let lexer = lexer::StringReader::new(&session.span_diagnostic, filemap);
    let mut output = io::MemWriter::new();
    {
        let mut formatter = rustfmt::Formatter::new(lexer, &mut output);
        loop {
            match formatter.next_token() {
                Ok(true) => {
                    match formatter.parse_production() {
                        Err(e) => fail!(e),
                        _ => {}
                    }
                },
                Ok(false) => break,
                Err(e) => fail!(e)
            }
        }
    }
    let output = str::from_utf8(output.unwrap().as_slice()).unwrap().to_string();
    print!(bar, output);
}
";

    assert_eq!(input.to_string(), test_rustfmt(input));
}

#[test]
fn should_preserve_single_empty_lines() {
    let input = "use foo;

fn foo() {

}
";
    assert_eq!("use foo;

fn foo() {

}
".to_string(), test_rustfmt(input));
}

#[test]
fn should_collapse_multiple_blank_lines_into_one() {
    let input = "fn foo() {

}
";
    assert_eq!("fn foo() {

}
".to_string(), test_rustfmt(input));
}

#[test]
fn has_blank_line_should_return_true_for_ws_with_more_than_one_unix_line_ending() {
    use transform::has_blank_line;
    let ws_str = "  \n  \n";
    assert_eq!(true, has_blank_line(ws_str));
}

#[test]
fn has_blank_line_should_return_false_for_ws_with_single_unix_line_ending() {
    use transform::has_blank_line;
    let ws_str = "  \n";
    assert_eq!(false, has_blank_line(ws_str));
}

#[test]
fn has_blank_line_should_return_false_for_just_spaces_and_tabs() {
    use transform::has_blank_line;
    let ws_str = "  \t ";
    assert_eq!(false, has_blank_line(ws_str));
}

#[test]
fn is_token_works() {
    let source = "{}";
    let session = parse::new_parse_sess();
    let filemap = parse::string_to_filemap(&session, source.to_string(), "<stdin>".to_string());
    let mut lexer = lexer::StringReader::new(&session.span_diagnostic, filemap);
    let all_tokens = extract_tokens(&mut lexer);
    let left_brace = LineToken::new(all_tokens[0].clone());
    assert!(left_brace.is_token(&token::LBRACE) == true);
    assert!(left_brace.is_token(&token::RBRACE) == false);
}

#[test]
fn regular_line_comments_on_their_own_line_are_preserved() {
    let input = "// standalone.. ends line
pub fn main() {
    /* blah
     * blah blah blah
     * blah blah blah
     * ends line
    */
    foo(); // ends line
    1 + /* doesn't end line! */ 42
}
";
    assert_eq!(input.to_string(), test_rustfmt(input));
}
<|endoftext|>


<commit_before>mod skills {
    use skills;
    use path;

    #[test]
    fn test_skills() {
        match ::skills::SkillReader(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(skill_reader) => {
                match skill_reader.read_skill(0) {
                    option::Some(skill) => {
                        assert skill.name == ~"Alchemy";
                        assert skill.clickable == false;
                    },
                    option::None => {
                        fail!(~"No skill at 0");
                    }
                };
                //There should be a skill at #10
                match skill_reader.read_skill(10) {
                    option::Some(_skill) => {
                        ()
                    },
                    option::None => {
                        fail!(~"No skill at 10");
                    }
                };
                //There shouldn't be 100 skills
                match skill_reader.read_skill(100) {
                    option::Some(_skill) => {
                        fail!(~"Skill at 100");
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_all() {
        match ::skills::load_skills(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(skill_list) => {
                assert skill_list.len() >= 49 //49 skills in the original UO
            }
        }
    }
}

mod map {
    use map;
    use path;

    #[test]
    fn test_read_map_statics() {
        match ::map::StaticReader(&path::Path(~"files/staidx0.mul"), &path::Path(~"files/statics0.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(staticReader) => {
                match staticReader.read_block(0) {
                    option::None => {
                        match staticReader.read_block(200 + (200 * 512)) {
                            option::Some(Statics) => {
                                assert(Statics.len() > 0);
                            },
                            option::None => {
                                fail!(~"Expected tile at 200x200 but found none");
                            }
                        }
                    },
                    option::Some(_) => {
                        fail!(~"Found unexpected statics at 0");
                    }
                }
            }
        }
    }
}

mod art {
    use art;
    use art::Tile;
    use path;

    #[test]
    fn test_read_tile() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tileReader) => {
                match tileReader.read_tile(100) {
                    option::Some(tile) => {
                        assert tile.raw_image.len() == 1012;
                        let bitmap = tile.with_transparency(0xF000);
                        assert bitmap.len() == (44*44);
                        assert bitmap[0] == 0xF000;
                        assert bitmap[21] != 0xF000;
                    },
                    option::None => {
                        fail!(~"Couldn't read tile 100")
                    }
                };
                match tileReader.read_tile(0x4000) {
                    option::Some(_tile) => {
                        fail!(~"Read static 0x4000 as tile")
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_static() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_reader) => {
                match tile_reader.read_static(0x4000) {
                    option::Some(static_tile) => {
                        let bitmap = static_tile.with_transparency(0xF000);
                        //warn!("%u, %u, %u", bitmap.len(), static_tile.width as uint, static_tile.height as uint);
                        //assert bitmap.len() == (static_tile.width + static_tile.height) as uint;
                    },
                    option::None => {
                        fail!(~"Couldn't read tile 0x4000");
                    }
                };
            }
        }
    }
}

mod tiledata {
    use path;
    use tiledata;
    #[test]
    fn test_read_map_tile_data() {
        match ::tiledata::TileDataReader::new(&path::Path(~"files/tiledata.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_data_reader) => {
                let mut out = ~[];
                for uint::range(0, 128) |idx| {
                    match tile_data_reader.read_map_tile_data(idx) {
                        option::Some(tile_data) => {
                            out.push(copy tile_data.name)
                        },
                        option::None => {
                            fail!(fmt!("Couldn't read tile %u", idx))
                        }
                    };
                }
                /*io::print(~"TILE DATA: (");
                let mut first = false;
                for out.each |name| {
                    if !first {
                        first = true
                    } else {
                        io::print(~",")
                    }
                    io::print(*name);
                }
                io::println(")");*/
            }
        }
    }

    #[test]
    fn test_read_map_static_data() {
        match ::tiledata::TileDataReader::new(&path::Path(~"files/tiledata.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_data_reader) => {
                let mut out = ~[];
                for uint::range(0, 128) |idx| {
                    match tile_data_reader.read_static_tile_data(idx) {
                        option::Some(tile_data) => {
                            out.push(copy tile_data.name)
                        },
                        option::None => {
                            fail!(fmt!("Couldn't read tile %u", idx))
                        }
                    };
                }
                io::print(~"STATIC DATA: (");
                let mut first = false;
                for out.each |name| {
                    if !first {
                        first = true
                    } else {
                        io::print(~",")
                    }
                    io::print(*name);
                }
                io::println(")");
            }
        }
    }
}
<commit_msg>Static tiles reading<commit_after>mod skills {
    use skills;
    use path;

    #[test]
    fn test_skills() {
        match ::skills::SkillReader(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(skill_reader) => {
                match skill_reader.read_skill(0) {
                    option::Some(skill) => {
                        assert skill.name == ~"Alchemy";
                        assert skill.clickable == false;
                    },
                    option::None => {
                        fail!(~"No skill at 0");
                    }
                };
                //There should be a skill at #10
                match skill_reader.read_skill(10) {
                    option::Some(_skill) => {
                        ()
                    },
                    option::None => {
                        fail!(~"No skill at 10");
                    }
                };
                //There shouldn't be 100 skills
                match skill_reader.read_skill(100) {
                    option::Some(_skill) => {
                        fail!(~"Skill at 100");
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_all() {
        match ::skills::load_skills(&path::Path(~"files/skills.idx"), &path::Path(~"files/skills.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(skill_list) => {
                assert skill_list.len() >= 49 //49 skills in the original UO
            }
        }
    }
}

mod map {
    use map;
    use path;

    #[test]
    fn test_read_map_statics() {
        match ::map::StaticReader(&path::Path(~"files/staidx0.mul"), &path::Path(~"files/statics0.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(staticReader) => {
                match staticReader.read_block(0) {
                    option::None => {
                        match staticReader.read_block(200 + (200 * 512)) {
                            option::Some(Statics) => {
                                assert(Statics.len() > 0);
                            },
                            option::None => {
                                fail!(~"Expected tile at 200x200 but found none");
                            }
                        }
                    },
                    option::Some(_) => {
                        fail!(~"Found unexpected statics at 0");
                    }
                }
            }
        }
    }
}

mod art {
    use art;
    use art::Tile;
    use path;

    #[test]
    fn test_read_tile() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tileReader) => {
                match tileReader.read_tile(100) {
                    option::Some(tile) => {
                        assert tile.raw_image.len() == 1012;
                        let bitmap = tile.with_transparency(0xF000);
                        assert bitmap.len() == (44*44);
                        assert bitmap[0] == 0xF000;
                        assert bitmap[21] != 0xF000;
                    },
                    option::None => {
                        fail!(~"Couldn't read tile 100")
                    }
                };
                match tileReader.read_tile(0x4000) {
                    option::Some(_tile) => {
                        fail!(~"Read static 0x4000 as tile")
                    },
                    option::None => ()
                };
            }
        }
    }

    #[test]
    fn test_read_static() {
        match ::art::TileReader(&path::Path(~"files/artidx.mul"), &path::Path(~"files/art.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_reader) => {
                match tile_reader.read_static(0x4000) {
                    option::Some(static_tile) => {
                        let bitmap = static_tile.with_transparency(0xF000);
                        //warn!("%u, %u, %u", bitmap.len(), static_tile.width as uint, static_tile.height as uint);
                        //assert bitmap.len() == (static_tile.width + static_tile.height) as uint;
                    },
                    option::None => {
                        fail!(~"Couldn't read tile 0x4000");
                    }
                };
            }
        }
    }
}

mod tiledata {
    use path;
    use tiledata;
    #[test]
    fn test_read_map_tile_data() {
        match ::tiledata::TileDataReader::new(&path::Path(~"files/tiledata.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_data_reader) => {
                let mut out = ~[];
                for uint::range(0, 128) |idx| {
                    match tile_data_reader.read_map_tile_data(idx) {
                        option::Some(tile_data) => {
                            out.push(copy tile_data.name)
                        },
                        option::None => {
                            fail!(fmt!("Couldn't read tile %u", idx))
                        }
                    };
                }
                /*io::print(~"TILE DATA: (");
                let mut first = false;
                for out.each |name| {
                    if !first {
                        first = true
                    } else {
                        io::print(~",")
                    }
                    io::print(*name);
                }
                io::println(")");*/
            }
        }
    }

    #[test]
    fn test_read_map_static_data() {
        match ::tiledata::TileDataReader::new(&path::Path(~"files/tiledata.mul")) {
            result::Err(msg) => {
                fail!(msg);
            },
            result::Ok(tile_data_reader) => {
                let mut out = ~[];
                for uint::range(0, 512 * 32) |idx| {
                    match tile_data_reader.read_static_tile_data(idx) {
                        option::Some(tile_data) => {
                            if tile_data.flags & ::tiledata::Unknown2Flag as u32 == 0 {
                                out.push(copy tile_data.name)
                            }
                        },
                        option::None => {
                            fail!(fmt!("Couldn't read tile %u", idx))
                        }
                    };
                }
                /*io::print(~"STATIC DATA: (");
                let mut first = false;
                for out.each |name| {
                    if !first {
                        first = true
                    } else {
                        io::print(~",")
                    }
                    io::print(*name);
                }
                io::println(")");*/
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate serde_json;
extern crate reqwest;
extern crate csv;

use self::iron::prelude::*;
use self::iron::{status, IronResult};
use std::env;
use std::convert::From;
use std::num::ParseIntError;
use std::fs::OpenOptions;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;

pub enum EnvVar {
    Token,
    Name,
    IdFile,
    BearerToken,
    LastTweetFile,
}

pub fn read_env_var(data: &EnvVar) -> String {
    let env_var = match *data {
        EnvVar::Token => "TELEGRAM_BOT_TOKEN",
        EnvVar::Name => "TELEGRAM_BOT_NAME",
        EnvVar::IdFile => "TELEGRAM_BOT_CHAT_ID_FILE",
        EnvVar::BearerToken => "TWITTER_BEARER_TOKEN",
        EnvVar::LastTweetFile => "LAST_TWEET_FILE",
    };
    read_raw_env_var(env_var)
}

fn read_raw_env_var(var: &str) -> String {
    env::var_os(var)
        .expect(&format!("{} must be specified. \
                Did you forget to add it to your .env file?",
                         var))
        .into_string()
        .expect(&format!("{} does not contain a valid UTF8 string", var))
}

pub fn send(chat_id: i32, msg: &str) -> IronResult<reqwest::Response> {
    let url = format!("{}{}{}",
                      "https://api.telegram.org/bot",
                      read_env_var(&EnvVar::Token),
                      "/sendMessage");
    let params = hashmap![
                "chat_id" => format!("{}", chat_id),
                "text" => msg.to_owned(),
            ];
    let client = reqwest::Client::new().map_err(|e| {
                     IronError::new(e,
                                    (status::InternalServerError, "Error setting up HTTP client"))
                 })?;
    client.post(&url)
        .json(&params)
        .send()
        .map_err(|e| IronError::new(e, (status::InternalServerError, "Error sending data")))
}

pub fn broadcast(msg: &str) -> Result<(), BroadcastErr> {
    for id in chat_ids()? {
        send(id, msg)?;
    }
    Ok(())
}

pub fn register(chat_id: i32) -> Result<bool, BroadcastErr> {
    if chat_ids()?.iter().any(|id| *id == chat_id) {
        return Ok(false);
    }
    let id_file = read_env_var(&EnvVar::IdFile);
    let file = OpenOptions::new()
        .append(true)
        .create(true)
        .open(&id_file)
        .expect(&format!("Failed to open file {} in append mode", id_file));
    let mut wtr = csv::WriterBuilder::new().has_headers(false).from_writer(file);

    wtr.write_record(&[format!("{}", chat_id)])?;
    wtr.flush().expect("Failed to flush CSV writer");
    Ok(true)
}

pub fn chat_ids() -> Result<Vec<i32>, BroadcastErr> {
    create_ids_file_if_not_exists();
    let id_file = read_env_var(&EnvVar::IdFile);
    let mut rdr = csv::ReaderBuilder::new()
        .has_headers(false)
        .from_path(id_file)
        .expect("Error creating csv reader. Does the id csv exist?");
    let mut ids = Vec::new();
    for record in rdr.records() {
        ids.push(record?[0].parse::<i32>()?);
    }
    Ok(ids)
}

pub fn create_ids_file_if_not_exists() {
    let id_file = read_env_var(&EnvVar::IdFile);
    create_file_if_not_exists(&id_file);
}

fn create_file_if_not_exists(name: &str) {
    OpenOptions::new()
        .append(true)
        .create(true)
        .open(&name)
        .expect(&format!("Failed to open or create file {}", name));
}

pub fn read_last_tweet_id() -> i64 {
    let filename = read_env_var(&EnvVar::LastTweetFile);
    create_file_if_not_exists(&filename);
    let mut file = File::open(filename).unwrap();
    let mut id = String::new();
    file.read_to_string(&mut id).unwrap();
    id.parse::<i64>().unwrap()
}

pub fn write_last_tweet_id(id: i64) {
    let filename = read_env_var(&EnvVar::LastTweetFile);
    let mut file = File::create(filename).unwrap();
    let content = format!("{}", id).into_bytes();
    file.write_all(&content).unwrap();
}

#[derive(Debug)]
pub enum BroadcastErr {
    Csv(csv::Error),
    Iron(IronError),
    Parse(ParseIntError),
}

impl fmt::Display for BroadcastErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            BroadcastErr::Csv(ref e) => e.fmt(f),
            BroadcastErr::Iron(ref e) => e.fmt(f),
            BroadcastErr::Parse(ref e) => e.fmt(f),
        }
    }
}
impl Error for BroadcastErr {
    fn description(&self) -> &str {
        "Something went wrong while doing csv stuff"
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            BroadcastErr::Csv(ref e) => Some(e),
            BroadcastErr::Iron(ref e) => Some(e),
            BroadcastErr::Parse(ref e) => Some(e),
        }
    }
}

impl From<csv::Error> for BroadcastErr {
    fn from(e: csv::Error) -> Self {
        BroadcastErr::Csv(e)
    }
}

impl From<IronError> for BroadcastErr {
    fn from(e: IronError) -> Self {
        BroadcastErr::Iron(e)
    }
}

impl From<ParseIntError> for BroadcastErr {
    fn from(e: ParseIntError) -> Self {
        BroadcastErr::Parse(e)
    }
}
<commit_msg>Remove unneeded function<commit_after>extern crate iron;
extern crate serde_json;
extern crate reqwest;
extern crate csv;

use self::iron::prelude::*;
use self::iron::{status, IronResult};
use std::env;
use std::convert::From;
use std::num::ParseIntError;
use std::fs::OpenOptions;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;

pub enum EnvVar {
    Token,
    Name,
    IdFile,
    BearerToken,
    LastTweetFile,
}

pub fn read_env_var(data: &EnvVar) -> String {
    let env_var = match *data {
        EnvVar::Token => "TELEGRAM_BOT_TOKEN",
        EnvVar::Name => "TELEGRAM_BOT_NAME",
        EnvVar::IdFile => "TELEGRAM_BOT_CHAT_ID_FILE",
        EnvVar::BearerToken => "TWITTER_BEARER_TOKEN",
        EnvVar::LastTweetFile => "LAST_TWEET_FILE",
    };
    read_raw_env_var(env_var)
}

fn read_raw_env_var(var: &str) -> String {
    env::var_os(var)
        .expect(&format!("{} must be specified. \
                Did you forget to add it to your .env file?",
                         var))
        .into_string()
        .expect(&format!("{} does not contain a valid UTF8 string", var))
}

pub fn send(chat_id: i32, msg: &str) -> IronResult<reqwest::Response> {
    let url = format!("{}{}{}",
                      "https://api.telegram.org/bot",
                      read_env_var(&EnvVar::Token),
                      "/sendMessage");
    let params = hashmap![
                "chat_id" => format!("{}", chat_id),
                "text" => msg.to_owned(),
            ];
    let client = reqwest::Client::new().map_err(|e| {
                     IronError::new(e,
                                    (status::InternalServerError, "Error setting up HTTP client"))
                 })?;
    client.post(&url)
        .json(&params)
        .send()
        .map_err(|e| IronError::new(e, (status::InternalServerError, "Error sending data")))
}

pub fn broadcast(msg: &str) -> Result<(), BroadcastErr> {
    for id in chat_ids()? {
        send(id, msg)?;
    }
    Ok(())
}

pub fn register(chat_id: i32) -> Result<bool, BroadcastErr> {
    if chat_ids()?.iter().any(|id| *id == chat_id) {
        return Ok(false);
    }
    let id_file = read_env_var(&EnvVar::IdFile);
    let file = OpenOptions::new()
        .append(true)
        .create(true)
        .open(&id_file)
        .expect(&format!("Failed to open file {} in append mode", id_file));
    let mut wtr = csv::WriterBuilder::new().has_headers(false).from_writer(file);

    wtr.write_record(&[format!("{}", chat_id)])?;
    wtr.flush().expect("Failed to flush CSV writer");
    Ok(true)
}

pub fn chat_ids() -> Result<Vec<i32>, BroadcastErr> {
    let id_file = read_env_var(&EnvVar::IdFile);
    create_file_if_not_exists(&id_file);
    let mut rdr = csv::ReaderBuilder::new()
        .has_headers(false)
        .from_path(id_file)
        .expect("Error creating csv reader. Does the id csv exist?");
    let mut ids = Vec::new();
    for record in rdr.records() {
        ids.push(record?[0].parse::<i32>()?);
    }
    Ok(ids)
}

fn create_file_if_not_exists(name: &str) {
    OpenOptions::new()
        .append(true)
        .create(true)
        .open(&name)
        .expect(&format!("Failed to open or create file {}", name));
}

pub fn read_last_tweet_id() -> i64 {
    let filename = read_env_var(&EnvVar::LastTweetFile);
    create_file_if_not_exists(&filename);
    let mut file = File::open(filename).unwrap();
    let mut id = String::new();
    file.read_to_string(&mut id).unwrap();
    id.parse::<i64>().unwrap()
}

pub fn write_last_tweet_id(id: i64) {
    let filename = read_env_var(&EnvVar::LastTweetFile);
    let mut file = File::create(filename).unwrap();
    let content = format!("{}", id).into_bytes();
    file.write_all(&content).unwrap();
}

#[derive(Debug)]
pub enum BroadcastErr {
    Csv(csv::Error),
    Iron(IronError),
    Parse(ParseIntError),
}

impl fmt::Display for BroadcastErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            BroadcastErr::Csv(ref e) => e.fmt(f),
            BroadcastErr::Iron(ref e) => e.fmt(f),
            BroadcastErr::Parse(ref e) => e.fmt(f),
        }
    }
}
impl Error for BroadcastErr {
    fn description(&self) -> &str {
        "Something went wrong while doing csv stuff"
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            BroadcastErr::Csv(ref e) => Some(e),
            BroadcastErr::Iron(ref e) => Some(e),
            BroadcastErr::Parse(ref e) => Some(e),
        }
    }
}

impl From<csv::Error> for BroadcastErr {
    fn from(e: csv::Error) -> Self {
        BroadcastErr::Csv(e)
    }
}

impl From<IronError> for BroadcastErr {
    fn from(e: IronError) -> Self {
        BroadcastErr::Iron(e)
    }
}

impl From<ParseIntError> for BroadcastErr {
    fn from(e: ParseIntError) -> Self {
        BroadcastErr::Parse(e)
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
pub enum Void {}
<commit_msg>Fix debug issue with void<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use core::fmt;
pub enum Void {}
impl fmt::Debug for Void {
    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
        unreachable!()
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use {SdpLine, SdpBandwidth, SdpConnection};
use attribute_type::SdpAttribute;
use error::SdpParserError;

#[derive(Clone)]
pub struct SdpMediaLine {
    pub media: SdpMediaValue,
    pub port: u32,
    pub proto: SdpProtocolValue,
    pub formats: SdpFormatList,
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpMediaValue {
    Audio,
    Video,
    Application,
}

impl fmt::Display for SdpMediaValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpMediaValue::Audio => "Audio",
            SdpMediaValue::Video => "Video",
            SdpMediaValue::Application => "Application",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpProtocolValue {
    RtpSavpf,
    UdpTlsRtpSavpf,
    TcpTlsRtpSavpf,
    DtlsSctp,
    UdpDtlsSctp,
    TcpDtlsSctp,
}

impl fmt::Display for SdpProtocolValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpProtocolValue::RtpSavpf => "Rtp/Savpf",
            SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf",
            SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf",
            SdpProtocolValue::DtlsSctp => "Dtls/Sctp",
            SdpProtocolValue::UdpDtlsSctp => "Udp/Dtls/Sctp",
            SdpProtocolValue::TcpDtlsSctp => "Tcp/Dtls/Sctp",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone)]
pub enum SdpFormatList {
    Integers(Vec<u32>),
    Strings(Vec<String>),
}

impl fmt::Display for SdpFormatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SdpFormatList::Integers(ref x) => write!(f, "{:?}", x),
            SdpFormatList::Strings(ref x) => write!(f, "{:?}", x),
        }
    }
}

pub struct SdpMedia {
    media: SdpMediaLine,
    information: Option<String>,
    connection: Option<SdpConnection>,
    bandwidth: Vec<SdpBandwidth>,
    key: Option<String>,
    attribute: Vec<SdpAttribute>,
}

impl SdpMedia {
    pub fn new(media: SdpMediaLine) -> SdpMedia {
        SdpMedia {
            media,
            information: None,
            connection: None,
            bandwidth: Vec::new(),
            key: None,
            attribute: Vec::new(),
        }
    }

    pub fn get_type(&self) -> &SdpMediaValue {
        &self.media.media
    }

    pub fn get_port(&self) -> u32 {
        self.media.port
    }

    pub fn get_proto(&self) -> &SdpProtocolValue {
        &self.media.proto
    }

    pub fn get_formats(&self) -> &SdpFormatList {
        &self.media.formats
    }

    pub fn has_connection(&self) -> bool {
        self.connection.is_some()
    }

    pub fn has_bandwidth(&self) -> bool {
        !self.bandwidth.is_empty()
    }

    pub fn get_bandwidth(&self) -> &Vec<SdpBandwidth> {
        &self.bandwidth
    }

    pub fn has_attributes(&self) -> bool {
        !self.attribute.is_empty()
    }

    pub fn add_attribute(&mut self, attr: SdpAttribute) {
        self.attribute.push(attr)
    }

    pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
        self.bandwidth.push(bw)
    }

    //TODO complain if connection is set already
    pub fn set_connection(&mut self, c: SdpConnection) {
        self.connection = Some(c);
    }

    //TODO complain if information is set already
    pub fn set_information(&mut self, i: String) {
        self.information = Some(i);
    }

    //TODO complain if key is set already
    pub fn set_key(&mut self, k: String) {
        self.key = Some(k);
    }
}
fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserError> {
    Ok(match value.to_lowercase().as_ref() {
           "audio" => SdpMediaValue::Audio,
           "video" => SdpMediaValue::Video,
           "application" => SdpMediaValue::Application,
           _ => {
               return Err(SdpParserError::Unsupported {
                              message: "unsupported media value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_media_token() {
    let audio = parse_media_token("audio");
    assert!(audio.is_ok());
    assert_eq!(audio.unwrap(), SdpMediaValue::Audio);
    let video = parse_media_token("VIDEO");
    assert!(video.is_ok());
    assert_eq!(video.unwrap(), SdpMediaValue::Video);
    let app = parse_media_token("aPplIcatIOn");
    assert!(app.is_ok());
    assert_eq!(app.unwrap(), SdpMediaValue::Application);

    assert!(parse_media_token("").is_err());
    assert!(parse_media_token("foobar").is_err());
}

fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserError> {
    Ok(match value.to_uppercase().as_ref() {
           "RTP/SAVPF" => SdpProtocolValue::RtpSavpf,
           "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
           "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf,
           "DTLS/SCTP" => SdpProtocolValue::DtlsSctp,
           "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp,
           "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp,
           _ => {
               return Err(SdpParserError::Unsupported {
                              message: "unsupported protocol value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_protocol_token() {
    let rtps = parse_protocol_token("rtp/savpf");
    assert!(rtps.is_ok());
    assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavpf);
    let udps = parse_protocol_token("udp/tls/rtp/savpf");
    assert!(udps.is_ok());
    assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf);
    let tcps = parse_protocol_token("TCP/tls/rtp/savpf");
    assert!(tcps.is_ok());
    assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf);
    let dtls = parse_protocol_token("dtLs/ScTP");
    assert!(dtls.is_ok());
    assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp);
    let usctp = parse_protocol_token("udp/DTLS/sctp");
    assert!(usctp.is_ok());
    assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp);
    let tsctp = parse_protocol_token("tcp/dtls/SCTP");
    assert!(tsctp.is_ok());
    assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp);

    assert!(parse_protocol_token("").is_err());
    assert!(parse_protocol_token("foobar").is_err());
}

pub fn parse_media(value: &str) -> Result<SdpLine, SdpParserError> {
    let mv: Vec<&str> = value.split_whitespace().collect();
    if mv.len() < 4 {
        return Err(SdpParserError::Line {
                       message: "media attribute must have at least four tokens".to_string(),
                       line: value.to_string(),
                   });
    }
    let media = parse_media_token(mv[0])?;
    let port = mv[1].parse::<u32>()?;
    if port > 65535 {
        return Err(SdpParserError::Line {
                       message: "media port token is too big".to_string(),
                       line: value.to_string(),
                   });
    }
    let proto = parse_protocol_token(mv[2])?;
    let fmt_slice: &[&str] = &mv[3..];
    let formats = match media {
        SdpMediaValue::Audio | SdpMediaValue::Video => {
            let mut fmt_vec: Vec<u32> = vec![];
            for num in fmt_slice {
                let fmt_num = num.parse::<u32>()?;
                match fmt_num {
                    0  |  // PCMU
                    8  |  // PCMA
                    9  |  // G722
                    13 |  // Comfort Noise
                    96 ... 127 => (),  // dynamic range
                    _ => return Err(SdpParserError::Line {
                          message: "format number in media line is out of range".to_string(),
                          line: value.to_string() }),
                };
                fmt_vec.push(fmt_num);
            }
            SdpFormatList::Integers(fmt_vec)
        }
        SdpMediaValue::Application => {
            let mut fmt_vec: Vec<String> = vec![];
            // TODO enforce length == 1 and content 'webrtc-datachannel' only?
            for token in fmt_slice {
                fmt_vec.push(String::from(*token));
            }
            SdpFormatList::Strings(fmt_vec)
        }
    };
    let m = SdpMediaLine {
        media,
        port,
        proto,
        formats,
    };
    println!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
    Ok(SdpLine::Media(m))
}

#[test]
fn test_media_works() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
    assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
    assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok());

    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok());
    assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok());
}

#[test]
fn test_media_missing_token() {
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
}

#[test]
fn test_media_invalid_port_number() {
    assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_type() {
    assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_transport() {
    assert!(parse_media("audio 9 invalid/invalid 8").is_err());
}

#[test]
fn test_media_invalid_payload() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
}

pub fn parse_media_vector(lines: &[SdpLine]) -> Result<Vec<SdpMedia>, SdpParserError> {
    let mut media_sections: Vec<SdpMedia> = Vec::new();
    let mut sdp_media = match lines[0] {
        SdpLine::Media(ref v) => SdpMedia::new(v.clone()),
        _ => {
            return Err(SdpParserError::Sequence {
                           message: "first line in media section needs to be a media line"
                               .to_string(),
                           line: None,
                       })
        }
    };
    for line in lines.iter().skip(1) {
        match *line {
            SdpLine::Information(ref v) => sdp_media.set_information(v.clone()),
            SdpLine::Connection(ref v) => sdp_media.set_connection(v.clone()),
            SdpLine::Bandwidth(ref v) => {
                sdp_media.add_bandwidth(v.clone());
            }
            SdpLine::Key(ref v) => sdp_media.set_key(v.clone()),
            SdpLine::Attribute(ref v) => {
                sdp_media.add_attribute(v.clone());
            }
            SdpLine::Media(ref v) => {
                media_sections.push(sdp_media);
                sdp_media = SdpMedia::new(v.clone());
            }

            SdpLine::Email(_) |
            SdpLine::Phone(_) |
            SdpLine::Origin(_) |
            SdpLine::Repeat(_) |
            SdpLine::Session(_) |
            SdpLine::Timing(_) |
            SdpLine::Uri(_) |
            SdpLine::Version(_) |
            SdpLine::Zone(_) => {
                return Err(SdpParserError::Sequence {
                               message: "invalid type in media section".to_string(),
                               line: None,
                           })
            }
        };
    }
    media_sections.push(sdp_media);
    Ok(media_sections)
}
// TODO add unit tests for parse_media_vector
<commit_msg>added support for port counts (#50)<commit_after>use std::fmt;
use {SdpLine, SdpBandwidth, SdpConnection};
use attribute_type::SdpAttribute;
use error::SdpParserError;

#[derive(Clone)]
pub struct SdpMediaLine {
    pub media: SdpMediaValue,
    pub port: u32,
    pub port_count: u32,
    pub proto: SdpProtocolValue,
    pub formats: SdpFormatList,
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpMediaValue {
    Audio,
    Video,
    Application,
}

impl fmt::Display for SdpMediaValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpMediaValue::Audio => "Audio",
            SdpMediaValue::Video => "Video",
            SdpMediaValue::Application => "Application",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone,Debug,PartialEq)]
pub enum SdpProtocolValue {
    RtpSavpf,
    UdpTlsRtpSavpf,
    TcpTlsRtpSavpf,
    DtlsSctp,
    UdpDtlsSctp,
    TcpDtlsSctp,
}

impl fmt::Display for SdpProtocolValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let printable = match *self {
            SdpProtocolValue::RtpSavpf => "Rtp/Savpf",
            SdpProtocolValue::UdpTlsRtpSavpf => "Udp/Tls/Rtp/Savpf",
            SdpProtocolValue::TcpTlsRtpSavpf => "Tcp/Tls/Rtp/Savpf",
            SdpProtocolValue::DtlsSctp => "Dtls/Sctp",
            SdpProtocolValue::UdpDtlsSctp => "Udp/Dtls/Sctp",
            SdpProtocolValue::TcpDtlsSctp => "Tcp/Dtls/Sctp",
        };
        write!(f, "{}", printable)
    }
}

#[derive(Clone)]
pub enum SdpFormatList {
    Integers(Vec<u32>),
    Strings(Vec<String>),
}

impl fmt::Display for SdpFormatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SdpFormatList::Integers(ref x) => write!(f, "{:?}", x),
            SdpFormatList::Strings(ref x) => write!(f, "{:?}", x),
        }
    }
}

pub struct SdpMedia {
    media: SdpMediaLine,
    information: Option<String>,
    connection: Option<SdpConnection>,
    bandwidth: Vec<SdpBandwidth>,
    key: Option<String>,
    attribute: Vec<SdpAttribute>,
}

impl SdpMedia {
    pub fn new(media: SdpMediaLine) -> SdpMedia {
        SdpMedia {
            media,
            information: None,
            connection: None,
            bandwidth: Vec::new(),
            key: None,
            attribute: Vec::new(),
        }
    }

    pub fn get_type(&self) -> &SdpMediaValue {
        &self.media.media
    }

    pub fn get_port(&self) -> u32 {
        self.media.port
    }

    pub fn get_proto(&self) -> &SdpProtocolValue {
        &self.media.proto
    }

    pub fn get_formats(&self) -> &SdpFormatList {
        &self.media.formats
    }

    pub fn has_connection(&self) -> bool {
        self.connection.is_some()
    }

    pub fn has_bandwidth(&self) -> bool {
        !self.bandwidth.is_empty()
    }

    pub fn get_bandwidth(&self) -> &Vec<SdpBandwidth> {
        &self.bandwidth
    }

    pub fn has_attributes(&self) -> bool {
        !self.attribute.is_empty()
    }

    pub fn add_attribute(&mut self, attr: SdpAttribute) {
        self.attribute.push(attr)
    }

    pub fn add_bandwidth(&mut self, bw: SdpBandwidth) {
        self.bandwidth.push(bw)
    }

    //TODO complain if connection is set already
    pub fn set_connection(&mut self, c: SdpConnection) {
        self.connection = Some(c);
    }

    //TODO complain if information is set already
    pub fn set_information(&mut self, i: String) {
        self.information = Some(i);
    }

    //TODO complain if key is set already
    pub fn set_key(&mut self, k: String) {
        self.key = Some(k);
    }
}
fn parse_media_token(value: &str) -> Result<SdpMediaValue, SdpParserError> {
    Ok(match value.to_lowercase().as_ref() {
           "audio" => SdpMediaValue::Audio,
           "video" => SdpMediaValue::Video,
           "application" => SdpMediaValue::Application,
           _ => {
               return Err(SdpParserError::Unsupported {
                              message: "unsupported media value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_media_token() {
    let audio = parse_media_token("audio");
    assert!(audio.is_ok());
    assert_eq!(audio.unwrap(), SdpMediaValue::Audio);
    let video = parse_media_token("VIDEO");
    assert!(video.is_ok());
    assert_eq!(video.unwrap(), SdpMediaValue::Video);
    let app = parse_media_token("aPplIcatIOn");
    assert!(app.is_ok());
    assert_eq!(app.unwrap(), SdpMediaValue::Application);

    assert!(parse_media_token("").is_err());
    assert!(parse_media_token("foobar").is_err());
}

fn parse_protocol_token(value: &str) -> Result<SdpProtocolValue, SdpParserError> {
    Ok(match value.to_uppercase().as_ref() {
           "RTP/SAVPF" => SdpProtocolValue::RtpSavpf,
           "UDP/TLS/RTP/SAVPF" => SdpProtocolValue::UdpTlsRtpSavpf,
           "TCP/TLS/RTP/SAVPF" => SdpProtocolValue::TcpTlsRtpSavpf,
           "DTLS/SCTP" => SdpProtocolValue::DtlsSctp,
           "UDP/DTLS/SCTP" => SdpProtocolValue::UdpDtlsSctp,
           "TCP/DTLS/SCTP" => SdpProtocolValue::TcpDtlsSctp,
           _ => {
               return Err(SdpParserError::Unsupported {
                              message: "unsupported protocol value".to_string(),
                              line: value.to_string(),
                          })
           }
       })
}

#[test]
fn test_parse_protocol_token() {
    let rtps = parse_protocol_token("rtp/savpf");
    assert!(rtps.is_ok());
    assert_eq!(rtps.unwrap(), SdpProtocolValue::RtpSavpf);
    let udps = parse_protocol_token("udp/tls/rtp/savpf");
    assert!(udps.is_ok());
    assert_eq!(udps.unwrap(), SdpProtocolValue::UdpTlsRtpSavpf);
    let tcps = parse_protocol_token("TCP/tls/rtp/savpf");
    assert!(tcps.is_ok());
    assert_eq!(tcps.unwrap(), SdpProtocolValue::TcpTlsRtpSavpf);
    let dtls = parse_protocol_token("dtLs/ScTP");
    assert!(dtls.is_ok());
    assert_eq!(dtls.unwrap(), SdpProtocolValue::DtlsSctp);
    let usctp = parse_protocol_token("udp/DTLS/sctp");
    assert!(usctp.is_ok());
    assert_eq!(usctp.unwrap(), SdpProtocolValue::UdpDtlsSctp);
    let tsctp = parse_protocol_token("tcp/dtls/SCTP");
    assert!(tsctp.is_ok());
    assert_eq!(tsctp.unwrap(), SdpProtocolValue::TcpDtlsSctp);

    assert!(parse_protocol_token("").is_err());
    assert!(parse_protocol_token("foobar").is_err());
}

pub fn parse_media(value: &str) -> Result<SdpLine, SdpParserError> {
    let mv: Vec<&str> = value.split_whitespace().collect();
    if mv.len() < 4 {
        return Err(SdpParserError::Line {
                       message: "media attribute must have at least four tokens".to_string(),
                       line: value.to_string(),
                   });
    }
    let media = parse_media_token(mv[0])?;
    let mut ptokens = mv[1].split('/');
    let port = match ptokens.next() {
        None => {
            return Err(SdpParserError::Line {
                           message: "missing port token".to_string(),
                           line: value.to_string(),
                       })
        }
        Some(p) => p.parse::<u32>()?,
    };
    if port > 65535 {
        return Err(SdpParserError::Line {
                       message: "media port token is too big".to_string(),
                       line: value.to_string(),
                   });
    }
    let port_count = match ptokens.next() {
        None => 0,
        Some(c) => c.parse::<u32>()?,
    };
    let proto = parse_protocol_token(mv[2])?;
    let fmt_slice: &[&str] = &mv[3..];
    let formats = match media {
        SdpMediaValue::Audio | SdpMediaValue::Video => {
            let mut fmt_vec: Vec<u32> = vec![];
            for num in fmt_slice {
                let fmt_num = num.parse::<u32>()?;
                match fmt_num {
                    0  |  // PCMU
                    8  |  // PCMA
                    9  |  // G722
                    13 |  // Comfort Noise
                    96 ... 127 => (),  // dynamic range
                    _ => return Err(SdpParserError::Line {
                          message: "format number in media line is out of range".to_string(),
                          line: value.to_string() }),
                };
                fmt_vec.push(fmt_num);
            }
            SdpFormatList::Integers(fmt_vec)
        }
        SdpMediaValue::Application => {
            let mut fmt_vec: Vec<String> = vec![];
            // TODO enforce length == 1 and content 'webrtc-datachannel' only?
            for token in fmt_slice {
                fmt_vec.push(String::from(*token));
            }
            SdpFormatList::Strings(fmt_vec)
        }
    };
    let m = SdpMediaLine {
        media,
        port,
        port_count,
        proto,
        formats,
    };
    println!("media: {}, {}, {}, {}", m.media, m.port, m.proto, m.formats);
    Ok(SdpLine::Media(m))
}

#[test]
fn test_media_works() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109").is_ok());
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF 126").is_ok());
    assert!(parse_media("application 9 DTLS/SCTP 5000").is_ok());
    assert!(parse_media("application 9 UDP/DTLS/SCTP webrtc-datachannel").is_ok());

    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8").is_ok());
    assert!(parse_media("audio 0 UDP/TLS/RTP/SAVPF 8").is_ok());
    assert!(parse_media("audio 9/2 UDP/TLS/RTP/SAVPF 8").is_ok());
}

#[test]
fn test_media_missing_token() {
    assert!(parse_media("video 9 UDP/TLS/RTP/SAVPF").is_err());
}

#[test]
fn test_media_invalid_port_number() {
    assert!(parse_media("video 75123 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_type() {
    assert!(parse_media("invalid 9 UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_port() {
    assert!(parse_media("audio / UDP/TLS/RTP/SAVPF 8").is_err());
}

#[test]
fn test_media_invalid_transport() {
    assert!(parse_media("audio 9 invalid/invalid 8").is_err());
}

#[test]
fn test_media_invalid_payload() {
    assert!(parse_media("audio 9 UDP/TLS/RTP/SAVPF 300").is_err());
}

pub fn parse_media_vector(lines: &[SdpLine]) -> Result<Vec<SdpMedia>, SdpParserError> {
    let mut media_sections: Vec<SdpMedia> = Vec::new();
    let mut sdp_media = match lines[0] {
        SdpLine::Media(ref v) => SdpMedia::new(v.clone()),
        _ => {
            return Err(SdpParserError::Sequence {
                           message: "first line in media section needs to be a media line"
                               .to_string(),
                           line: None,
                       })
        }
    };
    for line in lines.iter().skip(1) {
        match *line {
            SdpLine::Information(ref v) => sdp_media.set_information(v.clone()),
            SdpLine::Connection(ref v) => sdp_media.set_connection(v.clone()),
            SdpLine::Bandwidth(ref v) => {
                sdp_media.add_bandwidth(v.clone());
            }
            SdpLine::Key(ref v) => sdp_media.set_key(v.clone()),
            SdpLine::Attribute(ref v) => {
                sdp_media.add_attribute(v.clone());
            }
            SdpLine::Media(ref v) => {
                media_sections.push(sdp_media);
                sdp_media = SdpMedia::new(v.clone());
            }

            SdpLine::Email(_) |
            SdpLine::Phone(_) |
            SdpLine::Origin(_) |
            SdpLine::Repeat(_) |
            SdpLine::Session(_) |
            SdpLine::Timing(_) |
            SdpLine::Uri(_) |
            SdpLine::Version(_) |
            SdpLine::Zone(_) => {
                return Err(SdpParserError::Sequence {
                               message: "invalid type in media section".to_string(),
                               line: None,
                           })
            }
        };
    }
    media_sections.push(sdp_media);
    Ok(media_sections)
}
// TODO add unit tests for parse_media_vector
<|endoftext|>