<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::{
    collections::{HashMap, HashSet},
    sync::{atomic::AtomicI8, atomic::Ordering, Arc, RwLock},
};

use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use graphql_ir::Program;
use interner::StringKey;
use log::{debug, info};
use lsp_server::Message;
use relay_compiler::{
    compiler::Compiler, compiler_state::CompilerState, config::Config, errors::Error, FileSource,
    FileSourceResult, FileSourceSubscription, FileSourceSubscriptionNextChange,
};
use schema::SDLSchema;
use tokio::{sync::Notify, task, task::JoinHandle};

use crate::{
    diagnostic_reporter::DiagnosticReporter,
    lsp::set_ready_status,
    lsp::update_in_progress_status,
    lsp_process_error::{LSPProcessError, LSPProcessResult},
};

#[derive(Clone, Default)]
/// This structure is representing the state of the current source control update
/// Watchman subscription will trigger updates here
struct SourceControlUpdateStatus {
    // default - no updates
    // 1 - update started
    // 2 - update completed
    value: Arc<AtomicI8>,
}

impl SourceControlUpdateStatus {
    fn mark_as_started(&self) {
        debug!("SourceControlUpdateStatus: source control update started!");
        self.value.store(1, Ordering::Relaxed);
    }
    fn is_started(&self) -> bool {
        self.value.load(Ordering::Relaxed) == 1
    }
    fn mark_as_completed(&self) {
        debug!("SourceControlUpdateStatus: source control update completed!");
        self.value.store(2, Ordering::Relaxed);
    }
    fn is_completed(&self) -> bool {
        self.value.load(Ordering::Relaxed) == 2
    }
}

/// This structure is responsible for keeping schemas/programs in sync with the current state of the world
pub(crate) struct LSPStateResources<TPerfLogger: PerfLogger + 'static> {
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    schemas: Arc<RwLock<HashMap<StringKey, Arc<SDLSchema>>>>,
    source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
    compiler: Compiler<TPerfLogger>,
    errors: Arc<RwLock<Vec<String>>>,
    source_code_update_status: SourceControlUpdateStatus,
    notify_sender: Arc<Notify>,
    notify_receiver: Arc<Notify>,
    sender: Sender<Message>,
    diagnostic_reporter: DiagnosticReporter,
}

impl<TPerfLogger: PerfLogger + 'static> LSPStateResources<TPerfLogger> {
    pub(crate) fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        schemas: Arc<RwLock<HashMap<StringKey, Arc<SDLSchema>>>>,
        source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
        sender: Sender<Message>,
    ) -> Self {
        let compiler = Compiler::new(Arc::clone(&config), Arc::clone(&perf_logger));
        let notify_sender = Arc::new(Notify::new());
        let notify_receiver = notify_sender.clone();

        let diagnostic_reporter = DiagnosticReporter::new(config.root_dir.clone(), sender.clone());

        Self {
            config,
            perf_logger,
            schemas,
            source_programs,
            compiler,
            errors: Default::default(),
            sender,
            source_code_update_status: Default::default(),
            notify_sender,
            notify_receiver,
            diagnostic_reporter,
        }
    }

    /// Create an end-less loop of keeping the resources up-to-date with the source control changes
    pub(crate) async fn watch(&self) -> LSPProcessResult<()> {
        'outer: loop {
            info!("Initializing resources for LSP server");

            update_in_progress_status(
                "Relay: watchman...",
                Some("Sending watchman query to get source files and possible saved state"),
                &self.sender,
            );
            let setup_event = self
                .perf_logger
                .create_event("lsp_state_initialize_resources");
            let timer = setup_event.start("lsp_state_initialize_resources_time");

            let file_source = FileSource::connect(&self.config, &setup_event)
                .await
                .map_err(LSPProcessError::CompilerError)?;
            let (mut compiler_state, file_source_subscription) = file_source
                .subscribe(&setup_event, self.perf_logger.as_ref())
                .await
                .map_err(LSPProcessError::CompilerError)?;

            let pending_file_source_changes =
                Arc::clone(&compiler_state.pending_file_source_changes);

            // Start a separate task to receive watchman subscription updates
            // this process will notify, when the change is detected.
            // If watchman detects the source control update, it will update the state
            // of the source_control_update_status, so we can cancel building schemas/
            // and programs earlier
            let subscription_handle = self.watchman_subscription_handler(
                file_source_subscription,
                pending_file_source_changes,
            );
            update_in_progress_status(
                "Relay: creating state...",
                Some("Building schemas and source programs for LSP"),
                &self.sender,
            );

            self.diagnostic_reporter.clear_diagnostics();

            // Run initial build, before entering the watch changes loop
            self.initial_build(&compiler_state, &setup_event)?;
            compiler_state.complete_compilation();

            setup_event.stop(timer);
            self.perf_logger.complete_event(setup_event);

            self.log_errors("lsp_state_error");
            self.clear_errors();
            self.diagnostic_reporter.commit_diagnostics();

            info!("LSP server initialization completed!");

            set_ready_status(&self.sender);

            // Here we will wait for changes from watchman
            'inner: loop {
                self.notify_receiver.notified().await;

                // Source control update started, we can ignore all pending changes, and wait for it to complete,
                // we may change the status bar to `Source Control Update...`
                if self.source_code_update_status.is_started() {
                    update_in_progress_status(
                        "Relay: hg update...",
                        Some("Waiting for source control update"),
                        &self.sender,
                    );
                    break 'inner;
                }

                // SC Update completed, we need to abort current subscription, and re-initialize resource for LSP
                if self.source_code_update_status.is_completed() {
                    debug!("Watchman indicated the the source control update has completed!");
                    subscription_handle.abort();
                    continue 'outer;
                }

                let log_event = self.perf_logger.create_event("lsp_state_watchman_event");
                let log_time = log_event.start("lsp_state_watchman_event_time");

                let has_new_changes = compiler_state
                    .merge_file_source_changes(
                        &self.config,
                        &log_event,
                        self.perf_logger.as_ref(),
                        false,
                    )
                    .unwrap();

                // If changes contains schema files we need to rebuild schemas
                if has_new_changes {
                    self.diagnostic_reporter.clear_diagnostics();

                    update_in_progress_status(
                        "Relay: checking...",
                        Some(
                            "Validating changes, and updating source programs with the latest changes.",
                        ),
                        &self.sender,
                    );

                    if compiler_state.has_schema_changes() {
                        self.build_schemas(&compiler_state, &log_event)?;
                    }

                    self.build_source_programs(
                        &compiler_state,
                        Some(self.get_affected_projects(&compiler_state)),
                        &log_event,
                    )?;

                    compiler_state.complete_compilation();

                    self.log_errors("lsp_state_user_error");
                    self.clear_errors();
                    self.diagnostic_reporter.commit_diagnostics();

                    set_ready_status(&self.sender);
                }

                log_event.stop(log_time);
                self.perf_logger.complete_event(log_event);
                self.perf_logger.flush();
            }
        }
    }

    fn initial_build(
        &self,
        compiler_state: &CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> LSPProcessResult<()> {
        debug!("Initial build started...");
        // TODO: (from https://fburl.com/diff/m8jg14sy) - pass source code update status to
        // `compiler.build_schemas` and `compiler.build_source_programs` to cancel build earlier,
        // if update detected
        self.build_schemas(compiler_state, log_event)?;
        self.build_source_programs(compiler_state, None, log_event)?;

        Ok(())
    }

    fn build_schemas(
        &self,
        compiler_state: &CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> LSPProcessResult<()> {
        debug!("Building schemas");
        let timer = log_event.start("build_schemas");

        // Stop building programs if we detect source code update
        if self.source_code_update_status.is_started() {
            return Ok(());
        }

        let (next_schemas, build_errors) = self.compiler.build_schemas(&compiler_state, log_event);
        self.schemas
            .write()
            .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on schemas")
            .clone_from(&next_schemas);

        if !build_errors.is_empty() {
            let error = Error::DiagnosticsError {
                errors: build_errors,
            };
            self.report_error(error);
        }

        log_event.stop(timer);

        Ok(())
    }

    fn build_source_programs(
        &self,
        compiler_state: &CompilerState,
        affected_projects: Option<HashSet<&StringKey>>,
        log_event: &impl PerfLogEvent,
    ) -> LSPProcessResult<()> {
        debug!("Building source programs");
        // Stop building programs if we detect source code update
        if self.source_code_update_status.is_started() {
            return Ok(());
        }
        let timer = log_event.start("build_source_programs_time");

        // This will build programs, but won't apply any transformations to them
        // that should be enough for LSP to start showing fragments information
        let (programs, build_errors) = self.compiler.build_raw_programs(
            compiler_state,
            &self
                .schemas
                .read()
                .expect("LSPState::build_in_watch_mode: expect to acquire read lock on schemas"),
            affected_projects,
            log_event,
        )?;

        if !build_errors.is_empty() {
            let error = Error::BuildProjectsErrors {
                errors: build_errors,
            };
            self.report_error(error);
        }

        let mut source_programs_write_lock = self.source_programs.write().expect(
            "LSPState::build_in_watch_mode: expect to acquire write lock on source_programs",
        );

        for (program_name, program) in programs {
            // NOTE: Currently, we rely on the fact that `build_raw_programs`
            // will always return a full program, so we can safely replace it in the current
            // list of source_programs
            source_programs_write_lock.insert(program_name, program);
        }

        log_event.stop(timer);

        Ok(())
    }

    fn watchman_subscription_handler(
        &self,
        mut file_source_subscription: FileSourceSubscription,
        pending_file_source_changes: Arc<RwLock<Vec<FileSourceResult>>>,
    ) -> JoinHandle<()> {
        let source_code_update_status = self.source_code_update_status.clone();
        let notify_sender = self.notify_sender.clone();
        task::spawn(async move {
            loop {
                match file_source_subscription.next_change().await {
                    Err(_) => {
                        // do nothing? compiler should panic, and restart the lsp
                    }
                    Ok(FileSourceSubscriptionNextChange::None) => {}
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateEnter) => {
                        source_code_update_status.mark_as_started();
                        notify_sender.notify_one();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateLeave) => {
                        source_code_update_status.mark_as_completed();
                        notify_sender.notify_one();
                    }
                    Ok(FileSourceSubscriptionNextChange::Result(file_source_changes)) => {
                        pending_file_source_changes
                            .write()
                            .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on pending_file_source_changes")
                            .push(file_source_changes);

                        notify_sender.notify_one();
                    }
                }
            }
        })
    }

    fn get_affected_projects(&self, compiler_state: &CompilerState) -> HashSet<&StringKey> {
        self.config
            .projects
            .keys()
            .filter(|project_name| compiler_state.project_has_pending_changes(**project_name))
            .collect::<HashSet<_>>()
    }

    fn report_error(&self, error: Error) {
        self.diagnostic_reporter.report_error(&error);
        self.errors.write().unwrap().push(error.to_string());
    }

    fn log_errors(&self, log_event_name: &str) {
        if let Ok(read_lock) = self.errors.try_read() {
            if !read_lock.is_empty() {
                let error_message = read_lock
                    .iter()
                    .map(|err| format!("{:?}", err))
                    .collect::<Vec<String>>()
                    .join("\n");
                let error_event = self.perf_logger.create_event(log_event_name);
                error_event.string("error", error_message);
                self.perf_logger.complete_event(error_event);
                self.perf_logger.flush();
            }
        }
    }

    fn clear_errors(&self) {
        if let Ok(mut write_lock) = self.errors.try_write() {
            write_lock.clear();
        }
    }
}
<commit_msg>Correctly handle syntax errors in the LSP server<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::{
    collections::{HashMap, HashSet},
    sync::{atomic::AtomicI8, atomic::Ordering, Arc, RwLock},
};

