<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.
 */

//! This module is responsible to build a single project. It does not handle
//! watch mode or other state.

mod apply_transforms;
mod artifact_content;
pub mod artifact_writer;
mod build_ir;
mod build_schema;
mod generate_artifacts;
pub mod generate_extra_artifacts;
mod persist_operations;
mod source_control;
mod validate;

use crate::compiler_state::{ArtifactMapKind, CompilerState, ProjectName, SourceSetName};
use crate::config::{Config, ProjectConfig};
use crate::errors::BuildProjectError;
use crate::{artifact_map::ArtifactMap, graphql_asts::GraphQLAsts};
pub use apply_transforms::apply_transforms;
pub use apply_transforms::Programs;
use build_ir::BuildIRResult;
pub use build_ir::SourceHashes;
pub use build_schema::build_schema;
use common::{PerfLogEvent, PerfLogger};
use fnv::{FnvHashMap, FnvHashSet};
pub use generate_artifacts::{
    create_path_for_artifact, generate_artifacts, Artifact, ArtifactContent,
};
use generate_extra_artifacts::generate_extra_artifacts;
use graphql_ir::Program;
use interner::StringKey;
use log::info;
use relay_codegen::Printer;
use schema::Schema;
pub use source_control::add_to_mercurial;
use std::{collections::hash_map::Entry, path::PathBuf, sync::Arc};
pub use validate::validate;

fn build_programs(
    config: &Config,
    project_config: &ProjectConfig,
    compiler_state: &CompilerState,
    graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    schema: Arc<Schema>,
    log_event: &impl PerfLogEvent,
    perf_logger: Arc<impl PerfLogger + 'static>,
) -> Result<(Programs, Arc<SourceHashes>), BuildProjectError> {
    let project_name = project_config.name;
    let is_incremental_build =
        compiler_state.has_processed_changes() && !compiler_state.has_breaking_schema_change();

    // Build a type aware IR.
    let BuildIRResult {
        ir,
        base_fragment_names,
        source_hashes,
    } = log_event.time("build_ir_time", || {
        build_ir::build_ir(project_config, &schema, graphql_asts, is_incremental_build)
            .map_err(|errors| BuildProjectError::ValidationErrors { errors })
    })?;

    // Turn the IR into a base Program.
    let program = log_event.time("build_program_time", || {
        Program::from_definitions(schema, ir)
    });

    // Call validation rules that go beyond type checking.
    log_event.time("validate_time", || {
        // TODO(T63482263): Pass connection interface from configuration
        validate(&program, &config.connection_interface)
            .map_err(|errors| BuildProjectError::ValidationErrors { errors })
    })?;

    // Apply various chains of transforms to create a set of output programs.
    let programs = log_event.time("apply_transforms_time", || {
        apply_transforms(
            project_name,
            Arc::new(program),
            Arc::new(base_fragment_names),
            &config.connection_interface,
            Arc::new(config.feature_flags),
            perf_logger,
        )
        .map_err(|errors| BuildProjectError::ValidationErrors { errors })
    })?;

    Ok((programs, Arc::new(source_hashes)))
}

pub fn check_project(
    config: &Config,
    project_config: &ProjectConfig,
    compiler_state: &CompilerState,
    graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    schema: Arc<Schema>,
    perf_logger: Arc<impl PerfLogger + 'static>,
) -> Result<Programs, BuildProjectError> {
    let log_event = perf_logger.create_event("check_project");
    let build_time = log_event.start("check_time");
    let project_name = project_config.name.lookup();
    log_event.string("project", project_name.to_string());

    let (programs, _) = build_programs(
        config,
        project_config,
        compiler_state,
        graphql_asts,
        schema,
        &log_event,
        Arc::clone(&perf_logger),
    )?;

    log_event.stop(build_time);
    perf_logger.complete_event(log_event);

    Ok(programs)
}

pub fn build_project(
    config: &Config,
    project_config: &ProjectConfig,
    compiler_state: &CompilerState,
    graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    perf_logger: Arc<impl PerfLogger + 'static>,
) -> Result<(ProjectName, Arc<Schema>, Programs, Vec<Artifact>), BuildProjectError> {
    let log_event = perf_logger.create_event("build_project");
    let build_time = log_event.start("build_project_time");
    let project_name = project_config.name.lookup();
    log_event.string("project", project_name.to_string());

    // Construct a schema instance including project specific extensions.
    let schema = log_event.time("build_schema_time", || {
        Arc::new(build_schema(compiler_state, project_config))
    });

    // Apply different transform pipelines to produce the `Programs`.
    let (programs, source_hashes) = build_programs(
        config,
        project_config,
        compiler_state,
        graphql_asts,
        Arc::clone(&schema),
        &log_event,
        Arc::clone(&perf_logger),
    )?;

    // Generate artifacts by collecting information from the `Programs`.
    let artifacts_timer = log_event.start("generate_artifacts_time");
    let artifacts = generate_artifacts(project_config, &programs, Arc::clone(&source_hashes))?;
    log_event.stop(artifacts_timer);

    log_event.number(
        "generated_artifacts",
        programs.reader.document_count() + programs.normalization.document_count(),
    );

    log_event.stop(build_time);
    perf_logger.complete_event(log_event);
    Ok((project_config.name, schema, programs, artifacts))
}

pub async fn commit_project(
    config: &Config,
    project_config: &ProjectConfig,
    perf_logger: Arc<impl PerfLogger + 'static>,
    schema: &Schema,
    programs: Programs,
    mut artifacts: Vec<Artifact>,
    artifact_map: Arc<ArtifactMapKind>,
    // Definitions that are removed from the previous artifact map
    removed_definition_names: Vec<StringKey>,
    // Dirty artrifacts that should be removed
    mut artifacts_to_remove: FnvHashSet<PathBuf>,
) -> Result<ArtifactMap, BuildProjectError> {
    let log_event = perf_logger.create_event("commit_project");
    let commit_time = log_event.start("commit_project_time");

    if let Some(ref artifact_persister) = config.artifact_persister {
        if let Some(ref persist_config) = project_config.persist {
            let persist_operations_timer = log_event.start("persist_operations_time");
            persist_operations::persist_operations(
                &mut artifacts,
                &config.root_dir,
                &persist_config,
                &artifact_persister,
            )
            .await?;
            log_event.stop(persist_operations_timer);
        }
    }

    // In some cases we need to create additional (platform specific) artifacts
    // For that, we will use `generate_extra_operation_artifacts` from the configs
    if let Some(generate_extra_operation_artifacts_fn) = &config.generate_extra_operation_artifacts
    {
        log_event.time("generate_extra_operation_artifacts_time", || {
            generate_extra_artifacts(
                schema,
                project_config,
                &mut artifacts,
                generate_extra_operation_artifacts_fn,
            )
        });
    }

    // Write the generated artifacts to disk. This step is separate from
    // generating artifacts or persisting to avoid partial writes in case of
    // errors as much as possible.
    let next_artifact_map = match Arc::as_ref(&artifact_map) {
        ArtifactMapKind::Unconnected(existing_artifacts) => {
            let mut existing_artifacts = existing_artifacts.clone();
            let mut printer = Printer::default();

            log_event.time("write_artifacts_time", || {
                for artifact in &artifacts {
                    if !existing_artifacts.remove(&artifact.path) {
                        info!(
                            "[{}] NEW: {:?} -> {:?}",
                            project_config.name, &artifact.source_definition_names, &artifact.path
                        );
                    }

                    let path = config.root_dir.join(&artifact.path);
                    let content =
                        artifact
                            .content
                            .as_bytes(config, project_config, &mut printer, schema);
                    config.artifact_writer.write_if_changed(path, content)?;
                }
                Ok(())
            })?;

            log_event.time("delete_artifacts_time", || {
                for remaining_artifact in &existing_artifacts {
                    let path = config.root_dir.join(remaining_artifact);
                    config.artifact_writer.remove(path)?;
                }
                Ok(())
            })?;

            ArtifactMap::from(artifacts)
        }
        ArtifactMapKind::Mapping(artifact_map) => {
            let mut printer = Printer::default();
            let mut artifact_map = artifact_map.clone();

            // All generated paths for removed definitions should be removed
            log_event.time("delete_artifacts_time", || {
                for name in &removed_definition_names {
                    if let Some(artifact) = artifact_map.0.remove(&name) {
                        for (path, _) in artifact {
                            artifacts_to_remove.insert(path);
                        }
                    }
                }
                Ok(())
            })?;

            log_event.time("write_artifacts_time", || {
                let mut current_paths_map = ArtifactMap::default();
                // Write or update artifacts
                for artifact in artifacts {
                    // If the path is written, stop removing it
                    artifacts_to_remove.remove(&artifact.path);
                    let path = config.root_dir.join(&artifact.path);
                    let content =
                        artifact
                            .content
                            .as_bytes(config, project_config, &mut printer, schema);
                    config.artifact_writer.write_if_changed(path, content)?;
                    current_paths_map.insert(artifact);
                }
                // Check previous artifact map and delete any removed artifacts
                for (definition_name, artifact_tuples) in current_paths_map.0 {
                    match artifact_map.0.entry(definition_name) {
                        Entry::Occupied(mut entry) => {
                            let prev_tuples = entry.get_mut();
                            for (prev_path, _) in prev_tuples.drain(..) {
                                if !artifact_tuples.iter().any(|t| t.0 == prev_path) {
                                    artifacts_to_remove.insert(prev_path);
                                }
                            }
                            prev_tuples.extend(artifact_tuples.into_iter());
                        }
                        Entry::Vacant(entry) => {
                            entry.insert(artifact_tuples);
                        }
                    }
                }
                Ok(())
            })?;

            // All artifacts are committed, the remaining dirty artifacts are no longer required
            for path in artifacts_to_remove {
                config.artifact_writer.remove(config.root_dir.join(path))?;
            }

            artifact_map
        }
    };

    info!(
        "[{}] compiled documents: {} reader, {} normalization, {} operation text",
        project_config.name,
        programs.reader.document_count(),
        programs.normalization.document_count(),
        programs.operation_text.document_count()
    );
    log_event.stop(commit_time);
    perf_logger.complete_event(log_event);

    Ok(next_artifact_map)
}
<commit_msg>Fix deletion of normalization files in watch mode<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.
 */