use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use graphql_ir::Program;
use interner::StringKey;
use log::{debug, info};
use lsp_server::Message;
use relay_compiler::{
    compiler::Compiler, compiler_state::CompilerState, config::Config, errors::Error, FileSource,
    FileSourceResult, FileSourceSubscription, FileSourceSubscriptionNextChange,
};
use schema::SDLSchema;
use tokio::{sync::Notify, task, task::JoinHandle};

use crate::{
    diagnostic_reporter::DiagnosticReporter,
    lsp::set_ready_status,
    lsp::update_in_progress_status,
    lsp_process_error::{LSPProcessError, LSPProcessResult},
};

#[derive(Clone, Default)]
/// This structure is representing the state of the current source control update
/// Watchman subscription will trigger updates here
struct SourceControlUpdateStatus {
    // default - no updates
    // 1 - update started
    // 2 - update completed
    value: Arc<AtomicI8>,
}

impl SourceControlUpdateStatus {
    fn mark_as_started(&self) {
        debug!("SourceControlUpdateStatus: source control update started!");
        self.value.store(1, Ordering::Relaxed);
    }
    fn is_started(&self) -> bool {
        self.value.load(Ordering::Relaxed) == 1
    }
    fn mark_as_completed(&self) {
        debug!("SourceControlUpdateStatus: source control update completed!");
        self.value.store(2, Ordering::Relaxed);
    }
    fn is_completed(&self) -> bool {
        self.value.load(Ordering::Relaxed) == 2
    }
}

/// This structure is responsible for keeping schemas/programs in sync with the current state of the world
pub(crate) struct LSPStateResources<TPerfLogger: PerfLogger + 'static> {
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    schemas: Arc<RwLock<HashMap<StringKey, Arc<SDLSchema>>>>,
    source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
    compiler: Compiler<TPerfLogger>,
    errors: Arc<RwLock<Vec<String>>>,
    source_code_update_status: SourceControlUpdateStatus,
    notify_sender: Arc<Notify>,
    notify_receiver: Arc<Notify>,
    sender: Sender<Message>,
    diagnostic_reporter: DiagnosticReporter,
}

impl<TPerfLogger: PerfLogger + 'static> LSPStateResources<TPerfLogger> {
    pub(crate) fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        schemas: Arc<RwLock<HashMap<StringKey, Arc<SDLSchema>>>>,
        source_programs: Arc<RwLock<HashMap<StringKey, Program>>>,
        sender: Sender<Message>,
    ) -> Self {
        let compiler = Compiler::new(Arc::clone(&config), Arc::clone(&perf_logger));
        let notify_sender = Arc::new(Notify::new());
        let notify_receiver = notify_sender.clone();

        let diagnostic_reporter = DiagnosticReporter::new(config.root_dir.clone(), sender.clone());

        Self {
            config,
            perf_logger,
            schemas,
            source_programs,
            compiler,
            errors: Default::default(),
            sender,
            source_code_update_status: Default::default(),
            notify_sender,
            notify_receiver,
            diagnostic_reporter,
        }
    }

    /// Create an end-less loop of keeping the resources up-to-date with the source control changes
    pub(crate) async fn watch(&self) -> LSPProcessResult<()> {
        'outer: loop {
            info!("Initializing resources for LSP server");

            update_in_progress_status(
                "Relay: watchman...",
                Some("Sending watchman query to get source files and possible saved state"),
                &self.sender,
            );
            let setup_event = self
                .perf_logger
                .create_event("lsp_state_initialize_resources");
            let timer = setup_event.start("lsp_state_initialize_resources_time");

            let file_source = FileSource::connect(&self.config, &setup_event)
                .await
                .map_err(LSPProcessError::CompilerError)?;
            let (mut compiler_state, file_source_subscription) = file_source
                .subscribe(&setup_event, self.perf_logger.as_ref())
                .await
                .map_err(LSPProcessError::CompilerError)?;

            let pending_file_source_changes =
                Arc::clone(&compiler_state.pending_file_source_changes);

            // Start a separate task to receive watchman subscription updates
            // this process will notify, when the change is detected.
            // If watchman detects the source control update, it will update the state
            // of the source_control_update_status, so we can cancel building schemas/
            // and programs earlier
            let subscription_handle = self.watchman_subscription_handler(
                file_source_subscription,
                pending_file_source_changes,
            );
            update_in_progress_status(
                "Relay: creating state...",
                Some("Building schemas and source programs for LSP"),
                &self.sender,
            );

            self.diagnostic_reporter.clear_diagnostics();

            // Run initial build, before entering the watch changes loop
            if let Err(err) = self.initial_build(&compiler_state, &setup_event) {
                self.report_error(err);
            }
            compiler_state.complete_compilation();

            setup_event.stop(timer);
            self.perf_logger.complete_event(setup_event);

            self.publish_errors("lsp_state_error");

            info!("LSP server initialization completed!");

            set_ready_status(&self.sender);

            // Here we will wait for changes from watchman
            'inner: loop {
                self.notify_receiver.notified().await;

                // Source control update started, we can ignore all pending changes, and wait for it to complete,
                // we may change the status bar to `Source Control Update...`
                if self.source_code_update_status.is_started() {
                    update_in_progress_status(
                        "Relay: hg update...",
                        Some("Waiting for source control update"),
                        &self.sender,
                    );
                    break 'inner;
                }

                // SC Update completed, we need to abort current subscription, and re-initialize resource for LSP
                if self.source_code_update_status.is_completed() {
                    debug!("Watchman indicated the the source control update has completed!");
                    subscription_handle.abort();
                    continue 'outer;
                }

                if let Err(error) = self.incremental_build(&mut compiler_state) {
                    self.report_error(error);
                }

                self.publish_errors("lsp_state_user_error");
            }
        }
    }

    fn incremental_build(&self, compiler_state: &mut CompilerState) -> Result<(), Error> {
        let log_event = self.perf_logger.create_event("lsp_state_watchman_event");
        let log_time = log_event.start("lsp_state_watchman_event_time");

        let has_new_changes = compiler_state.merge_file_source_changes(
            &self.config,
            &log_event,
            self.perf_logger.as_ref(),
            false,
        )?;

        // If changes contains schema files we need to rebuild schemas
        if has_new_changes {
            info!("LSP server detected changes...");

            self.diagnostic_reporter.clear_diagnostics();

            update_in_progress_status(
                "Relay: checking...",
                Some("Validating changes, and updating source programs with the latest changes."),
                &self.sender,
            );

            if compiler_state.has_schema_changes() {
                self.build_schemas(compiler_state, &log_event)?;
            }

            self.build_source_programs(
                &compiler_state,
                Some(self.get_affected_projects(compiler_state)),
                &log_event,
            )?;

            compiler_state.complete_compilation();

            set_ready_status(&self.sender);
        }

        log_event.stop(log_time);
        self.perf_logger.complete_event(log_event);
        self.perf_logger.flush();

        Ok(())
    }

    fn initial_build(
        &self,
        compiler_state: &CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        debug!("Initial build started...");
        // TODO: (from https://fburl.com/diff/m8jg14sy) - pass source code update status to
        // `compiler.build_schemas` and `compiler.build_source_programs` to cancel build earlier,
        // if update detected
        self.build_schemas(compiler_state, log_event)?;
        self.build_source_programs(compiler_state, None, log_event)?;

        Ok(())
    }

    fn build_schemas(
        &self,
        compiler_state: &CompilerState,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        debug!("Building schemas");
        let timer = log_event.start("build_schemas");

        // Stop building programs if we detect source code update
        if self.source_code_update_status.is_started() {
            return Ok(());
        }

        let (next_schemas, build_errors) = self.compiler.build_schemas(&compiler_state, log_event);
        self.schemas
            .write()
            .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on schemas")
            .clone_from(&next_schemas);

        let result = if !build_errors.is_empty() {
            Err(Error::DiagnosticsError {
                errors: build_errors,
            })
        } else {
            Ok(())
        };

        log_event.stop(timer);

        result
    }

    fn build_source_programs(
        &self,
        compiler_state: &CompilerState,
        affected_projects: Option<HashSet<&StringKey>>,
        log_event: &impl PerfLogEvent,
    ) -> Result<(), Error> {
        debug!("Building source programs");
        // Stop building programs if we detect source code update
        if self.source_code_update_status.is_started() {
            return Ok(());
        }
        let timer = log_event.start("build_source_programs_time");

        // This will build programs, but won't apply any transformations to them
        // that should be enough for LSP to start showing fragments information
        let (programs, build_errors) = self.compiler.build_raw_programs(
            compiler_state,
            &self
                .schemas
                .read()
                .expect("LSPState::build_in_watch_mode: expect to acquire read lock on schemas"),
            affected_projects,
            log_event,
        )?;

        let mut source_programs_write_lock = self.source_programs.write().expect(
            "LSPState::build_in_watch_mode: expect to acquire write lock on source_programs",
        );

        for (program_name, program) in programs {
            // NOTE: Currently, we rely on the fact that `build_raw_programs`
            // will always return a full program, so we can safely replace it in the current
            // list of source_programs
            source_programs_write_lock.insert(program_name, program);
        }

        let result = if !build_errors.is_empty() {
            Err(Error::BuildProjectsErrors {
                errors: build_errors,
            })
        } else {
            Ok(())
        };

        log_event.stop(timer);

        result
    }

    fn watchman_subscription_handler(
        &self,
        mut file_source_subscription: FileSourceSubscription,
        pending_file_source_changes: Arc<RwLock<Vec<FileSourceResult>>>,
    ) -> JoinHandle<()> {
        let source_code_update_status = self.source_code_update_status.clone();
        let notify_sender = self.notify_sender.clone();
        task::spawn(async move {
            loop {
                match file_source_subscription.next_change().await {
                    Err(_) => {
                        // do nothing? compiler should panic, and restart the lsp
                    }
                    Ok(FileSourceSubscriptionNextChange::None) => {}
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateEnter) => {
                        source_code_update_status.mark_as_started();
                        notify_sender.notify_one();
                    }
                    Ok(FileSourceSubscriptionNextChange::SourceControlUpdateLeave) => {
                        source_code_update_status.mark_as_completed();
                        notify_sender.notify_one();
                    }
                    Ok(FileSourceSubscriptionNextChange::Result(file_source_changes)) => {
                        pending_file_source_changes
                            .write()
                            .expect("LSPState::watch_and_update_schemas: expect to acquire write lock on pending_file_source_changes")
                            .push(file_source_changes);

                        notify_sender.notify_one();
                    }
                }
            }
        })
    }

    fn get_affected_projects(&self, compiler_state: &CompilerState) -> HashSet<&StringKey> {
        self.config
            .projects
            .keys()
            .filter(|project_name| compiler_state.project_has_pending_changes(**project_name))
            .collect::<HashSet<_>>()
    }

    fn report_error(&self, error: Error) {
        self.diagnostic_reporter.report_error(&error);
        self.errors.write().unwrap().push(error.to_string());
    }

    fn log_errors(&self, log_event_name: &str) {
        if let Ok(read_lock) = self.errors.try_read() {
            if !read_lock.is_empty() {
                let error_message = read_lock
                    .iter()
                    .map(|err| format!("{:?}", err))
                    .collect::<Vec<String>>()
                    .join("\n");
                let error_event = self.perf_logger.create_event(log_event_name);
                error_event.string("error", error_message);
                self.perf_logger.complete_event(error_event);
                self.perf_logger.flush();
            }
        }
    }

    fn clear_errors(&self) {
        if let Ok(mut write_lock) = self.errors.try_write() {
            write_lock.clear();
        }
    }

    /// This method, will log errors (if there were errors)
    /// and report the diagnostics to IDE
    fn publish_errors(&self, log_event_name: &str) {
        self.log_errors(log_event_name);
        self.clear_errors();
        self.diagnostic_reporter.commit_diagnostics();
    }
}
<|endoftext|>