//! This module is responsible to build a single project. It does not handle
//! watch mode or other state.

mod apply_transforms;
mod artifact_content;
pub mod artifact_writer;
mod build_ir;
mod build_schema;
mod generate_artifacts;
pub mod generate_extra_artifacts;
mod persist_operations;
mod source_control;
mod validate;

use crate::compiler_state::{ArtifactMapKind, CompilerState, ProjectName, SourceSetName};
use crate::config::{Config, ProjectConfig};
use crate::errors::BuildProjectError;
use crate::{artifact_map::ArtifactMap, graphql_asts::GraphQLAsts};
pub use apply_transforms::apply_transforms;
pub use apply_transforms::Programs;
use build_ir::BuildIRResult;
pub use build_ir::SourceHashes;
pub use build_schema::build_schema;
use common::{PerfLogEvent, PerfLogger};
use fnv::{FnvHashMap, FnvHashSet};
pub use generate_artifacts::{
    create_path_for_artifact, generate_artifacts, Artifact, ArtifactContent,
};
use generate_extra_artifacts::generate_extra_artifacts;
use graphql_ir::Program;
use interner::StringKey;
use log::info;
use relay_codegen::Printer;
use schema::Schema;
pub use source_control::add_to_mercurial;
use std::{collections::hash_map::Entry, path::PathBuf, sync::Arc};
pub use validate::validate;

fn build_programs(
    config: &Config,
    project_config: &ProjectConfig,
    compiler_state: &CompilerState,
    graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    schema: Arc<Schema>,
    log_event: &impl PerfLogEvent,
    perf_logger: Arc<impl PerfLogger + 'static>,
) -> Result<(Programs, Arc<SourceHashes>), BuildProjectError> {
    let project_name = project_config.name;
    let is_incremental_build =
        compiler_state.has_processed_changes() && !compiler_state.has_breaking_schema_change();

    // Build a type aware IR.
    let BuildIRResult {
        ir,
        base_fragment_names,
        source_hashes,
    } = log_event.time("build_ir_time", || {
        build_ir::build_ir(project_config, &schema, graphql_asts, is_incremental_build)
            .map_err(|errors| BuildProjectError::ValidationErrors { errors })
    })?;

    // Turn the IR into a base Program.
    let program = log_event.time("build_program_time", || {
        Program::from_definitions(schema, ir)
    });

    // Call validation rules that go beyond type checking.
    log_event.time("validate_time", || {
        // TODO(T63482263): Pass connection interface from configuration
        validate(&program, &config.connection_interface)
            .map_err(|errors| BuildProjectError::ValidationErrors { errors })
    })?;

    // Apply various chains of transforms to create a set of output programs.
    let programs = log_event.time("apply_transforms_time", || {
        apply_transforms(
            project_name,
            Arc::new(program),
            Arc::new(base_fragment_names),
            &config.connection_interface,
            Arc::new(config.feature_flags),
            perf_logger,
        )
        .map_err(|errors| BuildProjectError::ValidationErrors { errors })
    })?;

    Ok((programs, Arc::new(source_hashes)))
}

pub fn check_project(
    config: &Config,
    project_config: &ProjectConfig,
    compiler_state: &CompilerState,
    graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    schema: Arc<Schema>,
    perf_logger: Arc<impl PerfLogger + 'static>,
) -> Result<Programs, BuildProjectError> {
    let log_event = perf_logger.create_event("check_project");
    let build_time = log_event.start("check_time");
    let project_name = project_config.name.lookup();
    log_event.string("project", project_name.to_string());

    let (programs, _) = build_programs(
        config,
        project_config,
        compiler_state,
        graphql_asts,
        schema,
        &log_event,
        Arc::clone(&perf_logger),
    )?;

    log_event.stop(build_time);
    perf_logger.complete_event(log_event);

    Ok(programs)
}

pub fn build_project(
    config: &Config,
    project_config: &ProjectConfig,
    compiler_state: &CompilerState,
    graphql_asts: &FnvHashMap<SourceSetName, GraphQLAsts>,
    perf_logger: Arc<impl PerfLogger + 'static>,
) -> Result<(ProjectName, Arc<Schema>, Programs, Vec<Artifact>), BuildProjectError> {
    let log_event = perf_logger.create_event("build_project");
    let build_time = log_event.start("build_project_time");
    let project_name = project_config.name.lookup();
    log_event.string("project", project_name.to_string());

    // Construct a schema instance including project specific extensions.
    let schema = log_event.time("build_schema_time", || {
        Arc::new(build_schema(compiler_state, project_config))
    });

    // Apply different transform pipelines to produce the `Programs`.
    let (programs, source_hashes) = build_programs(
        config,
        project_config,
        compiler_state,
        graphql_asts,
        Arc::clone(&schema),
        &log_event,
        Arc::clone(&perf_logger),
    )?;

    // Generate artifacts by collecting information from the `Programs`.
    let artifacts_timer = log_event.start("generate_artifacts_time");
    let artifacts = generate_artifacts(project_config, &programs, Arc::clone(&source_hashes))?;
    log_event.stop(artifacts_timer);

    log_event.number(
        "generated_artifacts",
        programs.reader.document_count() + programs.normalization.document_count(),
    );

    log_event.stop(build_time);
    perf_logger.complete_event(log_event);
    Ok((project_config.name, schema, programs, artifacts))
}

pub async fn commit_project(
    config: &Config,
    project_config: &ProjectConfig,
    perf_logger: Arc<impl PerfLogger + 'static>,
    schema: &Schema,
    programs: Programs,
    mut artifacts: Vec<Artifact>,
    artifact_map: Arc<ArtifactMapKind>,
    // Definitions that are removed from the previous artifact map
    removed_definition_names: Vec<StringKey>,
    // Dirty artrifacts that should be removed if no longer in the artiracts map
    mut artifacts_to_remove: FnvHashSet<PathBuf>,
) -> Result<ArtifactMap, BuildProjectError> {
    let log_event = perf_logger.create_event("commit_project");
    let commit_time = log_event.start("commit_project_time");

    if let Some(ref artifact_persister) = config.artifact_persister {
        if let Some(ref persist_config) = project_config.persist {
            let persist_operations_timer = log_event.start("persist_operations_time");
            persist_operations::persist_operations(
                &mut artifacts,
                &config.root_dir,
                &persist_config,
                &artifact_persister,
            )
            .await?;
            log_event.stop(persist_operations_timer);
        }
    }

    // In some cases we need to create additional (platform specific) artifacts
    // For that, we will use `generate_extra_operation_artifacts` from the configs
    if let Some(generate_extra_operation_artifacts_fn) = &config.generate_extra_operation_artifacts
    {
        log_event.time("generate_extra_operation_artifacts_time", || {
            generate_extra_artifacts(
                schema,
                project_config,
                &mut artifacts,
                generate_extra_operation_artifacts_fn,
            )
        });
    }

    // Write the generated artifacts to disk. This step is separate from
    // generating artifacts or persisting to avoid partial writes in case of
    // errors as much as possible.
    let next_artifact_map = match Arc::as_ref(&artifact_map) {
        ArtifactMapKind::Unconnected(existing_artifacts) => {
            let mut existing_artifacts = existing_artifacts.clone();
            let mut printer = Printer::default();

            log_event.time("write_artifacts_time", || {
                for artifact in &artifacts {
                    if !existing_artifacts.remove(&artifact.path) {
                        info!(
                            "[{}] NEW: {:?} -> {:?}",
                            project_config.name, &artifact.source_definition_names, &artifact.path
                        );
                    }

                    let path = config.root_dir.join(&artifact.path);
                    let content =
                        artifact
                            .content
                            .as_bytes(config, project_config, &mut printer, schema);
                    config.artifact_writer.write_if_changed(path, content)?;
                }
                Ok(())
            })?;

            log_event.time("delete_artifacts_time", || {
                for remaining_artifact in &existing_artifacts {
                    let path = config.root_dir.join(remaining_artifact);
                    config.artifact_writer.remove(path)?;
                }
                Ok(())
            })?;

            ArtifactMap::from(artifacts)
        }
        ArtifactMapKind::Mapping(artifact_map) => {
            let mut printer = Printer::default();
            let mut artifact_map = artifact_map.clone();
            let mut current_paths_map = ArtifactMap::default();

            log_event.time("write_artifacts_incremental_time", || {
                // Write or update artifacts
                for artifact in artifacts {
                    let path = config.root_dir.join(&artifact.path);
                    let content =
                        artifact
                            .content
                            .as_bytes(config, project_config, &mut printer, schema);
                    config.artifact_writer.write_if_changed(path, content)?;
                    current_paths_map.insert(artifact);
                }
                Ok(())
            })?;

            log_event.time("update_artifact_map_time", || {
                // All generated paths for removed definitions should be removed
                for name in &removed_definition_names {
                    if let Some(artifact) = artifact_map.0.remove(&name) {
                        for (path, _) in artifact {
                            artifacts_to_remove.insert(path);
                        }
                    }
                }
                // Update the artifact map, and delete any removed artifacts
                for (definition_name, artifact_tuples) in current_paths_map.0 {
                    match artifact_map.0.entry(definition_name) {
                        Entry::Occupied(mut entry) => {
                            let prev_tuples = entry.get_mut();
                            for (prev_path, _) in prev_tuples.drain(..) {
                                if !artifact_tuples.iter().any(|t| t.0 == prev_path) {
                                    artifacts_to_remove.insert(prev_path);
                                }
                            }
                            prev_tuples.extend(artifact_tuples.into_iter());
                        }
                        Entry::Vacant(entry) => {
                            entry.insert(artifact_tuples);
                        }
                    }
                }
                // Filter out any artifact that is in the artifact map
                for paths in artifact_map.0.values() {
                    for (path, _) in paths {
                        artifacts_to_remove.remove(path);
                    }
                }
            });

            log_event.time("delete_artifacts_incremental_time", || {
                // The remaining dirty artifacts are no longer required
                for path in artifacts_to_remove {
                    config.artifact_writer.remove(config.root_dir.join(path))?;
                }
                Ok(())
            })?;

            artifact_map
        }
    };

    info!(
        "[{}] compiled documents: {} reader, {} normalization, {} operation text",
        project_config.name,
        programs.reader.document_count(),
        programs.normalization.document_count(),
        programs.operation_text.document_count()
    );
    log_event.stop(commit_time);
    perf_logger.complete_event(log_event);

    Ok(next_artifact_map)
}
<|endoftext|>


<commit_before>// Copyright 2020 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::fs::File;
use tempfile::tempfile;
use wasmbin::Module;

fn deep_module() -> Module {
    use wasmbin::builtins::Blob;
    use wasmbin::instructions::{Expression, Instruction};
    use wasmbin::sections::FuncBody;
    use wasmbin::types::BlockType;

    let mut expr = Expression::default();
    for _ in 0..100_000 {
        expr.push(Instruction::BlockStart(BlockType::Empty));
    }
    for _ in 0..100_000 {
        expr.push(Instruction::End);
    }
    Module {
        sections: vec![vec![Blob::from(FuncBody {
            locals: Default::default(),
            expr,
        })]
        .into()],
        ..Default::default()
    }
}

fn bench_parse(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_parse"), |b| {
        b.iter(|| {
            let f = File::open("benches/fixture.wasm").unwrap();
            Module::decode_from(f).unwrap()
        })
    });
}

fn bench_parse_buf(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_parse_buf"), |b| {
        b.iter(|| {
            let f = File::open("benches/fixture.wasm").unwrap();
            let f = std::io::BufReader::new(f);
            Module::decode_from(f).unwrap()
        })
    });
}

fn bench_parse_vec(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_parse_vec"), |b| {
        let f = std::fs::read("benches/fixture.wasm").unwrap();
        b.iter(|| {
            let f = black_box(f.as_slice());
            Module::decode_from(f).unwrap()
        })
    });
}

fn bench_parse_deep_module(c: &mut Criterion) {
    c.bench_function(
        concat!(stringify!($name), "::bench_parse_deep_module"),
        |b| {
            let f = deep_module().encode_into(Vec::new()).unwrap();
            b.iter(|| {
                let f = black_box(f.as_slice());
                Module::decode_from(f).unwrap()
            })
        },
    );
}

fn read_module() -> Module {
    let f = std::fs::read("benches/fixture.wasm").unwrap();
    Module::decode_from(f.as_slice()).unwrap()
}

fn bench_write(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_write"), |b| {
        let m = read_module();
        b.iter(|| {
            let f = tempfile().unwrap();
            black_box(&m).encode_into(f).unwrap()
        })
    });
}

fn bench_write_buf(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_write_buf"), |b| {
        let m = read_module();
        b.iter(|| {
            let f = tempfile().unwrap();
            let f = std::io::BufWriter::new(f);
            black_box(&m).encode_into(f).unwrap()
        })
    });
}

fn bench_write_vec(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_write_vec"), |b| {
        let m = read_module();
        b.iter(|| black_box(&m).encode_into(Vec::new()).unwrap())
    });
}

fn bench_write_deep_module(c: &mut Criterion) {
    c.bench_function(
        concat!(stringify!($name), "::bench_write_deep_module"),
        |b| {
            let m = deep_module();
            b.iter(|| black_box(&m).encode_into(Vec::new()).unwrap())
        },
    );
}

criterion_group! {
    name = benches;
    config = Criterion::default().sample_size(20);
    targets =
        bench_parse,
        bench_parse_buf,
        bench_parse_vec,
        bench_parse_deep_module,
        bench_write,
        bench_write_buf,
        bench_write_vec,
        bench_write_deep_module,
}

criterion_main!(benches);
<commit_msg>unlazify in benchmark<commit_after>#[cfg(not(feature = "bulk-memory-operations"))]
compile_error!("benches/fixture.wasm requires the bulk-memory-operations feature to be enabled.");

// Copyright 2020 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::fs::File;
use tempfile::tempfile;
use wasmbin::io::DecodeError;
use wasmbin::visit::{Visit, VisitError};
use wasmbin::Module;

fn deep_module() -> Module {
    use wasmbin::builtins::Blob;
    use wasmbin::instructions::{Expression, Instruction};
    use wasmbin::sections::FuncBody;
    use wasmbin::types::BlockType;

    let mut expr = Expression::default();
    for _ in 0..100_000 {
        expr.push(Instruction::BlockStart(BlockType::Empty));
    }
    for _ in 0..100_000 {
        expr.push(Instruction::End);
    }
    Module {
        sections: vec![vec![Blob::from(FuncBody {
            locals: Default::default(),
            expr,
        })]
        .into()],
        ..Default::default()
    }
}