<commit_before>
pub fn key_xor(data: &[u8], key: u8) -> Vec<u8> {
    data.iter().map(|&x| x ^ key ).collect()
}

fn square_dist(values1: &[f64], values2: &[f64]) -> f64 {
    values1.iter()
           .zip(values2.iter())
           .fold(0.0, |s, (&a, &b)| s + (b/a - 1.0) * (b/a - 1.0))
           .sqrt()
}

fn count_letters(data: &[u8]) -> Vec<uint> {
    let mut act_count = Vec::from_fn(26, |x| 0 * x );

    for c in data.iter() {
        match *c as uint {
            uc@65...90 => act_count[uc-65] += 1,
            lc@97...122 => act_count[lc-97] += 1,
            _ => ()
        }
    }

    act_count
}

pub fn score(data: &[u8]) -> f64 {
    // normal frequencies of english letters a-z in %
    let nom_frequencies : &[f64] = [ 8.167, 1.492, 2.782, 4.253, 12.702, 2.228, 2.015, 6.094, 6.966, 0.153, 0.772, 4.025,
        2.406, 6.749, 7.507, 1.929, 0.095, 5.987, 6.327, 9.056, 2.758, 0.978, 2.360, 0.150, 1.974, 0.074 ];

    let act_count = count_letters(data);
    let total = act_count.iter().fold(0, |s, &a| s + a);

    if total == 0 {
        // no characters found at all
        return 100.0f64
    }

    let act_frequencies : Vec<f64> = act_count.iter().map(|&v| (v as f64) * 100f64 / (total as f64)).collect();

    (data.len() - total) as f64 + square_dist(nom_frequencies, act_frequencies.as_slice())
}

pub fn decode_xor(data: &[u8]) -> Vec<u8> {
    let mut best_score = 100.0f64;
    let mut best_result = vec![];

    for key in range(0u8, 255u8) {
        let plain = key_xor(data, key);
        let new_score = score(plain.as_slice());

        if new_score < best_score {
            best_result = plain;
            best_score = new_score;
        }
    }

    best_result
}

#[test]
fn test_key_xor() {
    assert_eq!(key_xor([255, 128, 0], 64), vec![191, 192, 64]);
}

#[test]
fn test_square_dist() {
    assert_eq!(square_dist([],[]), 0.0f64);
    assert_eq!(square_dist([0.0, 1.0],[1.0, 0.0]), 2.0.sqrt());
    assert_eq!(square_dist([-1.0, 1.0, 1.0],[1.0, -1.0, 0.0]), 3.0);}