fn unlazify<T: Visit>(wasm: T) -> Result<T, DecodeError> {
    match wasm.visit(|()| {}) {
        Ok(()) => Ok(wasm),
        Err(err) => match err {
            VisitError::LazyDecode(err) => Err(err),
            VisitError::Custom(err) => match err {},
        },
    }
}

fn bench_parse(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_parse"), |b| {
        b.iter(|| {
            let f = File::open("benches/fixture.wasm").unwrap();
            unlazify(Module::decode_from(f).unwrap())
        })
    });
}

fn bench_parse_buf(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_parse_buf"), |b| {
        b.iter(|| {
            let f = File::open("benches/fixture.wasm").unwrap();
            let f = std::io::BufReader::new(f);
            unlazify(Module::decode_from(f).unwrap())
        })
    });
}

fn bench_parse_vec(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_parse_vec"), |b| {
        let f = std::fs::read("benches/fixture.wasm").unwrap();
        b.iter(|| {
            let f = black_box(f.as_slice());
            unlazify(Module::decode_from(f).unwrap())
        })
    });
}

fn bench_parse_deep_module(c: &mut Criterion) {
    c.bench_function(
        concat!(stringify!($name), "::bench_parse_deep_module"),
        |b| {
            let f = deep_module().encode_into(Vec::new()).unwrap();
            b.iter(|| {
                let f = black_box(f.as_slice());
                unlazify(Module::decode_from(f).unwrap())
            })
        },
    );
}

fn read_module() -> Module {
    let f = std::fs::read("benches/fixture.wasm").unwrap();
    unlazify(Module::decode_from(f.as_slice()).unwrap()).unwrap()
}

fn bench_write(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_write"), |b| {
        let m = read_module();
        b.iter(|| {
            let f = tempfile().unwrap();
            black_box(&m).encode_into(f).unwrap()
        })
    });
}

fn bench_write_buf(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_write_buf"), |b| {
        let m = read_module();
        b.iter(|| {
            let f = tempfile().unwrap();
            let f = std::io::BufWriter::new(f);
            black_box(&m).encode_into(f).unwrap()
        })
    });
}

fn bench_write_vec(c: &mut Criterion) {
    c.bench_function(concat!(stringify!($name), "::bench_write_vec"), |b| {
        let m = read_module();
        b.iter(|| black_box(&m).encode_into(Vec::new()).unwrap())
    });
}

fn bench_write_deep_module(c: &mut Criterion) {
    c.bench_function(
        concat!(stringify!($name), "::bench_write_deep_module"),
        |b| {
            let m = deep_module();
            b.iter(|| black_box(&m).encode_into(Vec::new()).unwrap())
        },
    );
}

criterion_group! {
    name = benches;
    config = Criterion::default().sample_size(20);
    targets =
        bench_parse,
        bench_parse_buf,
        bench_parse_vec,
        bench_parse_deep_module,
        bench_write,
        bench_write_buf,
        bench_write_vec,
        bench_write_deep_module,
}

criterion_main!(benches);
<|endoftext|>


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

// The task that handles all rendering/painting.

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::task::spawn_named;

use std::comm::{Chan, Port, SharedChan};
use extra::arc::Arc;

use buffer_map::BufferMap;
use font_context::{FontContext, FontContextInfo};
use display_list::DisplayListCollection;
use font_context::{FontContext, FontContextInfo};
use opts::Opts;
use render_context::RenderContext;

pub struct RenderLayer<T> {
    display_list_collection: Arc<DisplayListCollection<T>>,
    size: Size2D<uint>,
    color: Color
}

pub enum Msg<T> {
    RenderMsg(RenderLayer<T>),
    ReRenderMsg(~[BufferRequest], f32, Epoch),
    UnusedBufferMsg(~[~LayerBuffer]),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Chan<()>),
}

/// A request from the compositor to the renderer for tiles that need to be (re)displayed.
#[deriving(Clone)]
pub struct BufferRequest {
    // The rect in pixels that will be drawn to the screen
    screen_rect: Rect<uint>,

    // The rect in page coordinates that this tile represents
    page_rect: Rect<f32>,
}

pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferRequest {
    BufferRequest {
        screen_rect: screen_rect,
        page_rect: page_rect,
    }
}

// FIXME(rust#9155): this should be a newtype struct, but
// generic newtypes ICE when compiled cross-crate
pub struct RenderChan<T> {
    chan: SharedChan<Msg<T>>,
}

impl<T: Send> Clone for RenderChan<T> {
    fn clone(&self) -> RenderChan<T> {
        RenderChan {
            chan: self.chan.clone(),
        }
    }
}

impl<T: Send> RenderChan<T> {
    pub fn new() -> (Port<Msg<T>>, RenderChan<T>) {
        let (port, chan) = SharedChan::new();
        let render_chan = RenderChan {
            chan: chan,
        };
        (port, render_chan)
    }

    pub fn send(&self, msg: Msg<T>) {
        assert!(self.try_send(msg), "RenderChan.send: render port closed")
    }

    pub fn try_send(&self, msg: Msg<T>) -> bool {
        self.chan.try_send(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C,T> {
    id: PipelineId,
    port: Port<Msg<T>>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: ~FontContext,
    opts: Opts,

    /// A channel to the profiler.
    profiler_chan: ProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layer to be rendered
    render_layer: Option<RenderLayer<T>>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap<~LayerBuffer>,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
    pub fn create(id: PipelineId,
                  port: Port<Msg<T>>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  opts: Opts,
                  profiler_chan: ProfilerChan,
                  shutdown_chan: Chan<()>) {
        spawn_named("RenderTask", proc() {

            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: ~FontContext::new(FontContextInfo {
                        backend: opts.render_backend.clone(),
                        needs_font_list: false,
                        profiler_chan: profiler_chan.clone(),
                    }),
                    opts: opts,
                    profiler_chan: profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layer: None,

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                render_task.native_graphics_context.as_ref().map(
                    |ctx| render_task.buffer_map.clear(ctx));
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layer) => {
                    if self.paint_permission {
                        self.epoch.next();
                        self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
                    } else {
                        debug!("render_task: render ready msg");
                        self.constellation_chan.send(RendererReadyMsg(self.id));
                    }
                    self.render_layer = Some(render_layer);
                }
                ReRenderMsg(tiles, scale, epoch) => {
                    if self.epoch == epoch {
                        self.render(tiles, scale);
                    } else {
                        debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                    }
                }
                UnusedBufferMsg(unused_buffers) => {
                    // move_rev_iter is more efficient
                    for buffer in unused_buffers.move_rev_iter() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;
                    match self.render_layer {
                        Some(ref render_layer) => {
                            self.epoch.next();
                            self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
                        }
                        None => {}
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.send(());
                    break;
                }
            }
        }
    }

    fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
        let render_layer;
        match self.render_layer {
            Some(ref r_layer) => {
                render_layer = r_layer;
            }
            _ => return, // nothing to do
        }

        self.compositor.set_render_state(RenderingRenderState);
        time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = ~[];

            // Divide up the layer into tiles.
            time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone(), || {
                for tile in tiles.iter() {
                    let width = tile.screen_rect.size.width;
                    let height = tile.screen_rect.size.height;

                    let size = Size2D(width as i32, height as i32);
                    let draw_target = match self.graphics_context {
                        CpuGraphicsContext => {
                            DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                        }
                        GpuGraphicsContext => {
                            // FIXME(pcwalton): Cache the components of draw targets
                            // (texture color buffer, renderbuffers) instead of recreating them.
                            let draw_target =
                                DrawTarget::new_with_fbo(self.opts.render_backend,
                                                         native_graphics_context!(self),
                                                         size,
                                                         B8G8R8A8);
                            draw_target.make_current();
                            draw_target
                        }
                    };

                    {
                        // Build the render context.
                        let mut ctx = RenderContext {
                            draw_target: &draw_target,
                            font_ctx: &mut self.font_ctx,
                            opts: &self.opts,
                            page_rect: tile.page_rect,
                            screen_rect: tile.screen_rect,
                        };

                        // Apply the translation to render the tile we want.
                        let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                        let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                        let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                      -(tile.page_rect.origin.y) as AzFloat);

                        ctx.draw_target.set_transform(&matrix);

                        // Clear the buffer.
                        ctx.clear();

                        // Draw the display list.
                        profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
                            render_layer.display_list_collection.get().draw_lists_into_context(&mut ctx);
                            ctx.draw_target.flush();
                        });
                    }

                    // Extract the texture from the draw target and place it into its slot in the
                    // buffer. If using CPU rendering, upload it first.
                    //
                    // FIXME(pcwalton): We should supply the texture and native surface *to* the
                    // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                    let buffer = match self.graphics_context {
                        CpuGraphicsContext => {
                            let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                                Some(buffer) => {
                                    let mut buffer = buffer;
                                    buffer.rect = tile.page_rect;
                                    buffer.screen_pos = tile.screen_rect;
                                    buffer.resolution = scale;
                                    buffer.native_surface.mark_wont_leak();
                                    buffer
                                }
                                None => {
                                    // Create an empty native surface. We mark it as not leaking
                                    // in case it dies in transit to the compositor task.
                                    let mut native_surface: NativeSurface =
                                        layers::platform::surface::NativeSurfaceMethods::new(
                                            native_graphics_context!(self),
                                            Size2D(width as i32, height as i32),
                                            width as i32 * 4);
                                    native_surface.mark_wont_leak();

                                    ~LayerBuffer {
                                        native_surface: native_surface,
                                        rect: tile.page_rect,
                                        screen_pos: tile.screen_rect,
                                        resolution: scale,
                                        stride: (width * 4) as uint
                                    }
                                }
                            };

                            draw_target.snapshot().get_data_surface().with_data(|data| {
                                buffer.native_surface.upload(native_graphics_context!(self), data);
                                debug!("RENDERER uploading to native surface {:d}",
                                       buffer.native_surface.get_id() as int);
                            });

                            buffer
                        }
                        GpuGraphicsContext => {
                            draw_target.make_current();
                            let StolenGLResources {
                                surface: native_surface
                            } = draw_target.steal_gl_resources().unwrap();

                            // We mark the native surface as not leaking in case the surfaces
                            // die on their way to the compositor task.
                            let mut native_surface: NativeSurface =
                                NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                            native_surface.mark_wont_leak();

                            ~LayerBuffer {
                                native_surface: native_surface,
                                rect: tile.page_rect,
                                screen_pos: tile.screen_rect,
                                resolution: scale,
                                stride: (width * 4) as uint
                            }
                        }
                    };

                    new_buffers.push(buffer);
                }
            });

            let layer_buffer_set = ~LayerBufferSet {
                buffers: new_buffers,
            };

            debug!("render_task: returning surface");
            if self.paint_permission {
                self.compositor.paint(self.id, layer_buffer_set, self.epoch);
            } else {
                debug!("render_task: RendererReadyMsg send");
                self.constellation_chan.send(RendererReadyMsg(self.id));
            }
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

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

// The task that handles all rendering/painting.

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::task::spawn_named;

use std::comm::{Chan, Port, SharedChan};
use extra::arc::Arc;

use buffer_map::BufferMap;
use display_list::DisplayListCollection;
use font_context::{FontContext, FontContextInfo};
use opts::Opts;
use render_context::RenderContext;

pub struct RenderLayer<T> {
    display_list_collection: Arc<DisplayListCollection<T>>,
    size: Size2D<uint>,
    color: Color
}

pub enum Msg<T> {
    RenderMsg(RenderLayer<T>),
    ReRenderMsg(~[BufferRequest], f32, Epoch),
    UnusedBufferMsg(~[~LayerBuffer]),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Chan<()>),
}

/// A request from the compositor to the renderer for tiles that need to be (re)displayed.
#[deriving(Clone)]
pub struct BufferRequest {
    // The rect in pixels that will be drawn to the screen
    screen_rect: Rect<uint>,

    // The rect in page coordinates that this tile represents
    page_rect: Rect<f32>,
}

pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferRequest {
    BufferRequest {
        screen_rect: screen_rect,
        page_rect: page_rect,
    }
}

// FIXME(rust#9155): this should be a newtype struct, but
// generic newtypes ICE when compiled cross-crate
pub struct RenderChan<T> {
    chan: SharedChan<Msg<T>>,
}

impl<T: Send> Clone for RenderChan<T> {
    fn clone(&self) -> RenderChan<T> {
        RenderChan {
            chan: self.chan.clone(),
        }
    }
}

impl<T: Send> RenderChan<T> {
    pub fn new() -> (Port<Msg<T>>, RenderChan<T>) {
        let (port, chan) = SharedChan::new();
        let render_chan = RenderChan {
            chan: chan,
        };
        (port, render_chan)
    }

    pub fn send(&self, msg: Msg<T>) {
        assert!(self.try_send(msg), "RenderChan.send: render port closed")
    }

    pub fn try_send(&self, msg: Msg<T>) -> bool {
        self.chan.try_send(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C,T> {
    id: PipelineId,
    port: Port<Msg<T>>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: ~FontContext,
    opts: Opts,

    /// A channel to the profiler.
    profiler_chan: ProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layer to be rendered
    render_layer: Option<RenderLayer<T>>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap<~LayerBuffer>,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
    pub fn create(id: PipelineId,
                  port: Port<Msg<T>>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  opts: Opts,
                  profiler_chan: ProfilerChan,
                  shutdown_chan: Chan<()>) {
        spawn_named("RenderTask", proc() {

            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: ~FontContext::new(FontContextInfo {
                        backend: opts.render_backend.clone(),
                        needs_font_list: false,
                        profiler_chan: profiler_chan.clone(),
                    }),
                    opts: opts,
                    profiler_chan: profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layer: None,

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                render_task.native_graphics_context.as_ref().map(
                    |ctx| render_task.buffer_map.clear(ctx));
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layer) => {
                    if self.paint_permission {
                        self.epoch.next();
                        self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
                    } else {
                        debug!("render_task: render ready msg");
                        self.constellation_chan.send(RendererReadyMsg(self.id));
                    }
                    self.render_layer = Some(render_layer);
                }
                ReRenderMsg(tiles, scale, epoch) => {
                    if self.epoch == epoch {
                        self.render(tiles, scale);
                    } else {
                        debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                    }
                }
                UnusedBufferMsg(unused_buffers) => {
                    // move_rev_iter is more efficient
                    for buffer in unused_buffers.move_rev_iter() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;
                    match self.render_layer {
                        Some(ref render_layer) => {
                            self.epoch.next();
                            self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
                        }
                        None => {}
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.send(());
                    break;
                }
            }
        }
    }

    fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
        let render_layer;
        match self.render_layer {
            Some(ref r_layer) => {
                render_layer = r_layer;
            }
            _ => return, // nothing to do
        }

        self.compositor.set_render_state(RenderingRenderState);
        time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = ~[];

            // Divide up the layer into tiles.
            time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone(), || {
                for tile in tiles.iter() {
                    let width = tile.screen_rect.size.width;
                    let height = tile.screen_rect.size.height;

                    let size = Size2D(width as i32, height as i32);
                    let draw_target = match self.graphics_context {
                        CpuGraphicsContext => {
                            DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                        }
                        GpuGraphicsContext => {
                            // FIXME(pcwalton): Cache the components of draw targets
                            // (texture color buffer, renderbuffers) instead of recreating them.
                            let draw_target =
                                DrawTarget::new_with_fbo(self.opts.render_backend,
                                                         native_graphics_context!(self),
                                                         size,
                                                         B8G8R8A8);
                            draw_target.make_current();
                            draw_target
                        }
                    };

                    {
                        // Build the render context.
                        let mut ctx = RenderContext {
                            draw_target: &draw_target,
                            font_ctx: &mut self.font_ctx,
                            opts: &self.opts,
                            page_rect: tile.page_rect,
                            screen_rect: tile.screen_rect,
                        };

                        // Apply the translation to render the tile we want.
                        let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                        let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                        let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                      -(tile.page_rect.origin.y) as AzFloat);

                        ctx.draw_target.set_transform(&matrix);

                        // Clear the buffer.
                        ctx.clear();

                        // Draw the display list.
                        profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
                            render_layer.display_list_collection.get().draw_lists_into_context(&mut ctx);
                            ctx.draw_target.flush();
                        });
                    }

                    // Extract the texture from the draw target and place it into its slot in the
                    // buffer. If using CPU rendering, upload it first.
                    //
                    // FIXME(pcwalton): We should supply the texture and native surface *to* the
                    // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                    let buffer = match self.graphics_context {
                        CpuGraphicsContext => {
                            let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                                Some(buffer) => {
                                    let mut buffer = buffer;
                                    buffer.rect = tile.page_rect;
                                    buffer.screen_pos = tile.screen_rect;
                                    buffer.resolution = scale;
                                    buffer.native_surface.mark_wont_leak();
                                    buffer
                                }
                                None => {
                                    // Create an empty native surface. We mark it as not leaking
                                    // in case it dies in transit to the compositor task.
                                    let mut native_surface: NativeSurface =
                                        layers::platform::surface::NativeSurfaceMethods::new(
                                            native_graphics_context!(self),
                                            Size2D(width as i32, height as i32),
                                            width as i32 * 4);
                                    native_surface.mark_wont_leak();

                                    ~LayerBuffer {
                                        native_surface: native_surface,
                                        rect: tile.page_rect,
                                        screen_pos: tile.screen_rect,
                                        resolution: scale,
                                        stride: (width * 4) as uint
                                    }
                                }
                            };

                            draw_target.snapshot().get_data_surface().with_data(|data| {
                                buffer.native_surface.upload(native_graphics_context!(self), data);
                                debug!("RENDERER uploading to native surface {:d}",
                                       buffer.native_surface.get_id() as int);
                            });

                            buffer
                        }
                        GpuGraphicsContext => {
                            draw_target.make_current();
                            let StolenGLResources {
                                surface: native_surface
                            } = draw_target.steal_gl_resources().unwrap();

                            // We mark the native surface as not leaking in case the surfaces
                            // die on their way to the compositor task.
                            let mut native_surface: NativeSurface =
                                NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                            native_surface.mark_wont_leak();

                            ~LayerBuffer {
                                native_surface: native_surface,
                                rect: tile.page_rect,
                                screen_pos: tile.screen_rect,
                                resolution: scale,
                                stride: (width * 4) as uint
                            }
                        }
                    };