#[test]
fn test_count_letters() {
    assert_eq!(count_letters([]), vec![0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    assert_eq!(count_letters("abcdabc".as_bytes()), vec![2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    assert_eq!(count_letters("zZzZxXxX".as_bytes()), vec![0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,4]);
    assert_eq!(count_letters("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".as_bytes()), vec![2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]);
}

#[test]
fn test_score_basics() {
    assert_eq!(score([]), score("232093582§$%$!-_.,".as_bytes()) );
    assert!(score([]) > score("A".as_bytes()) );
    assert!(score("this is the end, my friend".as_bytes()) < score("7ufhgvbnftz56ufgvghtzuh54".as_bytes()) );
    assert_eq!(score("this is the end, my friend".as_bytes()), score("endmythisfriend!!!theis.".as_bytes()) );
}
<commit_msg>Start to fix some tests<commit_after>
pub fn key_xor(data: &[u8], key: u8) -> Vec<u8> {
    data.iter().map(|&x| x ^ key ).collect()
}

fn rel_square_dist(values1: &[f64], values2: &[f64]) -> f64 {
    values1.iter()
           .zip(values2.iter())
           .fold(0.0, |s, (&a, &b)| {
               let rel = if a != 0.0 {b/a - 1.0} else {1.0};
               s + rel * rel
           })
           .sqrt()
}

fn count_letters(data: &[u8]) -> Vec<uint> {
    let mut act_count = Vec::from_fn(26, |x| 0 * x );

    for c in data.iter() {
        match *c as uint {
            uc@65...90 => act_count[uc-65] += 1,
            lc@97...122 => act_count[lc-97] += 1,
            _ => ()
        }
    }

    act_count
}

pub fn score(data: &[u8]) -> f64 {
    // normal frequencies of english letters a-z in %
    let nom_frequencies : &[f64] = [ 8.167, 1.492, 2.782, 4.253, 12.702, 2.228, 2.015, 6.094, 6.966, 0.153, 0.772, 4.025,
        2.406, 6.749, 7.507, 1.929, 0.095, 5.987, 6.327, 9.056, 2.758, 0.978, 2.360, 0.150, 1.974, 0.074 ];

    let act_count = count_letters(data);
    let total = act_count.iter().fold(0, |s, &a| s + a);

    if total == 0 {
        // no characters found at all
        return data.len() as f64;
    }

    let act_frequencies : Vec<f64> = act_count.iter().map(|&v| (v as f64) * 100f64 / (total as f64)).collect();

    (data.len() - total) as f64 + rel_square_dist(nom_frequencies, act_frequencies.as_slice())
}

pub fn decode_xor(data: &[u8]) -> Vec<u8> {
    let mut best_score = 100.0f64;
    let mut best_result = vec![];

    for key in range(0u8, 255u8) {
        let plain = key_xor(data, key);
        let new_score = score(plain.as_slice());

        if new_score < best_score {
            best_result = plain;
            best_score = new_score;
        }
    }

    best_result
}

#[test]
fn test_key_xor() {
    assert_eq!(key_xor([255, 128, 0], 64), vec![191, 192, 64]);
}

#[test]
fn test_rel_square_dist() {
    assert_eq!(rel_square_dist([],[]), 0.0f64);
    assert_eq!(rel_square_dist([1.0, 1.0],[1.0, 1.0]), 0.0);
    assert_eq!(rel_square_dist([0.0, 1.0],[1.0, 0.0]), 2.0.sqrt());
    assert_eq!(rel_square_dist([-1.0, 1.0, 1.0],[1.0, -1.0, 0.0]), 3.0);}

#[test]
fn test_count_letters() {
    assert_eq!(count_letters([]), vec![0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    assert_eq!(count_letters("abcdabc".as_bytes()), vec![2,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    assert_eq!(count_letters("zZzZxXxX".as_bytes()), vec![0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,4]);
    assert_eq!(count_letters("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".as_bytes()), vec![2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]);
}

#[test]
fn test_score_basics() {
    assert_eq!(score([]), score("232093582§$%$!-_.,".as_bytes()) );
    assert!(score([]) > score("A".as_bytes()) );
    assert!(score("this is the end, my friend".as_bytes()) < score("7ufhgvbnftz56ufgvghtzuh54".as_bytes()) );
    assert_eq!(score("this is the end, my friend".as_bytes()), score("endmythisfriend!!!theis.".as_bytes()) );
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use tera::Value;
use toml;

use errors::{Result};
use content::SortBy;

static DEFAULT_PAGINATE_PATH: &'static str = "page";

/// The front matter of every section
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SectionFrontMatter {
    /// <title> of the page
    pub title: Option<String>,
    /// Description in <meta> that appears when linked, e.g. on twitter
    pub description: Option<String>,
    /// Whether to sort by "date", "order" or "none". Defaults to `none`.
    #[serde(skip_serializing)]
    pub sort_by: Option<SortBy>,
    /// Optional template, if we want to specify which template to render for that page
    #[serde(skip_serializing)]
    pub template: Option<String>,
    /// How many pages to be displayed per paginated page. No pagination will happen if this isn't set
    #[serde(skip_serializing)]
    pub paginate_by: Option<usize>,
    /// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
    #[serde(skip_serializing)]
    pub paginate_path: Option<String>,
    /// Whether to render that section or not. Defaults to `true`.
    /// Useful when the section is only there to organize things but is not meant
    /// to be used directly, like a posts section in a personal site
    #[serde(skip_serializing)]
    pub render: Option<bool>,
    /// Any extra parameter present in the front matter
    pub extra: Option<HashMap<String, Value>>,
}

impl SectionFrontMatter {
    pub fn parse(toml: &str) -> Result<SectionFrontMatter> {
        let mut f: SectionFrontMatter = match toml::from_str(toml) {
            Ok(d) => d,
            Err(e) => bail!(e),
        };

        if f.paginate_path.is_none() {
            f.paginate_path = Some(DEFAULT_PAGINATE_PATH.to_string());
        }

        if f.render.is_none() {
            f.render = Some(true);
        }

        if f.sort_by.is_none() {
            f.sort_by = Some(SortBy::None);
        }

        Ok(f)
    }

    /// Returns the current sorting method, defaults to `None` (== no sorting)
    pub fn sort_by(&self) -> SortBy {
        self.sort_by.unwrap()
    }

    /// Only applies to section, whether it is paginated or not.
    pub fn is_paginated(&self) -> bool {
        match self.paginate_by {
            Some(v) => v > 0,
            None => false
        }
    }

    pub fn should_render(&self) -> bool {
        self.render.unwrap()
    }
}

impl Default for SectionFrontMatter {
    fn default() -> SectionFrontMatter {
        SectionFrontMatter {
            title: None,
            description: None,
            sort_by: None,
            template: None,
            paginate_by: None,
            paginate_path: Some(DEFAULT_PAGINATE_PATH.to_string()),
            render: Some(true),
            extra: None,
        }
    }
}
<commit_msg>Fix SectionFrontMatter default<commit_after>use std::collections::HashMap;

use tera::Value;
use toml;

use errors::{Result};
use content::SortBy;

static DEFAULT_PAGINATE_PATH: &'static str = "page";

/// The front matter of every section
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SectionFrontMatter {
    /// <title> of the page
    pub title: Option<String>,
    /// Description in <meta> that appears when linked, e.g. on twitter
    pub description: Option<String>,
    /// Whether to sort by "date", "order" or "none". Defaults to `none`.
    #[serde(skip_serializing)]
    pub sort_by: Option<SortBy>,
    /// Optional template, if we want to specify which template to render for that page
    #[serde(skip_serializing)]
    pub template: Option<String>,
    /// How many pages to be displayed per paginated page. No pagination will happen if this isn't set
    #[serde(skip_serializing)]
    pub paginate_by: Option<usize>,
    /// Path to be used by pagination: the page number will be appended after it. Defaults to `page`.
    #[serde(skip_serializing)]
    pub paginate_path: Option<String>,
    /// Whether to render that section or not. Defaults to `true`.
    /// Useful when the section is only there to organize things but is not meant
    /// to be used directly, like a posts section in a personal site
    #[serde(skip_serializing)]
    pub render: Option<bool>,
    /// Any extra parameter present in the front matter
    pub extra: Option<HashMap<String, Value>>,
}

impl SectionFrontMatter {
    pub fn parse(toml: &str) -> Result<SectionFrontMatter> {
        let mut f: SectionFrontMatter = match toml::from_str(toml) {
            Ok(d) => d,
            Err(e) => bail!(e),
        };

        if f.paginate_path.is_none() {
            f.paginate_path = Some(DEFAULT_PAGINATE_PATH.to_string());
        }

        if f.render.is_none() {
            f.render = Some(true);
        }

        if f.sort_by.is_none() {
            f.sort_by = Some(SortBy::None);
        }

        Ok(f)
    }

    /// Returns the current sorting method, defaults to `None` (== no sorting)
    pub fn sort_by(&self) -> SortBy {
        self.sort_by.unwrap()
    }

    /// Only applies to section, whether it is paginated or not.
    pub fn is_paginated(&self) -> bool {
        match self.paginate_by {
            Some(v) => v > 0,
            None => false
        }
    }

    pub fn should_render(&self) -> bool {
        self.render.unwrap()
    }
}

impl Default for SectionFrontMatter {
    fn default() -> SectionFrontMatter {
        SectionFrontMatter {
            title: None,
            description: None,
            sort_by: Some(SortBy::None),
            template: None,
            paginate_by: None,
            paginate_path: Some(DEFAULT_PAGINATE_PATH.to_string()),
            render: Some(true),
            extra: None,
        }
    }
}
<|endoftext|>


<commit_before>use futures::Stream;

use mime::{self, Mime, Name};

use std::str;

use server::{httparse, twoway};
use server::boundary::BoundaryFinder;

use { BodyChunk, StreamError};

use self::httparse::{EMPTY_HEADER, Status};

use helpers::*;

const MAX_BUF_LEN: usize = 1024;
const MAX_HEADERS: usize = 4;

/// The headers of a `Field`, including the name, filename, and `Content-Type`, if provided.
///
/// ### Note: Untrustworthy
/// These values are provided directly by the client, and as such, should be considered
/// *untrustworthy* and potentially **dangerous**. Avoid any unsanitized usage on the filesystem
/// or in a shell or database, or performing unsafe operations with the assumption of a
/// certain file type. Sanitizing/verifying these values is (currently) beyond the scope of this
/// crate.
#[derive(Clone, Default, Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct FieldHeaders {
    /// The name of the field as provided by the client.
    ///
    /// ### Special Value: `_charset_`
    /// If the client intended a different character set than UTF-8 for its text values, it may
    /// provide the name of the charset as a text field (ASCII-encoded) with the name `_charset_`.
    /// See [IETF RFC 7578, Section 4.6](https://tools.ietf.org/html/rfc7578#section-4.6) for more.
    ///
    /// Alternately, the charset can be provided for an individual field as a `charset` parameter
    /// to its `Content-Type` header; see the `charset()` method for a convenient wrapper.
    pub name: String,
    /// The name of the file as it was on the client. If not provided, it may still have been a
    /// file field.
    pub filename: Option<String>,
    /// The `Content-Type` of this field, as provided by the client. If `None`, then the field
    /// is probably text, but this is not guaranteed.
    pub content_type: Option<Mime>,
}

impl FieldHeaders {
    /// `true` if `content_type` is `None` or `text/*` (such as `text/plain`).
    ///
    /// **Note**: this does not guarantee that the field data is compatible with
    /// `FieldData::read_text()`; supporting more encodings than ASCII/UTF-8 is (currently)
    /// beyond the scope of this crate.
    pub fn is_text(&self) -> bool {
        self.content_type.as_ref().map_or(true, |ct| ct.type_() == mime::TEXT)
    }

    /// The character set of this field, if provided.
    pub fn charset(&self) -> Option<Name> {
        self.content_type.as_ref().and_then(|ct| ct.get_param(mime::CHARSET))
    }
}

#[derive(Debug, Default)]
pub struct ReadHeaders {
    accumulator: Vec<u8>
}

impl ReadHeaders {
    pub fn read_headers<S: Stream>(&mut self, stream: &mut BoundaryFinder<S>) -> PollOpt<FieldHeaders, S::Error>
    where S::Item: BodyChunk, S::Error: StreamError {
        loop {
            trace!("read_headers state: accumulator: {}", show_bytes(&self.accumulator));

            let chunk = match try_ready!(stream.poll()) {
                Some(chunk) => chunk,
                None => return if !self.accumulator.is_empty() {
                    error("unexpected end of stream")
                } else {
                    trace!("end of request reached");
                    ready(None)
                },
            };

            trace!("got chunk for headers: {}", show_bytes(chunk.as_slice()));

            // End of the headers section is signalled by a double-CRLF
            if let Some(header_end) = twoway::find_bytes(chunk.as_slice(), b"\r\n\r\n") {
                // Split after the double-CRLF because we don't want to yield it and httparse expects it
                let (headers, rem) = chunk.split_at(header_end + 4);
                stream.push_chunk(rem);

                if !self.accumulator.is_empty() {
                    self.accumulator.extend_from_slice(headers.as_slice());
                    let headers = parse_headers(&self.accumulator)?;
                    self.accumulator.clear();

                    return ready(Some(headers));
                } else {
                    return ready(Some(parse_headers(headers.as_slice())?));
                }
            } else if let Some(split_idx) = header_end_split(&self.accumulator, chunk.as_slice()) {
                let (head, tail) = chunk.split_at(split_idx);
                self.accumulator.extend_from_slice(head.as_slice());
                stream.push_chunk(tail);
                continue;
            }

            if self.accumulator.len().saturating_add(chunk.len()) > MAX_BUF_LEN {
                return error("headers section too long or trailing double-CRLF missing");
            }

            self.accumulator.extend_from_slice(chunk.as_slice());
        }
    }
}

const CRLF2: &[u8] = b"\r\n\r\n";

/// Check if the double-CRLF falls between chunk boundaries, and if so, the split index of
/// the second boundary
fn header_end_split(first: &[u8], second: &[u8]) -> Option<usize> {
    fn split_subcheck(start: usize, first: &[u8], second: &[u8]) -> bool {
        first.len() >= start && first[first.len() - start ..].iter().chain(second).take(4).eq(CRLF2)
    }

    if split_subcheck(3, first, second) {
        Some(1)
    } else if split_subcheck(2, first, second) {
        Some(2)
    } else if split_subcheck(1, first, second) {
        Some(3)
    } else {
        None
    }
}

fn parse_headers<E: StreamError>(bytes: &[u8]) -> Result<FieldHeaders, E> {
    debug_assert!(bytes.ends_with(b"\r\n\r\n"),
                  "header byte sequence does not end with `\\r\\n\\r\\n`: {}",
                  show_bytes(bytes));

    let mut header_buf = [EMPTY_HEADER; MAX_HEADERS];

    let headers = match httparse::parse_headers(bytes, &mut header_buf) {
        Ok(Status::Complete((_, headers))) => headers,
        Ok(Status::Partial) => ret_err!("field headers incomplete: {}", show_bytes(bytes)),
        Err(e) => ret_err!("error parsing headers: {}; from buffer: {}", e, show_bytes(bytes)),
    };

    trace!("parsed headers: {:?}", headers);

    let mut out_headers = FieldHeaders::default();

    for header in headers {
        let str_val = str::from_utf8(header.value)
            .or_else(|_| error("multipart field headers must be UTF-8 encoded"))?
            .trim();

        match header.name {
            "Content-Disposition" => parse_cont_disp_val(str_val, &mut out_headers)?,
            "Content-Type" => out_headers.content_type = Some(str_val.parse::<Mime>()
                 .or_else(|_| ret_err!("could not parse MIME type from {:?}", str_val))?),
            _ => (),
        }
    }

    Ok(out_headers)
}

fn parse_cont_disp_val<E: StreamError>(val: &str, out: &mut FieldHeaders) -> Result<(), E> {
    debug!("parse_cont_disp_val({:?})", val);

    // Only take the first section, the rest can be in quoted strings that we want to handle
    let mut sections = val.splitn(2, ';').map(str::trim);

    match sections.next() {
        Some("form-data") => (),
        Some(other) => ret_err!("unexpected multipart field Content-Disposition: {}", other),
        None => return error("each multipart field requires a Content-Disposition: form-data header"),
    }

    let mut rem = sections.next().unwrap_or("");

    while let Some((key, val, rest)) = parse_keyval(rem) {
        rem = rest;

        match key {
            "name" => out.name = val.to_string(),
            "filename" => out.filename = Some(val.to_string()),
            _ => debug!("unknown key-value pair in Content-Disposition: {:?} = {:?}", key, val),
        }
    }

    if out.name.is_empty() {
        ret_err!("expected 'name' attribute in Content-Disposition: {}", val);
    }

    Ok(())
}

fn parse_keyval(input: &str) -> Option<(&str, &str, &str)> {
    let (name, rest) = try_opt!(param_name(input));
    let (val, rest) = try_opt!(param_val(rest));

    Some((name, val, rest))
}

fn param_name(input: &str) -> Option<(&str, &str)> {
    let mut splits = input.trim_left_matches(&[' ', ';'][..]).splitn(2, '=');

    let name = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or("");

    Some((name, rem))
}

fn param_val(input: &str) -> Option<(&str, &str)> {
    let pat: &[char] = &['"'];
    let mut splits = input.splitn(3, pat);

    let token = try_opt!(splits.next()).trim();

    // the value doesn't have to be in quotes if it doesn't contain forbidden chars like `;`
    if !token.is_empty() {
        let mut splits = token.splitn(2, ';');
        let token = try_opt!(splits.next()).trim();
        let rem = splits.next().unwrap_or("");

        return Some((token, rem));
    }

    let qstr = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or_else(|| { warn!("unterminated quote: {:?}", qstr); "" });

    Some((qstr, rem))
}

#[test]
fn test_header_end_split() {
    assert_eq!(header_end_split(b"\r\n\r", b"\n"), Some(1));
    assert_eq!(header_end_split(b"\r\n", b"\r\n"), Some(2));
    assert_eq!(header_end_split(b"\r", b"\n\r\n"), Some(3));
    assert_eq!(header_end_split(b"\r\n\r\n", b"FOOBAR"), None);
    assert_eq!(header_end_split(b"FOOBAR", b"\r\n\r\n"), None);
}

#[test]
fn test_parse_headers() {
    let parse_headers = parse_headers::<StringError>;

    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = \"field\"\r\n\r\n"),
        Ok(FieldHeaders { name: "field".into(), .. FieldHeaders::default()})
    );

    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = field\r\n\r\n"),
        Ok(FieldHeaders { name: "field".into(), .. FieldHeaders::default()})
    );

    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = field\r\n\
                        Content-Type: application/octet-stream\r\n\r\n"),
        Ok(FieldHeaders {
            name: "field".into(),
            content_type: Some(mime::APPLICATION_OCTET_STREAM),
            .. FieldHeaders::default()
        })
    );

    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = field; filename = file.bin\r\n\
                        Content-Type: application/octet-stream\r\n\r\n"),
        Ok(FieldHeaders {
            name: "field".into(),
            filename: Some("file.bin".into()),
            content_type: Some(mime::APPLICATION_OCTET_STREAM),
        })
    );

    // reversed headers (can happen)
    assert_eq!(
        parse_headers(b"Content-Type: application/octet-stream\r\n\
                        Content-Disposition: form-data; name = field; filename = file.bin\r\n\r\n"),
        Ok(FieldHeaders {
            name: "field".into(),
            filename: Some("file.bin".into()),
            content_type: Some(mime::APPLICATION_OCTET_STREAM),
        })
    );
}
<commit_msg>make header name matching case-insensitive<commit_after>use futures::Stream;

use mime::{self, Mime, Name};

use std::ascii::AsciiExt;
use std::str;

use server::{httparse, twoway};
use server::boundary::BoundaryFinder;

use { BodyChunk, StreamError};

use self::httparse::{EMPTY_HEADER, Status};

use helpers::*;

const MAX_BUF_LEN: usize = 1024;
const MAX_HEADERS: usize = 4;

/// The headers of a `Field`, including the name, filename, and `Content-Type`, if provided.
///
/// ### Note: Untrustworthy
/// These values are provided directly by the client, and as such, should be considered
/// *untrustworthy* and potentially **dangerous**. Avoid any unsanitized usage on the filesystem
/// or in a shell or database, or performing unsafe operations with the assumption of a
/// certain file type. Sanitizing/verifying these values is (currently) beyond the scope of this
/// crate.
#[derive(Clone, Default, Debug)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct FieldHeaders {
    /// The name of the field as provided by the client.
    ///
    /// ### Special Value: `_charset_`
    /// If the client intended a different character set than UTF-8 for its text values, it may
    /// provide the name of the charset as a text field (ASCII-encoded) with the name `_charset_`.
    /// See [IETF RFC 7578, Section 4.6](https://tools.ietf.org/html/rfc7578#section-4.6) for more.
    ///
    /// Alternately, the charset can be provided for an individual field as a `charset` parameter
    /// to its `Content-Type` header; see the `charset()` method for a convenient wrapper.
    pub name: String,
    /// The name of the file as it was on the client. If not provided, it may still have been a
    /// file field.
    pub filename: Option<String>,
    /// The `Content-Type` of this field, as provided by the client. If `None`, then the field
    /// is probably text, but this is not guaranteed.
    pub content_type: Option<Mime>,
}

impl FieldHeaders {
    /// `true` if `content_type` is `None` or `text/*` (such as `text/plain`).
    ///
    /// **Note**: this does not guarantee that the field data is compatible with
    /// `FieldData::read_text()`; supporting more encodings than ASCII/UTF-8 is (currently)
    /// beyond the scope of this crate.
    pub fn is_text(&self) -> bool {
        self.content_type.as_ref().map_or(true, |ct| ct.type_() == mime::TEXT)
    }

    /// The character set of this field, if provided.
    pub fn charset(&self) -> Option<Name> {
        self.content_type.as_ref().and_then(|ct| ct.get_param(mime::CHARSET))
    }
}

#[derive(Debug, Default)]
pub struct ReadHeaders {
    accumulator: Vec<u8>
}

impl ReadHeaders {
    pub fn read_headers<S: Stream>(&mut self, stream: &mut BoundaryFinder<S>) -> PollOpt<FieldHeaders, S::Error>
    where S::Item: BodyChunk, S::Error: StreamError {
        loop {
            trace!("read_headers state: accumulator: {}", show_bytes(&self.accumulator));

            let chunk = match try_ready!(stream.poll()) {
                Some(chunk) => chunk,
                None => return if !self.accumulator.is_empty() {
                    error("unexpected end of stream")
                } else {
                    trace!("end of request reached");
                    ready(None)
                },
            };

            trace!("got chunk for headers: {}", show_bytes(chunk.as_slice()));

            // End of the headers section is signalled by a double-CRLF
            if let Some(header_end) = twoway::find_bytes(chunk.as_slice(), b"\r\n\r\n") {
                // Split after the double-CRLF because we don't want to yield it and httparse expects it
                let (headers, rem) = chunk.split_at(header_end + 4);
                stream.push_chunk(rem);

                if !self.accumulator.is_empty() {
                    self.accumulator.extend_from_slice(headers.as_slice());
                    let headers = parse_headers(&self.accumulator)?;
                    self.accumulator.clear();

                    return ready(Some(headers));
                } else {
                    return ready(Some(parse_headers(headers.as_slice())?));
                }
            } else if let Some(split_idx) = header_end_split(&self.accumulator, chunk.as_slice()) {
                let (head, tail) = chunk.split_at(split_idx);
                self.accumulator.extend_from_slice(head.as_slice());
                stream.push_chunk(tail);
                continue;
            }

            if self.accumulator.len().saturating_add(chunk.len()) > MAX_BUF_LEN {
                return error("headers section too long or trailing double-CRLF missing");
            }

            self.accumulator.extend_from_slice(chunk.as_slice());
        }
    }
}

const CRLF2: &[u8] = b"\r\n\r\n";

/// Check if the double-CRLF falls between chunk boundaries, and if so, the split index of
/// the second boundary
fn header_end_split(first: &[u8], second: &[u8]) -> Option<usize> {
    fn split_subcheck(start: usize, first: &[u8], second: &[u8]) -> bool {
        first.len() >= start && first[first.len() - start ..].iter().chain(second).take(4).eq(CRLF2)
    }

    if split_subcheck(3, first, second) {
        Some(1)
    } else if split_subcheck(2, first, second) {
        Some(2)
    } else if split_subcheck(1, first, second) {
        Some(3)
    } else {
        None
    }
}

fn parse_headers<E: StreamError>(bytes: &[u8]) -> Result<FieldHeaders, E> {
    debug_assert!(bytes.ends_with(b"\r\n\r\n"),
                  "header byte sequence does not end with `\\r\\n\\r\\n`: {}",
                  show_bytes(bytes));

    let mut header_buf = [EMPTY_HEADER; MAX_HEADERS];

    let headers = match httparse::parse_headers(bytes, &mut header_buf) {
        Ok(Status::Complete((_, headers))) => headers,
        Ok(Status::Partial) => ret_err!("field headers incomplete: {}", show_bytes(bytes)),
        Err(e) => ret_err!("error parsing headers: {}; from buffer: {}", e, show_bytes(bytes)),
    };

    trace!("parsed headers: {:?}", headers);

    let mut out_headers = FieldHeaders::default();

    for header in headers {
        let str_val = str::from_utf8(header.value)
            .or_else(|_| error("multipart field headers must be UTF-8 encoded"))?
            .trim();

        if "Content-Disposition".eq_ignore_ascii_case(header.name) {
            parse_cont_disp_val(str_val, &mut out_headers)?;
        } else if "Content-Type".eq_ignore_ascii_case(header.name) {
            out_headers.content_type = Some(
                str_val.parse::<Mime>()
                    .or_else(|_| ret_err!("could not parse MIME type from {:?}", str_val))?
            );
        }
    }

    Ok(out_headers)
}

fn parse_cont_disp_val<E: StreamError>(val: &str, out: &mut FieldHeaders) -> Result<(), E> {
    debug!("parse_cont_disp_val({:?})", val);

    // Only take the first section, the rest can be in quoted strings that we want to handle
    let mut sections = val.splitn(2, ';').map(str::trim);

    match sections.next() {
        Some("form-data") => (),
        Some(other) => ret_err!("unexpected multipart field Content-Disposition: {}", other),
        None => return error("each multipart field requires a Content-Disposition: form-data header"),
    }

    let mut rem = sections.next().unwrap_or("");

    while let Some((key, val, rest)) = parse_keyval(rem) {
        rem = rest;

        match key {
            "name" => out.name = val.to_string(),
            "filename" => out.filename = Some(val.to_string()),
            _ => debug!("unknown key-value pair in Content-Disposition: {:?} = {:?}", key, val),
        }
    }

    if out.name.is_empty() {
        ret_err!("expected 'name' attribute in Content-Disposition: {}", val);
    }

    Ok(())
}

fn parse_keyval(input: &str) -> Option<(&str, &str, &str)> {
    let (name, rest) = try_opt!(param_name(input));
    let (val, rest) = try_opt!(param_val(rest));

    Some((name, val, rest))
}

fn param_name(input: &str) -> Option<(&str, &str)> {
    let mut splits = input.trim_left_matches(&[' ', ';'][..]).splitn(2, '=');

    let name = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or("");

    Some((name, rem))
}

fn param_val(input: &str) -> Option<(&str, &str)> {
    let pat: &[char] = &['"'];
    let mut splits = input.splitn(3, pat);

    let token = try_opt!(splits.next()).trim();

    // the value doesn't have to be in quotes if it doesn't contain forbidden chars like `;`
    if !token.is_empty() {
        let mut splits = token.splitn(2, ';');
        let token = try_opt!(splits.next()).trim();
        let rem = splits.next().unwrap_or("");

        return Some((token, rem));
    }

    let qstr = try_opt!(splits.next()).trim();
    let rem = splits.next().unwrap_or_else(|| { warn!("unterminated quote: {:?}", qstr); "" });

    Some((qstr, rem))
}

#[test]
fn test_header_end_split() {
    assert_eq!(header_end_split(b"\r\n\r", b"\n"), Some(1));
    assert_eq!(header_end_split(b"\r\n", b"\r\n"), Some(2));
    assert_eq!(header_end_split(b"\r", b"\n\r\n"), Some(3));
    assert_eq!(header_end_split(b"\r\n\r\n", b"FOOBAR"), None);
    assert_eq!(header_end_split(b"FOOBAR", b"\r\n\r\n"), None);
}

#[test]
fn test_parse_headers() {
    use StringError;

    let parse_headers = parse_headers::<StringError>;

    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = \"field\"\r\n\r\n"),
        Ok(FieldHeaders { name: "field".into(), .. FieldHeaders::default()})
    );

    // lowercase
    assert_eq!(
        parse_headers(b"content-disposition: form-data; name = \"field\"\r\n\r\n"),
        Ok(FieldHeaders { name: "field".into(), .. FieldHeaders::default()})
    );

    // mixed case
    assert_eq!(
        parse_headers(b"cOnTent-dIsPosition: form-data; name = \"field\"\r\n\r\n"),
        Ok(FieldHeaders { name: "field".into(), .. FieldHeaders::default()})
    );

    // omitted quotes
    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = field\r\n\r\n"),
        Ok(FieldHeaders { name: "field".into(), .. FieldHeaders::default()})
    );

    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = field\r\n\
                        Content-Type: application/octet-stream\r\n\r\n"),
        Ok(FieldHeaders {
            name: "field".into(),
            content_type: Some(mime::APPLICATION_OCTET_STREAM),
            .. FieldHeaders::default()
        })
    );

    // filename without quotes with extension
    assert_eq!(
        parse_headers(b"Content-Disposition: form-data; name = field; filename = file.bin\r\n\
                        Content-Type: application/octet-stream\r\n\r\n"),
        Ok(FieldHeaders {
            name: "field".into(),
            filename: Some("file.bin".into()),
            content_type: Some(mime::APPLICATION_OCTET_STREAM),
        })
    );

    // reversed headers (can happen)
    assert_eq!(
        parse_headers(b"Content-Type: application/octet-stream\r\n\
                        Content-Disposition: form-data; name = field; filename = file.bin\r\n\r\n"),
        Ok(FieldHeaders {
            name: "field".into(),
            filename: Some("file.bin".into()),
            content_type: Some(mime::APPLICATION_OCTET_STREAM),
        })
    );
}
<|endoftext|>


<commit_before><commit_msg>Adds an integration test on push pull protocol<commit_after>extern crate scaproust;

use scaproust::*;

#[test]
fn test_push_pull_one_to_one() {
	let session = Session::new().unwrap();
	let mut pull = session.create_socket(SocketType::Pull).unwrap();
	let mut push = session.create_socket(SocketType::Push).unwrap();

	pull.bind("tcp://127.0.0.1:5454").unwrap();
	push.connect("tcp://127.0.0.1:5454").unwrap();

	let sent = vec![65, 66, 67];
	push.send(sent).unwrap();
	let received = pull.recv().unwrap();

	assert_eq!(vec![65, 66, 67], received)
}<|endoftext|>


<commit_before>extern crate sdl2;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;

fn main()
{
    let context = sdl2::init().unwrap();
    let video = context.video().unwrap();

    let window = video.window("rrthozopsi", 800, 600)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();
    let mut event_pump = context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                Event::KeyDown {..} => {
                    renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 0));
                    renderer.clear();
                    renderer.present();
                },
                _ => {}
            }
        }
    }
}
<commit_msg>FPS counter<commit_after>extern crate sdl2;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;

fn main()
{
    let context = sdl2::init().unwrap();
    let video = context.video().unwrap();

    let window = video.window("rrthozopsi", 800, 600)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();
    let mut event_pump = context.event_pump().unwrap();

    let mut timer = context.timer().unwrap();
    let mut frames = 0;
    let mut start_time = timer.ticks();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                Event::KeyDown {..} => {
                    renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 0));
                    renderer.clear();
                    renderer.present();
                },
                _ => {}
            }
        }

        frames += 1;

        let current_time = timer.ticks();
        let diff_time = current_time - start_time;
        if diff_time > 250
        {
            println!("PFS: {}", frames / diff_time);
            start_time = current_time;
            frames = 0;
        }
    }
}
<|endoftext|>