                    new_buffers.push(buffer);
                }
            });

            let layer_buffer_set = ~LayerBufferSet {
                buffers: new_buffers,
            };

            debug!("render_task: returning surface");
            if self.paint_permission {
                self.compositor.paint(self.id, layer_buffer_set, self.epoch);
            } else {
                debug!("render_task: RendererReadyMsg send");
                self.constellation_chan.send(RendererReadyMsg(self.id));
            }
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

<|endoftext|>


<commit_before>use config::action::message::MessageActionBuilder;
use message::MessageBuilder;
use context::base::BaseContextBuilder;
use super::{CONTEXT_LEN, CONTEXT_NAME, CONTEXT_UUID};

use conditions::ConditionsBuilder;
use dispatcher::Response;
use test_utils::MockResponseSender;
use state::State;
use action::Action;

use env_logger;
use handlebars::Template;
use std::time::Duration;
use std::sync::Arc;
use uuid::Uuid;

#[test]
fn test_given_a_message_action_when_it_is_executed_then_it_adds_the_name_and_uuid_of_the_context_to_the_message
    () {
    let mut responder = MockResponseSender::new();
    let name = Some("name".to_owned());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = State::new();
    let message_action = {
        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        MessageActionBuilder::new("uuid", message).build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let Response::Alert(ref response) = *responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        assert_eq!("0", response.message().get(CONTEXT_LEN).unwrap());
    } else {
        unreachable!();
    }
}

#[test]
fn test_given_message_action_when_it_is_executed_then_it_uses_the_messages_to_render_the_message_and_additonal_templated_values
    () {
    let mut responder = MockResponseSender::new();
    let _ = env_logger::init();
    let name = Some("name".to_owned());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = {
        let messages = vec![Arc::new(MessageBuilder::new("uuid1", "message1")
                                         .pair("key1", "value1")
                                         .build()),
                            Arc::new(MessageBuilder::new("uuid2", "message2")
                                         .pair("key2", "value2")
                                         .build())];
        State::with_messages(messages)
    };
    let message_action = {
        let message = Template::compile("key1={{{messages.[0].values.key1}}} \
                                         key2={{{messages.[1].values.key2}}}"
                                            .to_owned())
                          .expect("Failed to compile a handlebars template");
        MessageActionBuilder::new("uuid", message)
            .pair("message_num",
                  Template::compile("we have {{context_len}} messages".to_owned())
                      .expect("Failed to compile a handlebars template"))
            .build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let Response::Alert(ref response) = *responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        let message = response.message();
        assert_eq!("we have 2 messages",
                   message.get("message_num").expect("Failed to get an inserted key from a map"));
        assert_eq!("key1=value1 key2=value2", message.message());
    } else {
        unreachable!();
    }
}
<commit_msg>MessageAction: add a unit test for RenderErrors<commit_after>use config::action::message::MessageActionBuilder;
use message::MessageBuilder;
use context::base::BaseContextBuilder;
use super::{CONTEXT_LEN, CONTEXT_NAME, CONTEXT_UUID};

use conditions::ConditionsBuilder;
use dispatcher::Response;
use test_utils::MockResponseSender;
use state::State;
use action::Action;

use env_logger;
use handlebars::Template;
use std::time::Duration;
use std::sync::Arc;
use uuid::Uuid;

#[test]
fn test_given_a_message_action_when_it_is_executed_then_it_adds_the_name_and_uuid_of_the_context_to_the_message
    () {
    let mut responder = MockResponseSender::new();
    let name = Some("name".to_owned());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = State::new();
    let message_action = {
        let message = Template::compile("message".to_owned())
                          .expect("Failed to compile a handlebars template");
        MessageActionBuilder::new("uuid", message).build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let Response::Alert(ref response) = *responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        assert_eq!("0", response.message().get(CONTEXT_LEN).unwrap());
    } else {
        unreachable!();
    }
}

#[test]
fn test_given_message_action_when_it_is_executed_then_it_uses_the_messages_to_render_the_message_and_additonal_templated_values
    () {
    let mut responder = MockResponseSender::new();
    let _ = env_logger::init();
    let name = Some("name".to_owned());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = {
        let messages = vec![Arc::new(MessageBuilder::new("uuid1", "message1")
                                         .pair("key1", "value1")
                                         .build()),
                            Arc::new(MessageBuilder::new("uuid2", "message2")
                                         .pair("key2", "value2")
                                         .build())];
        State::with_messages(messages)
    };
    let message_action = {
        let message = Template::compile("key1={{{messages.[0].values.key1}}} \
                                         key2={{{messages.[1].values.key2}}}"
                                            .to_owned())
                          .expect("Failed to compile a handlebars template");
        MessageActionBuilder::new("uuid", message)
            .pair("message_num",
                  Template::compile("we have {{context_len}} messages".to_owned())
                      .expect("Failed to compile a handlebars template"))
            .build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let Response::Alert(ref response) = *responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        let message = response.message();
        assert_eq!("we have 2 messages",
                   message.get("message_num").expect("Failed to get an inserted key from a map"));
        assert_eq!("key1=value1 key2=value2", message.message());
    } else {
        unreachable!();
    }
}