<commit_before>extern crate pbr;

use pbr::{ProgressBar, PbIter};
use std::time::Duration;
use std::thread;

#[test]
fn simple_example() {
    let count = 1000;
    let mut pb = ProgressBar::new(count);
    pb.format("╢▌▌░╟");
    for _ in 0..count {
        pb.inc();
        thread::sleep(Duration::from_millis(1));
    }
    println!("done!");
}

#[test]
fn simple_iter_example(){
    for _ in PbIter::new(0..1000) {
        thread::sleep(Duration::from_millis(1));
    }
    println!("done!");
}

#[test]
fn timeout_example() {
    let count = 100;
    let mut pb = ProgressBar::new(count*20);
    pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
    pb.show_message = true;
    pb.inc();
    for _ in 0..count {
        for _ in 0..20 {
            pb.message("Waiting  : ");
            thread::sleep(Duration::from_millis(50));
            pb.tick();
        }
        for _ in 0..20 {
            pb.message("Connected: ");
            thread::sleep(Duration::from_millis(50));
            pb.inc();
        }
    }
    for _ in 0..10 {
        pb.message("Cleaning :");
        thread::sleep(Duration::from_millis(50));
        pb.tick();
    }
    pb.finish();
    println!("done!");
}

#[test]
fn npm_bar() {
   let count = 20;
    let mut pb = ProgressBar::new(count*5);
    pb.tick_format("\\|/-");
    pb.format("|#--|");
    pb.show_tick = true;
    pb.show_speed = false;
    pb.show_percent = false;
    pb.show_counter = false;
    pb.show_time_left = false;
    pb.inc();
    for _ in 0..count {
        for _ in 0..5 {
            pb.message("normalize -> thing ");
            thread::sleep(Duration::from_millis(80));
            pb.tick();
        }
        for _ in 0..5 {
            pb.message("fuzz -> tree       ");
            thread::sleep(Duration::from_millis(80));
            pb.inc();
        }
    }
    pb.finish();
    println!("done!");
}
<commit_msg>test(examples): update<commit_after>extern crate pbr;

use pbr::{ProgressBar, PbIter};
use std::time::Duration;
use std::thread;

#[test]
fn simple_example() {
    let count = 5000;
    let mut pb = ProgressBar::new(count);
    pb.format("╢▌▌░╟");
    for _ in 0..count {
        pb.inc();
        thread::sleep(Duration::from_millis(5));
    }
    println!("done!");
}

#[test]
fn simple_iter_example(){
    for _ in PbIter::new(0..20000) {
        thread::sleep(Duration::from_millis(1));
    }
    println!("done!");
}

#[test]
fn timeout_example() {
    let count = 10;
    let mut pb = ProgressBar::new(count*20);
    pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
    pb.show_message = true;
    pb.inc();
    for _ in 0..count {
        for _ in 0..20 {
            pb.message("Waiting  : ");
            thread::sleep(Duration::from_millis(50));
            pb.tick();
        }
        for _ in 0..20 {
            pb.message("Connected: ");
            thread::sleep(Duration::from_millis(50));
            pb.inc();
        }
    }
    for _ in 0..10 {
        pb.message("Cleaning :");
        thread::sleep(Duration::from_millis(50));
        pb.tick();
    }
    pb.finish();
    println!("done!");
}

#[test]
fn npm_bar() {
   let count = 30;
    let mut pb = ProgressBar::new(count*5);
    pb.tick_format("\\|/-");
    pb.format("|#--|");
    pb.show_tick = true;
    pb.show_speed = false;
    pb.show_percent = false;
    pb.show_counter = false;
    pb.show_time_left = false;
    pb.inc();
    for _ in 0..count {
        for _ in 0..5 {
            pb.message("normalize -> thing ");
            thread::sleep(Duration::from_millis(80));
            pb.tick();
        }
        for _ in 0..5 {
            pb.message("fuzz -> tree       ");
            thread::sleep(Duration::from_millis(80));
            pb.inc();
        }
    }
    pb.finish();
    println!("done!");
}
<|endoftext|>


<commit_before>extern crate bus;

use std::sync::mpsc;
use std::time;

#[test]
fn it_works() {
    let mut c = bus::Bus::new(10);
    let mut r1 = c.add_rx();
    let mut r2 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(r1.try_recv(), Ok(true));
    assert_eq!(r2.try_recv(), Ok(true));
}

#[test]
fn it_fails_when_full() {
    let mut c = bus::Bus::new(1);
    let r1 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(c.try_broadcast(false), Err(false));
    drop(r1);
}

#[test]
fn it_succeeds_when_not_full() {
    let mut c = bus::Bus::new(1);
    let mut r1 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(c.try_broadcast(false), Err(false));
    assert_eq!(r1.try_recv(), Ok(true));
    assert_eq!(c.try_broadcast(true), Ok(()));
}

#[test]
fn it_fails_when_empty() {
    let mut c = bus::Bus::<bool>::new(10);
    let mut r1 = c.add_rx();
    assert_eq!(r1.try_recv(), Err(mpsc::TryRecvError::Empty));
}

#[test]
fn it_reads_when_full() {
    let mut c = bus::Bus::new(1);
    let mut r1 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(r1.try_recv(), Ok(true));
}

#[test]
fn it_iterates() {
    use std::thread;

    let mut tx = bus::Bus::new(2);
    let mut rx = tx.add_rx();
    let j = thread::spawn(move || {
        for i in 0..1_000 {
            tx.broadcast(i);
        }
    });

    let mut ii = 0;
    for i in rx.iter() {
        assert_eq!(i, ii);
        ii += 1;
    }

    j.join().unwrap();
    assert_eq!(ii, 1_000);
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
fn aggressive_iteration() {
    for _ in 0..1_000 {
        use std::thread;

        let mut tx = bus::Bus::new(2);
        let mut rx = tx.add_rx();
        let j = thread::spawn(move || {
            for i in 0..1_000 {
                tx.broadcast(i);
            }
        });

        let mut ii = 0;
        for i in rx.iter() {
            assert_eq!(i, ii);
            ii += 1;
        }

        j.join().unwrap();
        assert_eq!(ii, 1_000);
        assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
    }
}

#[test]
fn it_detects_closure() {
    let mut tx = bus::Bus::new(1);
    let mut rx = tx.add_rx();
    assert_eq!(tx.try_broadcast(true), Ok(()));
    assert_eq!(rx.try_recv(), Ok(true));
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
    drop(tx);
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
fn it_recvs_after_close() {
    let mut tx = bus::Bus::new(1);
    let mut rx = tx.add_rx();
    assert_eq!(tx.try_broadcast(true), Ok(()));
    drop(tx);
    assert_eq!(rx.try_recv(), Ok(true));
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
fn it_handles_leaves() {
    let mut c = bus::Bus::new(1);
    let mut r1 = c.add_rx();
    let r2 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    drop(r2);
    assert_eq!(r1.try_recv(), Ok(true));
    assert_eq!(c.try_broadcast(true), Ok(()));
}

#[test]
fn it_runs_blocked_writes() {
    use std::thread;

    let mut c = Box::new(bus::Bus::new(1));
    let mut r1 = c.add_rx();
    c.broadcast(true); // this is fine

    // buffer is now full
    assert_eq!(c.try_broadcast(false), Err(false));
    // start other thread that blocks
    let c = thread::spawn(move || {
        c.broadcast(false);
    });
    // unblock sender by receiving
    assert_eq!(r1.try_recv(), Ok(true));
    // drop r1 to release other thread and safely drop c
    drop(r1);
    c.join().unwrap();
}

#[test]
fn it_runs_blocked_reads() {
    use std::sync::mpsc;
    use std::thread;

    let mut tx = Box::new(bus::Bus::new(1));
    let mut rx = tx.add_rx();
    // buffer is now empty
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
    // start other thread that blocks
    let c = thread::spawn(move || {
        rx.recv().unwrap();
    });
    // unblock receiver by broadcasting
    tx.broadcast(true);
    // check that thread now finished
    c.join().unwrap();
}

#[test]
fn it_can_count_to_10000() {
    use std::thread;

    let mut c = bus::Bus::new(2);
    let mut r1 = c.add_rx();
    let j = thread::spawn(move || {
        for i in 0..10_000 {
            c.broadcast(i);
        }
    });

    for i in 0..10_000 {
        assert_eq!(r1.recv(), Ok(i));
    }

    j.join().unwrap();
    assert_eq!(r1.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
fn test_busy() {
    use std::thread;

    // start a bus with limited space
    let mut bus = bus::Bus::new(1);

    // first receiver only receives 5 items
    let mut rx1 = bus.add_rx();
    let t1 = thread::spawn(move || {
        for _ in 0..5 {
            rx1.recv().unwrap();
        }
        drop(rx1);
    });

    // second receiver receives 10 items
    let mut rx2 = bus.add_rx();
    let t2 = thread::spawn(move || {
        for _ in 0..10 {
            rx2.recv().unwrap();
        }
        drop(rx2);
    });

    // let receivers start
    std::thread::sleep(time::Duration::from_millis(500));

    // try to send 25 items -- should work fine
    for i in 0..25 {
        std::thread::sleep(time::Duration::from_millis(100));
        match bus.try_broadcast(i) {
            Ok(_) => (),
            Err(e) => println!("Broadcast failed {}", e),
        }
    }

    // done sending -- wait for receivers (which should already be done)
    t1.join().unwrap();
    t2.join().unwrap();
    assert!(true);
}
<commit_msg>Miri can't do threads<commit_after>extern crate bus;

use std::sync::mpsc;
use std::time;

#[test]
fn it_works() {
    let mut c = bus::Bus::new(10);
    let mut r1 = c.add_rx();
    let mut r2 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(r1.try_recv(), Ok(true));
    assert_eq!(r2.try_recv(), Ok(true));
}

#[test]
fn it_fails_when_full() {
    let mut c = bus::Bus::new(1);
    let r1 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(c.try_broadcast(false), Err(false));
    drop(r1);
}

#[test]
fn it_succeeds_when_not_full() {
    let mut c = bus::Bus::new(1);
    let mut r1 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(c.try_broadcast(false), Err(false));
    assert_eq!(r1.try_recv(), Ok(true));
    assert_eq!(c.try_broadcast(true), Ok(()));
}

#[test]
fn it_fails_when_empty() {
    let mut c = bus::Bus::<bool>::new(10);
    let mut r1 = c.add_rx();
    assert_eq!(r1.try_recv(), Err(mpsc::TryRecvError::Empty));
}

#[test]
fn it_reads_when_full() {
    let mut c = bus::Bus::new(1);
    let mut r1 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    assert_eq!(r1.try_recv(), Ok(true));
}

#[test]
#[cfg(not(miri))]
fn it_iterates() {
    use std::thread;

    let mut tx = bus::Bus::new(2);
    let mut rx = tx.add_rx();
    let j = thread::spawn(move || {
        for i in 0..1_000 {
            tx.broadcast(i);
        }
    });

    let mut ii = 0;
    for i in rx.iter() {
        assert_eq!(i, ii);
        ii += 1;
    }

    j.join().unwrap();
    assert_eq!(ii, 1_000);
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
#[cfg(not(miri))]
fn aggressive_iteration() {
    for _ in 0..1_000 {
        use std::thread;

        let mut tx = bus::Bus::new(2);
        let mut rx = tx.add_rx();
        let j = thread::spawn(move || {
            for i in 0..1_000 {
                tx.broadcast(i);
            }
        });

        let mut ii = 0;
        for i in rx.iter() {
            assert_eq!(i, ii);
            ii += 1;
        }

        j.join().unwrap();
        assert_eq!(ii, 1_000);
        assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
    }
}

#[test]
fn it_detects_closure() {
    let mut tx = bus::Bus::new(1);
    let mut rx = tx.add_rx();
    assert_eq!(tx.try_broadcast(true), Ok(()));
    assert_eq!(rx.try_recv(), Ok(true));
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
    drop(tx);
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
fn it_recvs_after_close() {
    let mut tx = bus::Bus::new(1);
    let mut rx = tx.add_rx();
    assert_eq!(tx.try_broadcast(true), Ok(()));
    drop(tx);
    assert_eq!(rx.try_recv(), Ok(true));
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
fn it_handles_leaves() {
    let mut c = bus::Bus::new(1);
    let mut r1 = c.add_rx();
    let r2 = c.add_rx();
    assert_eq!(c.try_broadcast(true), Ok(()));
    drop(r2);
    assert_eq!(r1.try_recv(), Ok(true));
    assert_eq!(c.try_broadcast(true), Ok(()));
}

#[test]
#[cfg(not(miri))]
fn it_runs_blocked_writes() {
    use std::thread;

    let mut c = Box::new(bus::Bus::new(1));
    let mut r1 = c.add_rx();
    c.broadcast(true); // this is fine

    // buffer is now full
    assert_eq!(c.try_broadcast(false), Err(false));
    // start other thread that blocks
    let c = thread::spawn(move || {
        c.broadcast(false);
    });
    // unblock sender by receiving
    assert_eq!(r1.try_recv(), Ok(true));
    // drop r1 to release other thread and safely drop c
    drop(r1);
    c.join().unwrap();
}

#[test]
#[cfg(not(miri))]
fn it_runs_blocked_reads() {
    use std::sync::mpsc;
    use std::thread;

    let mut tx = Box::new(bus::Bus::new(1));
    let mut rx = tx.add_rx();
    // buffer is now empty
    assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
    // start other thread that blocks
    let c = thread::spawn(move || {
        rx.recv().unwrap();
    });
    // unblock receiver by broadcasting
    tx.broadcast(true);
    // check that thread now finished
    c.join().unwrap();
}

#[test]
#[cfg(not(miri))]
fn it_can_count_to_10000() {
    use std::thread;

    let mut c = bus::Bus::new(2);
    let mut r1 = c.add_rx();
    let j = thread::spawn(move || {
        for i in 0..10_000 {
            c.broadcast(i);
        }
    });

    for i in 0..10_000 {
        assert_eq!(r1.recv(), Ok(i));
    }

    j.join().unwrap();
    assert_eq!(r1.try_recv(), Err(mpsc::TryRecvError::Disconnected));
}

#[test]
#[cfg(not(miri))]
fn test_busy() {
    use std::thread;

    // start a bus with limited space
    let mut bus = bus::Bus::new(1);

    // first receiver only receives 5 items
    let mut rx1 = bus.add_rx();
    let t1 = thread::spawn(move || {
        for _ in 0..5 {
            rx1.recv().unwrap();
        }
        drop(rx1);
    });

    // second receiver receives 10 items
    let mut rx2 = bus.add_rx();
    let t2 = thread::spawn(move || {
        for _ in 0..10 {
            rx2.recv().unwrap();
        }
        drop(rx2);
    });

    // let receivers start
    std::thread::sleep(time::Duration::from_millis(500));

    // try to send 25 items -- should work fine
    for i in 0..25 {
        std::thread::sleep(time::Duration::from_millis(100));
        match bus.try_broadcast(i) {
            Ok(_) => (),
            Err(e) => println!("Broadcast failed {}", e),
        }
    }

    // done sending -- wait for receivers (which should already be done)
    t1.join().unwrap();
    t2.join().unwrap();
    assert!(true);
}
<|endoftext|>


<commit_before>extern crate chattium_oxide_lib as cho;  // Chang
extern crate rand;

#[cfg(test)]
mod user {
	use rand::{self, Rng};
	use cho::*;
	use std::net::Ipv4Addr;

	fn random_ip<Rand>(rng: &mut Rand) -> (Ipv4Addr, u16) where Rand: Rng {
		(Ipv4Addr::new(rng.gen(), rng.gen(), rng.gen(), rng.gen()), rng.gen())
	}

	fn random_name<Rand>(rng: &mut Rand) -> String where Rand: Rng {
		rng.gen_ascii_chars().take(10).collect()
	}

	#[test]
	fn equal_because_of_different_names_same_ips() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {
		            	100000
		            } else {
		            	1000
		            };

		for _ in 1..times {
			let ip = random_ip(&mut rng);
			let user_1 = ChatUser::get(random_name(&mut rng), ip);
			let user_2 = ChatUser::get(random_name(&mut rng), ip);
			assert_eq!(user_1, user_2);
		}
	}

	#[test]
	fn unequal_because_of_different_ips_same_names() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {100000} else {1000};

		for _ in 1..times {
			let user_1 = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
			let user_2 = ChatUser::get(user_1.name.clone(), random_ip(&mut rng));
			assert!(user_1 != user_2);
		}
	}

	#[test]
	fn name_of_instance_from_get_is_equal_to_passed_name() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {100000} else {1000};

		for _ in 1..times {
			let name = random_name(&mut rng);
			let user = ChatUser::get(name.clone(), "0.0.0.0:0");
			assert_eq!(user.name, name);
		}
	}

	#[test]
	fn cloner_eq_clonee() {
		let mut rng = rand::thread_rng();

		let user = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
		let clone = user.clone();
		assert_eq!(user, clone);
		assert_eq!(user.name, clone.name);
	}
}
<commit_msg>Add JSON transserialization tests for ChatUser<commit_after>extern crate chattium_oxide_lib as cho;  // Chang
extern crate rand;

use rand::Rng;
use std::net::Ipv4Addr;

fn random_ip<Rand: Rng>(rng: &mut Rand) -> (Ipv4Addr, u16) {
	(Ipv4Addr::new(rng.gen(), rng.gen(), rng.gen(), rng.gen()), rng.gen())
}

fn random_name<Rand: Rng>(rng: &mut Rand) -> String {
	rng.gen_ascii_chars().take(10).collect()
}

#[cfg(test)]
mod user {
	use random_ip;
	use random_name;
	use rand;
	use cho::*;

	#[test]
	fn self_eq_self() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {100000} else {1000};

		for _ in 1..times {
			let user = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
			assert_eq!(user, user);
		}
	}

	#[test]
	fn equal_because_of_different_names_same_ips() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {100000} else {1000};

		for _ in 1..times {
			let ip = random_ip(&mut rng);
			let user_1 = ChatUser::get(random_name(&mut rng), ip);
			let user_2 = ChatUser::get(random_name(&mut rng), ip);
			assert_eq!(user_1, user_2);
		}
	}

	#[test]
	fn unequal_because_of_different_ips_same_names() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {100000} else {1000};

		for _ in 1..times {
			let user_1 = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
			let user_2 = ChatUser::get(user_1.name.clone(), random_ip(&mut rng));
			assert!(user_1 != user_2);
		}
	}

	#[test]
	fn name_of_instance_from_get_is_equal_to_passed_name() {
		let mut rng = rand::thread_rng();
		let times = if cfg!(feature = "ci") {100000} else {1000};

		for _ in 1..times {
			let name = random_name(&mut rng);
			let user = ChatUser::get(name.clone(), "0.0.0.0:0");
			assert_eq!(user.name, name);
		}
	}

	#[test]
	fn cloner_eq_clonee() {
		let mut rng = rand::thread_rng();

		let user = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
		let clone = user.clone();
		assert_eq!(user, clone);
		assert_eq!(user.name, clone.name);
	}

	mod j_son {
		use random_ip;
		use random_name;
		use rand;
		use cho::*;
		use cho::json::*;

		#[test]
		fn full_transserializes_properly() {
			let mut rng = rand::thread_rng();
			let times = if cfg!(feature = "ci") {100000} else {1000};

			for _ in 1..times {
				let user = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
				let trans = ChatUser::from_json(user.to_json()).expect("Full transserialization");
				assert_eq!(user, trans);
				assert_eq!(user.name, trans.name);
			}
		}

		#[test]
		fn ipless_transserializes_properly() {
			let mut rng = rand::thread_rng();
			let times = if cfg!(feature = "ci") {100000} else {1000};

			for _ in 1..times {
				let user = ChatUser::me(random_name(&mut rng));
				let trans = ChatUser::from_json(user.to_json()).expect("IP-less transserialization");
				assert_eq!(user, trans);
				assert_eq!(user.name, trans.name);
			}
		}
		#[test]
		fn full_transserializes_properly_through_string() {
			let mut rng = rand::thread_rng();
			let times = if cfg!(feature = "ci") {100000} else {1000};

			for _ in 1..times {
				let user = ChatUser::get(random_name(&mut rng), random_ip(&mut rng));
				let user_s = user.to_json_string().expect("Full serialization to string");
				let trans = ChatUser::from_json_string(&user_s).expect("Full deserialization from string");
				assert_eq!(user, trans);
				assert_eq!(user.name, trans.name);
			}
		}

		#[test]
		fn ipless_transserializes_properly_through_string() {
			let mut rng = rand::thread_rng();
			let times = if cfg!(feature = "ci") {100000} else {1000};

			for _ in 1..times {
				let user = ChatUser::me(random_name(&mut rng));
				let user_s = user.to_json_string().expect("IP-less serialization to string");
				let trans = ChatUser::from_json_string(&user_s).expect("IP-less deserialization from string");
				assert_eq!(user, trans);
				assert_eq!(user.name, trans.name);
			}
		}

		#[test]
		//#[should_fail]  // The attribute `should_fail` is currently unknown to the compiler and may have meaning added to it in the future
		fn deserialization_from_malformed_fails() {
			ChatUser::from_json_string(&"{\"user\": \"you\"}".to_string()).unwrap_err();
		}
	}
}
<|endoftext|>


<commit_before>#[feature(globs)];

extern mod wx;

use std::rt::start_on_main_thread;
use std::vec;

use wx::*;
use wx::native::*;
use wx::wrapper::*;

static nullptr: *u8 = 0 as *u8;

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    start_on_main_thread(argc, argv, crate_map, on_main)
}

#[fixed_stack_segment]
fn on_main() {
    let closure = wxClosure::new(wx_main as *u8, nullptr);
    let args: ~[*i32] = ~[];
    ELJApp::initializeC(closure, args.len() as i32, vec::raw::to_ptr(args) as *i32);
}

#[fixed_stack_segment]
fn wxT(s: &str) -> wxString {
    unsafe {
        do s.to_c_str().with_ref |c_str| {
            wxString(wxString_CreateUTF8(c_str as *u8))
        }
    }
}

extern "C"
fn wx_main() {
    unsafe {
        // not mandatory
        // ELJApp::initAllImageHandlers();
        let idAny = -1;
        let defaultFrameStyle = 536878656 | 4194304;

        let frame = wxFrame::new(wxWindow(nullptr), idAny, wxT("Hello, wxRust!"), -1, -1, -1, -1, defaultFrameStyle);
        println("OK");

        let menubar = wxMenuBar::new(0);

        let fileMenu = wxMenu::new(wxT("File2"), 1);
        let fileNew = wxMenuItem::newEx(idAny, wxT("New"), wxT("Create a new file."), 0, wxMenu(nullptr));
        fileMenu.appendItem(fileNew);

        menubar.append(fileMenu, wxT("File"));

        frame.setMenuBar(menubar);

        let id = 30;
        let button = wxButton::new(frame, id, wxT("Push me!"), 10, 10, 50, 30, 0);
        fn button_clicked(fun: *u8, data: *u8, evt: *u8) {
            println("hello!");
            let frame = wxFrame(data);
            let msgDlg = wxMessageDialog::new(frame, wxT("Pushed!!"), wxT("The Button"), 0);
            msgDlg.showModal();
        }
        let closure = wxClosure::new(button_clicked as *u8, frame.handle());
        button.connect(id, id, expEVT_COMMAND_BUTTON_CLICKED(), closure.handle());

        frame.show();
        frame.raise();
    }
}

<commit_msg>Remove the strange menu top label.<commit_after>#[feature(globs)];

extern mod wx;

use std::rt::start_on_main_thread;
use std::vec;

use wx::*;
use wx::native::*;
use wx::wrapper::*;

static nullptr: *u8 = 0 as *u8;

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    start_on_main_thread(argc, argv, crate_map, on_main)
}