#[test]
fn test_given_message_action_with_templated_values_when_a_render_error_occurres_then_it_does_not_cause_panic() {
    let _ = env_logger::init();
    let mut responder = MockResponseSender::new();
    let name = Some("name".to_owned());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = {
        let messages = vec![Arc::new(MessageBuilder::new("uuid1", "message1")
                                         .pair("key1", "value1")
                                         .build())];
        State::with_messages(messages)
    };
    let message_action = {
        // this will produce the RenderError
        let message = Template::compile("{{lookup 1}}".to_owned())
                          .expect("Failed to compile a handlebars template");
        MessageActionBuilder::new("uuid", message).build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
}
<|endoftext|>


<commit_before>pub mod reader;
use reader::ShmReader;
pub mod writer;
use crate::header::Header;
use log::{error, info};
use memmap::MmapOptions;

use std::fs::OpenOptions;
use std::fs::{remove_file, DirBuilder};
use std::path::Path;
use std::result::Result;
use writer::ShmWriter;

pub fn shm_reader(root_path: &Path, producer_id: u64, channel_id: u64) -> Result<ShmReader, String> {
    let dir_path = root_path.join(producer_id.to_string());
    let kek_file_name = dir_path.join(format!("{}.kekbit", channel_id));
    let kek_lock_name = dir_path.join(format!("{}.kekbit.lock", channel_id));
    if !kek_file_name.exists() {
        return Err(format!("{:?} does not exist.", kek_file_name));
    }
    if kek_lock_name.exists() {
        return Err(format!("{:?} Is not ready yet", kek_file_name));
    }

    let kek_file = OpenOptions::new()
        .write(true)
        .read(true)
        .open(&kek_file_name)
        .or_else(|err| Err(err.to_string()))?;

    info!("Kekbit file {:?} opened for read.", kek_file);
    let mmap = unsafe { MmapOptions::new().map_mut(&kek_file) }.or_else(|err| Err(err.to_string()))?;
    ShmReader::new(mmap)
}

pub fn shm_writer(root_path: &Path, header: &Header) -> Result<ShmWriter, String> {
    let dir_path = root_path.join(header.producer_id().to_string());
    let mut builder = DirBuilder::new();
    builder.recursive(true);
    builder.create(&dir_path).or_else(|err| Err(err.to_string()))?;
    let lock_file_name = dir_path.join(format!("{}.kekbit.lock", header.channel_id()));
    OpenOptions::new()
        .write(true)
        .create(true)
        .open(&lock_file_name)
        .or_else(|err| Err(err.to_string()))?;
    info!("Kekbit lock {:?} created", lock_file_name);
    let kek_file_name = dir_path.join(format!("{}.kekbit", header.channel_id()));
    if kek_file_name.exists() {
        error!(
            "Kekbit writer creation error . The channel file {:?} already exists",
            kek_file_name
        );
        return Err("Channel file already exists!".to_string());
    }
    let kek_file = OpenOptions::new()
        .write(true)
        .read(true)
        .create(true)
        .open(&kek_file_name)
        .or_else(|err| Err(err.to_string()))?;
    let total_len = (header.capacity() + header.len() as u32) as u64;
    kek_file.set_len(total_len).or_else(|err| Err(err.to_string()))?;
    info!("Kekbit channel store {:?} created.", kek_file);
    let mut mmap = unsafe { MmapOptions::new().map_mut(&kek_file) }.or_else(|err| Err(err.to_string()))?;
    let buf = &mut mmap[..];
    header.write_to(buf);
    mmap.flush().or_else(|err| Err(err.to_string()))?;
    info!("Kekbit channel with store {:?} succesfully initialized", kek_file_name);
    let res = ShmWriter::new(mmap);
    if res.is_err() {
        error!("Kekbit writer creation error . The file {:?} will be removed!", kek_file_name);
        remove_file(&kek_file_name).expect("Could not remove kekbit file");
    }
    remove_file(&lock_file_name).expect("Could not remove kekbit lock file");
    info!("Kekbit lock file {:?} removed", lock_file_name);
    res
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::api::{Reader, Writer};
    use crate::tick::TickUnit::Nanos;
    use crate::utils::{align, REC_HEADER_LEN};
    use std::sync::Once;

    const FOREVER: u64 = 99_999_999_999;

    static INIT_LOG: Once = Once::new();

    #[test]
    fn check_max_len() {
        let header = Header::new(100, 1000, 300_000, 1000, FOREVER, 1, Nanos);
        dbg!(&header);
        let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
        let writer = shm_writer(&test_tmp_dir.path(), &header).unwrap();
        let reader = shm_reader(&test_tmp_dir.path(), 100, 1000).unwrap();
        assert_eq!(writer.header(), reader.header());
    }

    #[test]
    fn read_than_write() {
        INIT_LOG.call_once(|| {
            simple_logger::init().unwrap();
        });
        let header = Header::new(100, 1000, 10000, 1000, FOREVER, 1000, Nanos);
        let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
        let mut writer = shm_writer(&test_tmp_dir.path(), &header).unwrap();
        let txt = "There are 10 kinds of people: those who know binary and those who don't";
        let msgs = txt.split_whitespace();
        let mut msg_count = 0;
        let mut bytes_written = 8; //accout for the initial heartbeat
        for m in msgs {
            let to_wr = m.as_bytes();
            let len = to_wr.len() as u32;
            let size = writer.write(&to_wr, len).unwrap();
            assert_eq!(size, align(len + REC_HEADER_LEN));
            bytes_written += size;
            msg_count += 1;
        }
        let mut reader = shm_reader(&test_tmp_dir.path(), 100, 1000).unwrap();
        let mut res_msg = StrMsgsAppender::default();
        let bytes_read = reader
            .read(&mut |msg| res_msg.on_message(msg), msg_count + 10 as u16)
            .unwrap();
        assert_eq!(res_msg.txt, txt);
        assert_eq!(bytes_written, bytes_read);
    }

    #[derive(Default, Debug)]
    struct StrMsgsAppender {
        txt: String,
    }

    impl StrMsgsAppender {
        pub fn on_message(&mut self, buf: &[u8]) {
            let msg_str = std::str::from_utf8(&buf).unwrap();
            if self.txt.is_empty() {
                self.txt.push_str(" ");
            }
            self.txt.push_str(msg_str);
        }
    }
}
<commit_msg>Fix read/write test case<commit_after>pub mod reader;
use reader::ShmReader;
pub mod writer;
use crate::header::Header;
use log::{error, info};
use memmap::MmapOptions;

use std::fs::OpenOptions;
use std::fs::{remove_file, DirBuilder};
use std::path::Path;
use std::result::Result;
use writer::ShmWriter;

pub fn shm_reader(root_path: &Path, producer_id: u64, channel_id: u64) -> Result<ShmReader, String> {
    let dir_path = root_path.join(producer_id.to_string());
    let kek_file_name = dir_path.join(format!("{}.kekbit", channel_id));
    let kek_lock_name = dir_path.join(format!("{}.kekbit.lock", channel_id));
    if !kek_file_name.exists() {
        return Err(format!("{:?} does not exist.", kek_file_name));
    }
    if kek_lock_name.exists() {
        return Err(format!("{:?} Is not ready yet", kek_file_name));
    }

    let kek_file = OpenOptions::new()
        .write(true)
        .read(true)
        .open(&kek_file_name)
        .or_else(|err| Err(err.to_string()))?;

    info!("Kekbit file {:?} opened for read.", kek_file);
    let mmap = unsafe { MmapOptions::new().map_mut(&kek_file) }.or_else(|err| Err(err.to_string()))?;
    ShmReader::new(mmap)
}

pub fn shm_writer(root_path: &Path, header: &Header) -> Result<ShmWriter, String> {
    let dir_path = root_path.join(header.producer_id().to_string());
    let mut builder = DirBuilder::new();
    builder.recursive(true);
    builder.create(&dir_path).or_else(|err| Err(err.to_string()))?;
    let lock_file_name = dir_path.join(format!("{}.kekbit.lock", header.channel_id()));
    OpenOptions::new()
        .write(true)
        .create(true)
        .open(&lock_file_name)
        .or_else(|err| Err(err.to_string()))?;
    info!("Kekbit lock {:?} created", lock_file_name);
    let kek_file_name = dir_path.join(format!("{}.kekbit", header.channel_id()));
    if kek_file_name.exists() {
        error!(
            "Kekbit writer creation error . The channel file {:?} already exists",
            kek_file_name
        );
        return Err("Channel file already exists!".to_string());
    }
    let kek_file = OpenOptions::new()
        .write(true)
        .read(true)
        .create(true)
        .open(&kek_file_name)
        .or_else(|err| Err(err.to_string()))?;
    let total_len = (header.capacity() + header.len() as u32) as u64;
    kek_file.set_len(total_len).or_else(|err| Err(err.to_string()))?;
    info!("Kekbit channel store {:?} created.", kek_file);
    let mut mmap = unsafe { MmapOptions::new().map_mut(&kek_file) }.or_else(|err| Err(err.to_string()))?;
    let buf = &mut mmap[..];
    header.write_to(buf);
    mmap.flush().or_else(|err| Err(err.to_string()))?;
    info!("Kekbit channel with store {:?} succesfully initialized", kek_file_name);
    let res = ShmWriter::new(mmap);
    if res.is_err() {
        error!("Kekbit writer creation error . The file {:?} will be removed!", kek_file_name);
        remove_file(&kek_file_name).expect("Could not remove kekbit file");
    }
    remove_file(&lock_file_name).expect("Could not remove kekbit lock file");
    info!("Kekbit lock file {:?} removed", lock_file_name);
    res
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::api::{Reader, Writer};
    use crate::tick::TickUnit::Nanos;
    use crate::utils::{align, REC_HEADER_LEN};
    use std::sync::Once;

    const FOREVER: u64 = 99_999_999_999;

    static INIT_LOG: Once = Once::new();

    #[test]
    fn check_max_len() {
        let header = Header::new(100, 1000, 300_000, 1000, FOREVER, 1, Nanos);
        dbg!(&header);
        let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
        let writer = shm_writer(&test_tmp_dir.path(), &header).unwrap();
        let reader = shm_reader(&test_tmp_dir.path(), 100, 1000).unwrap();
        assert_eq!(writer.header(), reader.header());
    }

    #[test]
    fn read_than_write() {
        INIT_LOG.call_once(|| {
            simple_logger::init().unwrap();
        });
        let header = Header::new(100, 1000, 10000, 1000, FOREVER, 1000, Nanos);
        let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
        let mut writer = shm_writer(&test_tmp_dir.path(), &header).unwrap();
        let txt = "There are 10 kinds of people: those who know binary and those who don't";
        let msgs = txt.split_whitespace();
        let mut msg_count = 0;
        let mut bytes_written = 8; //accout for the initial heartbeat
        for m in msgs {
            let to_wr = m.as_bytes();
            let len = to_wr.len() as u32;
            let size = writer.write(&to_wr, len).unwrap();
            assert_eq!(size, align(len + REC_HEADER_LEN));
            bytes_written += size;
            msg_count += 1;
        }
        let mut reader = shm_reader(&test_tmp_dir.path(), 100, 1000).unwrap();
        let mut res_msg = StrMsgsAppender::default();
        let bytes_read = reader
            .read(&mut |msg| res_msg.on_message(msg), msg_count + 10 as u16)
            .unwrap();
        assert_eq!(res_msg.txt, txt);
        assert_eq!(bytes_written, bytes_read);
    }

    #[derive(Default, Debug)]
    struct StrMsgsAppender {
        txt: String,
    }

    impl StrMsgsAppender {
        pub fn on_message(&mut self, buf: &[u8]) {
            let msg_str = std::str::from_utf8(&buf).unwrap();
            if !self.txt.is_empty() {
                self.txt.push_str(" ");
            }
            self.txt.push_str(msg_str);
        }
    }
}
<|endoftext|>


<commit_before>use message::Message;
use config::action::message::MessageActionBuilder;
use message::MessageBuilder;
use context::base::BaseContextBuilder;
use super::{CONTEXT_LEN, CONTEXT_NAME, CONTEXT_UUID, MessageAction};

use conditions::ConditionsBuilder;
use config;
use dispatcher::Response;
use dispatcher::response::MockResponseSender;
use state::State;
use action::Action;

use env_logger;
use handlebars::Template;
use std::time::Duration;
use std::sync::Arc;
use uuid::Uuid;

// #[test]
// fn test_given_message_when_it_is_created_from_a_message_action_then_every_fields_are_copied_into_it() {
//     let uuid = "uuid";
//     let name = "name";
//     let expected_message = MessageBuilder::new(uuid, "message")
//                                             .name(Some(name))
//                                             .pair("key1", "value1")
//                                             .pair("key2", "value1")
//                                             .build();
//     let action = MessageActionBuilder::new(uuid, Template::compile("message".to_string()).unwrap())
//                                         .name(Some(name))
//                                         .pair("key1", Template::compile("value1".to_string()).unwrap())
//                                         .pair("key2", Template::compile("value1".to_string()).unwrap())
//                                         .build();
//     let message = Message::from(&action);
//     assert_eq!(&expected_message, &message);
// }

#[test]
fn test_given_a_message_action_when_it_is_executed_then_it_adds_the_name_and_uuid_of_the_context_to_the_message
    () {
    let mut responder = MockResponseSender::new();
    let name = Some("name".to_string());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = State::new();
    let message_action = {
        let message = Template::compile("message".to_string())
                          .ok()
                          .expect("Failed to compile a handlebars template");
        config::action::message::MessageActionBuilder::new("uuid", message)
                                .build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let &Response::Alert(ref response) = responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        assert_eq!("0", response.message().get(CONTEXT_LEN).unwrap());
    } else {
        unreachable!();
    }
}

#[test]
fn test_given_message_action_when_it_is_executed_then_it_uses_the_messages_to_render_the_message_and_additonal_templated_values
    () {
    let mut responder = MockResponseSender::new();
    let _ = env_logger::init();
    let name = Some("name".to_string());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = {
        let messages = vec![Arc::new(MessageBuilder::new("uuid1", "message1")
                                         .pair("key1", "value1")
                                         .build()),
                            Arc::new(MessageBuilder::new("uuid2", "message2")
                                         .pair("key2", "value2")
                                         .build())];
        State::with_messages(messages)
    };
    let message_action = {
        let message = Template::compile("key1={{{messages.[0].values.key1}}} \
                                         key2={{{messages.[1].values.key2}}}"
                                            .to_string())
                          .ok()
                          .expect("Failed to compile a handlebars template");
        config::action::message::MessageActionBuilder::new("uuid", message)
                                .pair("message_num",
                                      Template::compile("we have {{context_len}} messages"
                                                            .to_string())
                                          .ok()
                                          .expect("Failed to compile a handlebars template"))
                                .build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let &Response::Alert(ref response) = responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        let message = response.message();
        assert_eq!("we have 2 messages",
                   message.get("message_num").expect("Failed to get an inserted key from a map"));
        assert_eq!("key1=value1 key2=value2", message.message());
    } else {
        unreachable!();
    }
}
<commit_msg>config/action/message/test: remove invalid unit test<commit_after>use config::action::message::MessageActionBuilder;
use message::MessageBuilder;
use context::base::BaseContextBuilder;
use super::{CONTEXT_LEN, CONTEXT_NAME, CONTEXT_UUID};

use conditions::ConditionsBuilder;
use config;
use dispatcher::Response;
use dispatcher::response::MockResponseSender;
use state::State;
use action::Action;

use env_logger;
use handlebars::Template;
use std::time::Duration;
use std::sync::Arc;
use uuid::Uuid;

#[test]
fn test_given_a_message_action_when_it_is_executed_then_it_adds_the_name_and_uuid_of_the_context_to_the_message
    () {
    let mut responder = MockResponseSender::new();
    let name = Some("name".to_string());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = State::new();
    let message_action = {
        let message = Template::compile("message".to_string())
                          .ok()
                          .expect("Failed to compile a handlebars template");
        config::action::message::MessageActionBuilder::new("uuid", message)
                                .build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let &Response::Alert(ref response) = responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        assert_eq!("0", response.message().get(CONTEXT_LEN).unwrap());
    } else {
        unreachable!();
    }
}

#[test]
fn test_given_message_action_when_it_is_executed_then_it_uses_the_messages_to_render_the_message_and_additonal_templated_values
    () {
    let mut responder = MockResponseSender::new();
    let _ = env_logger::init();
    let name = Some("name".to_string());
    let base_context = {
        let conditions = ConditionsBuilder::new(Duration::from_millis(100)).build();
        let uuid = Uuid::new_v4();
        BaseContextBuilder::new(uuid, conditions).name(name.clone()).build()
    };
    let state = {
        let messages = vec![Arc::new(MessageBuilder::new("uuid1", "message1")
                                         .pair("key1", "value1")
                                         .build()),
                            Arc::new(MessageBuilder::new("uuid2", "message2")
                                         .pair("key2", "value2")
                                         .build())];
        State::with_messages(messages)
    };
    let message_action = {
        let message = Template::compile("key1={{{messages.[0].values.key1}}} \
                                         key2={{{messages.[1].values.key2}}}"
                                            .to_string())
                          .ok()
                          .expect("Failed to compile a handlebars template");
        config::action::message::MessageActionBuilder::new("uuid", message)
                                .pair("message_num",
                                      Template::compile("we have {{context_len}} messages"
                                                            .to_string())
                                          .ok()
                                          .expect("Failed to compile a handlebars template"))
                                .build()
    };

    message_action.on_closed(&state, &base_context, &mut responder);
    assert_eq!(1, responder.0.len());
    let responses = responder.0;
    if let &Response::Alert(ref response) = responses.get(0).unwrap() {
        assert_eq!(name.as_ref().unwrap(),
                   response.message().get(CONTEXT_NAME).unwrap());
        assert_eq!(&base_context.uuid().to_hyphenated_string(),
                   response.message().get(CONTEXT_UUID).unwrap());
        let message = response.message();
        assert_eq!("we have 2 messages",
                   message.get("message_num").expect("Failed to get an inserted key from a map"));
        assert_eq!("key1=value1 key2=value2", message.message());
    } else {
        unreachable!();
    }
}
<|endoftext|>