#[fixed_stack_segment]
fn on_main() {
    let closure = wxClosure::new(wx_main as *u8, nullptr);
    let args: ~[*i32] = ~[];
    ELJApp::initializeC(closure, args.len() as i32, vec::raw::to_ptr(args) as *i32);
}

#[fixed_stack_segment]
fn wxT(s: &str) -> wxString {
    unsafe {
        do s.to_c_str().with_ref |c_str| {
            wxString(wxString_CreateUTF8(c_str as *u8))
        }
    }
}

extern "C"
fn wx_main() {
    unsafe {
        // not mandatory
        // ELJApp::initAllImageHandlers();
        let idAny = -1;
        let defaultFrameStyle = 536878656 | 4194304;

        let frame = wxFrame::new(wxWindow(nullptr), idAny, wxT("Hello, wxRust!"), -1, -1, -1, -1, defaultFrameStyle);
        println("OK");

        let menubar = wxMenuBar::new(0);

        let fileMenu = wxMenu::new(wxT(""), 0);
        let fileNew = wxMenuItem::newEx(idAny, wxT("New"), wxT("Create a new file."), 0, wxMenu(nullptr));
        fileMenu.appendItem(fileNew);

        menubar.append(fileMenu, wxT("File"));

        frame.setMenuBar(menubar);

        let id = 30;
        let button = wxButton::new(frame, id, wxT("Push me!"), 10, 10, 50, 30, 0);
        fn button_clicked(fun: *u8, data: *u8, evt: *u8) {
            println("hello!");
            let frame = wxFrame(data);
            let msgDlg = wxMessageDialog::new(frame, wxT("Pushed!!"), wxT("The Button"), 0);
            msgDlg.showModal();
        }
        let closure = wxClosure::new(button_clicked as *u8, frame.handle());
        button.connect(id, id, expEVT_COMMAND_BUTTON_CLICKED(), closure.handle());

        frame.show();
        frame.raise();
    }
}

<|endoftext|>