<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access::ModuleAccess,
    file_format::{
        CompiledModule, FieldDefinition, FunctionHandle, Kind, SignatureToken, StructDefinition,
        StructFieldInformation, TypeParameterIndex,
    },
};
use move_core_types::{
    account_address::AccountAddress,
    identifier::Identifier,
    language_storage::{StructTag, TypeTag},
};

/// Defines normalized representations of Move types, fields, kinds, structs, functions, and
/// modules. These representations are useful in situations that require require comparing
/// functions, resources, and types across modules. This arises in linking, compatibility checks
/// (e.g., "is it safe to deploy this new module without updating its dependents and/or restarting
/// genesis?"), defining schemas for resources stored on-chain, and (possibly in the future)
/// allowing module updates transactions.

/// A normalized version of `SignatureToken`, a type expression appearing in struct or function
/// declarations. Unlike `SignatureToken`s, `normalized::Type`s from different modules can safely be
/// compared.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type {
    Bool,
    U8,
    U64,
    U128,
    Address,
    Signer,
    Struct {
        address: AccountAddress,
        module: Identifier,
        name: Identifier,
        type_arguments: Vec<Type>,
    },
    Vector(Box<Type>),
    TypeParameter(TypeParameterIndex),
    Reference(Box<Type>),
    MutableReference(Box<Type>),
}

/// Normalized version of a `FieldDefinition`. The `name` is included even though it is
/// metadata that it is ignored by the VM. The reason: names are important to clients. We would
/// want a change from `Account { bal: u64, seq: u64 }` to `Account { seq: u64, bal: u64 }` to be
/// marked as incompatible. Not safe to compare without an enclosing `Struct`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Field {
    pub name: Identifier,
    pub type_: Type,
}

/// Normalized version of a `StructDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Struct {
    pub name: Identifier,
    pub kind: Kind,
    pub type_parameters: Vec<Kind>,
    pub fields: Vec<Field>,
}

/// Normalized version of a `FunctionDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FunctionSignature {
    pub name: Identifier,
    pub type_parameters: Vec<Kind>,
    pub formals: Vec<Type>,
    pub ret: Vec<Type>,
}

/// Normalized version of a `CompiledModule`: its address, name, struct declarations, and public
/// function declarations.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Module {
    pub address: AccountAddress,
    pub name: Identifier,
    pub structs: Vec<Struct>,
    pub public_functions: Vec<FunctionSignature>,
}

impl Module {
    /// Extract a normalized module from a `CompiledModule`. The module `m` should be verified.
    /// Nothing will break here if that is not the case, but there is little point in computing a
    /// normalized representation of a module that won't verify (since it can't be published).
    pub fn new(m: &CompiledModule) -> Self {
        let structs = m.struct_defs().iter().map(|d| Struct::new(m, d)).collect();
        let public_functions = m
            .function_defs()
            .iter()
            .filter_map(|f| {
                if f.is_public {
                    Some(FunctionSignature::new(m, m.function_handle_at(f.function)))
                } else {
                    None
                }
            })
            .collect();
        Self {
            address: *m.address(),
            name: m.name().to_owned(),
            structs,
            public_functions,
        }
    }
}

impl Type {
    /// Create a normalized `Type` for `SignatureToken` `s` in module `m`.
    pub fn new(m: &CompiledModule, s: &SignatureToken) -> Self {
        use SignatureToken::*;
        match s {
            Struct(shi) => {
                let handle = m.struct_handle_at(*shi);
                assert!(handle.type_parameters.is_empty(), "A struct with N type parameters should be encoded as StructModuleInstantiation with type_arguments = [TypeParameter(1), ..., TypeParameter(N)]");
                Type::Struct {
                    address: *m.address(),
                    module: m.name().to_owned(),
                    name: m.identifier_at(handle.name).to_owned(),
                    type_arguments: Vec::new(),
                }
            }
            StructInstantiation(shi, type_actuals) => Type::Struct {
                address: *m.address(),
                module: m.name().to_owned(),
                name: m.identifier_at(m.struct_handle_at(*shi).name).to_owned(),
                type_arguments: type_actuals.iter().map(|t| Type::new(m, t)).collect(),
            },
            Bool => Type::Bool,
            U8 => Type::U8,
            U64 => Type::U64,
            U128 => Type::U128,
            Address => Type::Address,
            Signer => Type::Signer,
            Vector(t) => Type::Vector(Box::new(Type::new(m, t))),
            TypeParameter(i) => Type::TypeParameter(*i),
            Reference(t) => Type::Reference(Box::new(Type::new(m, t))),
            MutableReference(t) => Type::MutableReference(Box::new(Type::new(m, t))),
        }
    }

    /// Return true if `self` is a closed type with no free type variables
    pub fn is_closed(&self) -> bool {
        use Type::*;
        match self {
            TypeParameter(_) => false,
            Bool => true,
            U8 => true,
            U64 => true,
            U128 => true,
            Address => true,
            Signer => true,
            Struct { type_arguments, .. } => type_arguments.iter().all(|t| t.is_closed()),
            Vector(t) | Reference(t) | MutableReference(t) => t.is_closed(),
        }
    }

    pub fn into_type_tag(self) -> Option<TypeTag> {
        use Type::*;
        Some(if self.is_closed() {
            match self {
                Reference(_) | MutableReference(_) => return None,
                Bool => TypeTag::Bool,
                U8 => TypeTag::U8,
                U64 => TypeTag::U64,
                U128 => TypeTag::U128,
                Address => TypeTag::Address,
                Signer => TypeTag::Signer,
                Vector(t) => TypeTag::Vector(Box::new(
                    t.into_type_tag()
                        .expect("Invariant violation: vector type argument contains reference"),
                )),
                Struct {
                    address,
                    module,
                    name,
                    type_arguments,
                } => TypeTag::Struct(StructTag {
                    address,
                    module,
                    name,
                    type_params: type_arguments
                        .into_iter()
                        .map(|t| {
                            t.into_type_tag().expect(
                                "Invariant violation: struct type argument contains reference",
                            )
                        })
                        .collect(),
                }),
                TypeParameter(_) => unreachable!(),
            }
        } else {
            return None;
        })
    }
}

impl Field {
    /// Create a `Field` for `FieldDefinition` `f` in module `m`.
    pub fn new(m: &CompiledModule, f: &FieldDefinition) -> Self {
        Field {
            name: m.identifier_at(f.name).to_owned(),
            type_: Type::new(m, &f.signature.0),
        }
    }
}

impl Struct {
    /// Create a `Struct` for `StructDefinition` `def` in module `m`. Panics if `def` is a
    /// a native struct definition.
    pub fn new(m: &CompiledModule, def: &StructDefinition) -> Self {
        let handle = m.struct_handle_at(def.struct_handle);
        let fields = match &def.field_information {
            StructFieldInformation::Native => panic!("Can't extract  for native struct"),
            StructFieldInformation::Declared(fields) => {
                fields.iter().map(|f| Field::new(m, f)).collect()
            }
        };
        Struct {
            name: m.identifier_at(handle.name).to_owned(),
            kind: if handle.is_nominal_resource {
                Kind::Resource
            } else {
                Kind::Copyable
            },
            type_parameters: handle.type_parameters.clone(),
            fields,
        }
    }
}

impl FunctionSignature {
    /// Create a `FunctionSignature` for `FunctionHandle` `f` in module `m`.
    pub fn new(m: &CompiledModule, f: &FunctionHandle) -> Self {
        FunctionSignature {
            name: m.identifier_at(f.name).to_owned(),
            type_parameters: f.type_parameters.clone(),
            formals: m
                .signature_at(f.parameters)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
            ret: m
                .signature_at(f.return_)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
        }
    }
}
<commit_msg>[vm] Fix bug in creation of normalized types<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access::ModuleAccess,
    file_format::{
        CompiledModule, FieldDefinition, FunctionHandle, Kind, SignatureToken, StructDefinition,
        StructFieldInformation, TypeParameterIndex,
    },
};
use move_core_types::{
    account_address::AccountAddress,
    identifier::Identifier,
    language_storage::{StructTag, TypeTag},
};

/// Defines normalized representations of Move types, fields, kinds, structs, functions, and
/// modules. These representations are useful in situations that require require comparing
/// functions, resources, and types across modules. This arises in linking, compatibility checks
/// (e.g., "is it safe to deploy this new module without updating its dependents and/or restarting
/// genesis?"), defining schemas for resources stored on-chain, and (possibly in the future)
/// allowing module updates transactions.

/// A normalized version of `SignatureToken`, a type expression appearing in struct or function
/// declarations. Unlike `SignatureToken`s, `normalized::Type`s from different modules can safely be
/// compared.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type {
    Bool,
    U8,
    U64,
    U128,
    Address,
    Signer,
    Struct {
        address: AccountAddress,
        module: Identifier,
        name: Identifier,
        type_arguments: Vec<Type>,
    },
    Vector(Box<Type>),
    TypeParameter(TypeParameterIndex),
    Reference(Box<Type>),
    MutableReference(Box<Type>),
}

/// Normalized version of a `FieldDefinition`. The `name` is included even though it is
/// metadata that it is ignored by the VM. The reason: names are important to clients. We would
/// want a change from `Account { bal: u64, seq: u64 }` to `Account { seq: u64, bal: u64 }` to be
/// marked as incompatible. Not safe to compare without an enclosing `Struct`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Field {
    pub name: Identifier,
    pub type_: Type,
}

/// Normalized version of a `StructDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Struct {
    pub name: Identifier,
    pub kind: Kind,
    pub type_parameters: Vec<Kind>,
    pub fields: Vec<Field>,
}

/// Normalized version of a `FunctionDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FunctionSignature {
    pub name: Identifier,
    pub type_parameters: Vec<Kind>,
    pub formals: Vec<Type>,
    pub ret: Vec<Type>,
}

/// Normalized version of a `CompiledModule`: its address, name, struct declarations, and public
/// function declarations.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Module {
    pub address: AccountAddress,
    pub name: Identifier,
    pub structs: Vec<Struct>,
    pub public_functions: Vec<FunctionSignature>,
}

impl Module {
    /// Extract a normalized module from a `CompiledModule`. The module `m` should be verified.
    /// Nothing will break here if that is not the case, but there is little point in computing a
    /// normalized representation of a module that won't verify (since it can't be published).
    pub fn new(m: &CompiledModule) -> Self {
        let structs = m.struct_defs().iter().map(|d| Struct::new(m, d)).collect();
        let public_functions = m
            .function_defs()
            .iter()
            .filter_map(|f| {
                if f.is_public {
                    Some(FunctionSignature::new(m, m.function_handle_at(f.function)))
                } else {
                    None
                }
            })
            .collect();
        Self {
            address: *m.address(),
            name: m.name().to_owned(),
            structs,
            public_functions,
        }
    }
}

impl Type {
    /// Create a normalized `Type` for `SignatureToken` `s` in module `m`.
    pub fn new(m: &CompiledModule, s: &SignatureToken) -> Self {
        use SignatureToken::*;
        match s {
            Struct(shi) => {
                let s_handle = m.struct_handle_at(*shi);
                assert!(s_handle.type_parameters.is_empty(), "A struct with N type parameters should be encoded as StructModuleInstantiation with type_arguments = [TypeParameter(1), ..., TypeParameter(N)]");
                let m_handle = m.module_handle_at(s_handle.module);
                Type::Struct {
                    address: *m.address_identifier_at(m_handle.address),
                    module: m.identifier_at(m_handle.name).to_owned(),
                    name: m.identifier_at(s_handle.name).to_owned(),
                    type_arguments: Vec::new(),
                }
            }
            StructInstantiation(shi, type_actuals) => {
                let s_handle = m.struct_handle_at(*shi);
                let m_handle = m.module_handle_at(s_handle.module);
                Type::Struct {
                    address: *m.address_identifier_at(m_handle.address),
                    module: m.identifier_at(m_handle.name).to_owned(),
                    name: m.identifier_at(s_handle.name).to_owned(),
                    type_arguments: type_actuals.iter().map(|t| Type::new(m, t)).collect(),
                }
            }
            Bool => Type::Bool,
            U8 => Type::U8,
            U64 => Type::U64,
            U128 => Type::U128,
            Address => Type::Address,
            Signer => Type::Signer,
            Vector(t) => Type::Vector(Box::new(Type::new(m, t))),
            TypeParameter(i) => Type::TypeParameter(*i),
            Reference(t) => Type::Reference(Box::new(Type::new(m, t))),
            MutableReference(t) => Type::MutableReference(Box::new(Type::new(m, t))),
        }
    }

    /// Return true if `self` is a closed type with no free type variables
    pub fn is_closed(&self) -> bool {
        use Type::*;
        match self {
            TypeParameter(_) => false,
            Bool => true,
            U8 => true,
            U64 => true,
            U128 => true,
            Address => true,
            Signer => true,
            Struct { type_arguments, .. } => type_arguments.iter().all(|t| t.is_closed()),
            Vector(t) | Reference(t) | MutableReference(t) => t.is_closed(),
        }
    }

    pub fn into_type_tag(self) -> Option<TypeTag> {
        use Type::*;
        Some(if self.is_closed() {
            match self {
                Reference(_) | MutableReference(_) => return None,
                Bool => TypeTag::Bool,
                U8 => TypeTag::U8,
                U64 => TypeTag::U64,
                U128 => TypeTag::U128,
                Address => TypeTag::Address,
                Signer => TypeTag::Signer,
                Vector(t) => TypeTag::Vector(Box::new(
                    t.into_type_tag()
                        .expect("Invariant violation: vector type argument contains reference"),
                )),
                Struct {
                    address,
                    module,
                    name,
                    type_arguments,
                } => TypeTag::Struct(StructTag {
                    address,
                    module,
                    name,
                    type_params: type_arguments
                        .into_iter()
                        .map(|t| {
                            t.into_type_tag().expect(
                                "Invariant violation: struct type argument contains reference",
                            )
                        })
                        .collect(),
                }),
                TypeParameter(_) => unreachable!(),
            }
        } else {
            return None;
        })
    }
}

impl Field {
    /// Create a `Field` for `FieldDefinition` `f` in module `m`.
    pub fn new(m: &CompiledModule, f: &FieldDefinition) -> Self {
        Field {
            name: m.identifier_at(f.name).to_owned(),
            type_: Type::new(m, &f.signature.0),
        }
    }
}

impl Struct {
    /// Create a `Struct` for `StructDefinition` `def` in module `m`. Panics if `def` is a
    /// a native struct definition.
    pub fn new(m: &CompiledModule, def: &StructDefinition) -> Self {
        let handle = m.struct_handle_at(def.struct_handle);
        let fields = match &def.field_information {
            StructFieldInformation::Native => panic!("Can't extract  for native struct"),
            StructFieldInformation::Declared(fields) => {
                fields.iter().map(|f| Field::new(m, f)).collect()
            }
        };
        Struct {
            name: m.identifier_at(handle.name).to_owned(),
            kind: if handle.is_nominal_resource {
                Kind::Resource
            } else {
                Kind::Copyable
            },
            type_parameters: handle.type_parameters.clone(),
            fields,
        }
    }
}

impl FunctionSignature {
    /// Create a `FunctionSignature` for `FunctionHandle` `f` in module `m`.
    pub fn new(m: &CompiledModule, f: &FunctionHandle) -> Self {
        FunctionSignature {
            name: m.identifier_at(f.name).to_owned(),
            type_parameters: f.type_parameters.clone(),
            formals: m
                .signature_at(f.parameters)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
            ret: m
                .signature_at(f.return_)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
        }
    }
}
<|endoftext|>


<commit_before>extern crate bazel_protos;
extern crate boxfuture;
extern crate clap;
extern crate fs;
extern crate futures;
extern crate protobuf;

use boxfuture::{Boxable, BoxFuture};
use clap::{App, Arg, SubCommand};
use fs::hash::Fingerprint;
use fs::store::Store;
use futures::future::{Future, join_all};
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::Arc;

fn main() {
  match execute(
    App::new("fs_util")
      .subcommand(
        SubCommand::with_name("file")
          .subcommand(
            SubCommand::with_name("cat")
              .about("Output the contents of a file by fingerprint.")
              .arg(Arg::with_name("fingerprint").required(true).takes_value(
                true,
              )),
          )
          .subcommand(
            SubCommand::with_name("save")
              .about(
                "Ingest a file by path, which allows it to be used in Directories/Snapshots. \
Outputs a fingerprint of its contents.",
              )
              .arg(Arg::with_name("path").required(true).takes_value(true)),
          ),
      )
      .subcommand(
        SubCommand::with_name("directory").subcommand(
          SubCommand::with_name("save")
            .about(
              "Ingest a directory recursively. Saves all files found therein and saves Directory \
protos for each directory found. Outputs a fingerprint of the canonical top-level Directory proto.",
            )
            .arg(Arg::with_name("source").required(true).takes_value(true)),
        ),
      )
      .arg(
        Arg::with_name("local_store_path")
          .takes_value(true)
          .long("local_store_path")
          .required(true),
      )
      .get_matches(),
  ) {
    Ok(_) => {}
    Err(err) => {
      eprintln!("{}", err);
      exit(1)
    }
  };
}

fn execute(top_match: clap::ArgMatches) -> Result<(), String> {
  let store_dir = top_match.value_of("local_store_path").unwrap();
  let store = Arc::new(Store::new(store_dir).map_err(|e| {
    format!(
      "Failed to open/create store for directory {}: {}",
      store_dir,
      e
    )
  })?);

  match top_match.subcommand() {
    ("file", Some(sub_match)) => {
      match sub_match.subcommand() {
        ("cat", Some(args)) => {
          let fingerprint = Fingerprint::from_hex_string(args.value_of("fingerprint").unwrap())?;
          match store.load_bytes(&fingerprint)? {
            Some(bytes) => {
              io::stdout().write(&bytes).unwrap();
              Ok(())
            }
            None => Err(format!("File with fingerprint {} not found", fingerprint)),
          }
        }
        ("save", Some(args)) => {
          let path = PathBuf::from(args.value_of("path").unwrap());
          let posix_fs = make_posix_fs(path.parent().unwrap());
          let file = posix_fs
            .stat(PathBuf::from(path.file_name().unwrap()))
            .unwrap();
          match file {
            fs::Stat::File(f) => {
              let (fingerprint, _) = save_file(store, &posix_fs, f).wait().unwrap();
              Ok(println!("{}", fingerprint))
            }
            o => Err(format!(
              "Tried to save file {:?} but it was not a file, was a {:?}",
              path,
              o
            )),
          }

        }
        (_, _) => unimplemented!(),
      }
    }
    ("directory", Some(sub_match)) => {
      match sub_match.subcommand() {
        ("save", Some(args)) => {
          let posix_fs = Arc::new(make_posix_fs(args.value_of("source").unwrap()));
          let (fingerprint, _) =
            save_directory(store, posix_fs, Arc::new(fs::Dir(PathBuf::from("."))))
              .wait()?;
          Ok(println!("{}", fingerprint))
        }
        (_, _) => unimplemented!(),
      }
    }
    (_, _) => unimplemented!(),
  }
}

fn make_posix_fs<P: AsRef<Path>>(root: P) -> fs::PosixFS {
  fs::PosixFS::new(&root, vec![]).unwrap()
}

fn save_file(
  store: Arc<Store>,
  posix_fs: &fs::PosixFS,
  file: fs::File,
) -> BoxFuture<(Fingerprint, usize), String> {
  posix_fs
    .read_file(&file)
    .map_err(move |err| {
      format!("Error reading file {:?}: {}", file, err.description())
    })
    .and_then(move |content| {
      Ok((
        store.store_file_bytes(&content.content)?,
        content.content.len(),
      ))
    })
    .to_boxed()
}

fn save_directory(
  store: Arc<Store>,
  posix_fs: Arc<fs::PosixFS>,
  base_dir: Arc<fs::Dir>,
) -> BoxFuture<(Fingerprint, usize), String> {
  let store_copy = store.clone();
  Box::new(
    posix_fs
      .scandir(&base_dir)
      .map_err(move |err| {
        format!("Failed to scandir {:?}: {}", &base_dir, err.description())
      })
      .and_then(move |stats| {
        let mut dirs = Vec::new();
        let mut files = Vec::new();
        for stat in stats.into_iter() {
          match stat {
            fs::Stat::Dir(dir) => dirs.push(dir),
            fs::Stat::File(file) => files.push(file),
            fs::Stat::Link(fs::Link(path)) => {
              let err: BoxFuture<_, _> =
                futures::future::err(format!("Don't yet know how to handle symlinks: {:?}", path))
                  .to_boxed();
              return err;
            }
          }
        }

        let store_inner_copy = store_copy.clone();
        let posix_fs_copy = posix_fs.clone();
        let dir_futures: Vec<_> = dirs
          .into_iter()
          .map(move |dir| {
            let dir = Arc::new(dir);
            save_directory(store_inner_copy.clone(), posix_fs_copy.clone(), dir.clone())
              .and_then(move |(fingerprint, proto_size_bytes)| {
                let mut dir_node = bazel_protos::remote_execution::DirectoryNode::new();
                dir_node.set_name(file_name_as_utf8(&dir.0)?);
                dir_node.set_digest(fingerprint_to_digest(&fingerprint, proto_size_bytes as i64));
                Ok(dir_node)
              })
          })
          .collect();

        let file_futures: Vec<_> = files
          .into_iter()
          .map(move |file| {
            file_to_file_node(store_copy.clone(), posix_fs.clone(), file)
          })
          .collect();

        join_all(dir_futures)
          .join(join_all(file_futures))
          .to_boxed()
      })
      .and_then(move |(dirs, files)| {
        let mut directory = bazel_protos::remote_execution::Directory::new();
        directory.set_directories(protobuf::RepeatedField::from_vec(dirs));
        directory.set_files(protobuf::RepeatedField::from_vec(files));
        store.record_directory(&directory)
      }),
  )
}

fn file_to_file_node(
  store: Arc<Store>,
  posix_fs: Arc<fs::PosixFS>,
  file: fs::File,
) -> BoxFuture<bazel_protos::remote_execution::FileNode, String> {
  let file_copy = file.clone();
  save_file(store, &posix_fs, file)
    .and_then(move |(fingerprint, size_bytes)| {
      let mut file_node = bazel_protos::remote_execution::FileNode::new();
      file_node.set_name(file_name_as_utf8(&file_copy.path)?);
      file_node.set_digest(fingerprint_to_digest(&fingerprint, size_bytes as i64));
      file_node.set_is_executable(file_copy.is_executable);
      Ok(file_node)
    })
    .to_boxed()
}

pub fn fingerprint_to_digest(
  fingerprint: &Fingerprint,
  size_bytes: i64,
) -> bazel_protos::remote_execution::Digest {
  let mut digest = bazel_protos::remote_execution::Digest::new();
  digest.set_hash(fingerprint.to_hex());
  digest.set_size_bytes(size_bytes);
  digest
}

fn file_name_as_utf8(path: &Path) -> Result<String, String> {
  match path.file_name() {
    Some(name) => {
      match name.to_str() {
        Some(name_utf8) => Ok(name_utf8.to_string()),
        None => Err(format!(
          "{:?}'s file_name is not representable in UTF8",
          path
        )),
      }
    }
    None => Err(format!("{:?} did not have a file_name", path)),
  }
}
<commit_msg>Output size as well as fingerprint (#5053)<commit_after>extern crate bazel_protos;
extern crate boxfuture;
extern crate clap;
extern crate fs;
extern crate futures;
extern crate protobuf;

use boxfuture::{Boxable, BoxFuture};
use clap::{App, Arg, SubCommand};
use fs::hash::Fingerprint;
use fs::store::Store;
use futures::future::{Future, join_all};
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::Arc;

fn main() {
  match execute(
    App::new("fs_util")
      .subcommand(
        SubCommand::with_name("file")
          .subcommand(
            SubCommand::with_name("cat")
              .about("Output the contents of a file by fingerprint.")
              .arg(Arg::with_name("fingerprint").required(true).takes_value(
                true,
              )),
          )
          .subcommand(
            SubCommand::with_name("save")
              .about(
                "Ingest a file by path, which allows it to be used in Directories/Snapshots. \
Outputs a fingerprint of its contents and its size in bytes, separated by a space.",
              )
              .arg(Arg::with_name("path").required(true).takes_value(true)),
          ),
      )
      .subcommand(
        SubCommand::with_name("directory").subcommand(
          SubCommand::with_name("save")
            .about(
              "Ingest a directory recursively. Saves all files found therein and saves Directory \
protos for each directory found. Outputs a fingerprint of the canonical top-level Directory proto \
and the size of the serialized proto in bytes, separated by a space.",
            )
            .arg(Arg::with_name("source").required(true).takes_value(true)),
        ),
      )
      .arg(
        Arg::with_name("local_store_path")
          .takes_value(true)
          .long("local_store_path")
          .required(true),
      )
      .get_matches(),
  ) {
    Ok(_) => {}
    Err(err) => {
      eprintln!("{}", err);
      exit(1)
    }
  };
}

fn execute(top_match: clap::ArgMatches) -> Result<(), String> {
  let store_dir = top_match.value_of("local_store_path").unwrap();
  let store = Arc::new(Store::new(store_dir).map_err(|e| {
    format!(
      "Failed to open/create store for directory {}: {}",
      store_dir,
      e
    )
  })?);

  match top_match.subcommand() {
    ("file", Some(sub_match)) => {
      match sub_match.subcommand() {
        ("cat", Some(args)) => {
          let fingerprint = Fingerprint::from_hex_string(args.value_of("fingerprint").unwrap())?;
          match store.load_bytes(&fingerprint)? {
            Some(bytes) => {
              io::stdout().write(&bytes).unwrap();
              Ok(())
            }
            None => Err(format!("File with fingerprint {} not found", fingerprint)),
          }
        }
        ("save", Some(args)) => {
          let path = PathBuf::from(args.value_of("path").unwrap());
          let posix_fs = make_posix_fs(path.parent().unwrap());
          let file = posix_fs
            .stat(PathBuf::from(path.file_name().unwrap()))
            .unwrap();
          match file {
            fs::Stat::File(f) => {
              let (fingerprint, size_bytes) = save_file(store, &posix_fs, f).wait().unwrap();
              Ok(println!("{} {}", fingerprint, size_bytes))
            }
            o => Err(format!(
              "Tried to save file {:?} but it was not a file, was a {:?}",
              path,
              o
            )),
          }

        }
        (_, _) => unimplemented!(),
      }
    }
    ("directory", Some(sub_match)) => {
      match sub_match.subcommand() {
        ("save", Some(args)) => {
          let posix_fs = Arc::new(make_posix_fs(args.value_of("source").unwrap()));
          let (fingerprint, size_bytes) =
            save_directory(store, posix_fs, Arc::new(fs::Dir(PathBuf::from("."))))
              .wait()?;
          Ok(println!("{} {}", fingerprint, size_bytes))
        }
        (_, _) => unimplemented!(),
      }
    }
    (_, _) => unimplemented!(),
  }
}

fn make_posix_fs<P: AsRef<Path>>(root: P) -> fs::PosixFS {
  fs::PosixFS::new(&root, vec![]).unwrap()
}

fn save_file(
  store: Arc<Store>,
  posix_fs: &fs::PosixFS,
  file: fs::File,
) -> BoxFuture<(Fingerprint, usize), String> {
  posix_fs
    .read_file(&file)
    .map_err(move |err| {
      format!("Error reading file {:?}: {}", file, err.description())
    })
    .and_then(move |content| {
      Ok((
        store.store_file_bytes(&content.content)?,
        content.content.len(),
      ))
    })
    .to_boxed()
}

fn save_directory(
  store: Arc<Store>,
  posix_fs: Arc<fs::PosixFS>,
  base_dir: Arc<fs::Dir>,
) -> BoxFuture<(Fingerprint, usize), String> {
  let store_copy = store.clone();
  Box::new(
    posix_fs
      .scandir(&base_dir)
      .map_err(move |err| {
        format!("Failed to scandir {:?}: {}", &base_dir, err.description())
      })
      .and_then(move |stats| {
        let mut dirs = Vec::new();
        let mut files = Vec::new();
        for stat in stats.into_iter() {
          match stat {
            fs::Stat::Dir(dir) => dirs.push(dir),
            fs::Stat::File(file) => files.push(file),
            fs::Stat::Link(fs::Link(path)) => {
              let err: BoxFuture<_, _> =
                futures::future::err(format!("Don't yet know how to handle symlinks: {:?}", path))
                  .to_boxed();
              return err;
            }
          }
        }

        let store_inner_copy = store_copy.clone();
        let posix_fs_copy = posix_fs.clone();
        let dir_futures: Vec<_> = dirs
          .into_iter()
          .map(move |dir| {
            let dir = Arc::new(dir);
            save_directory(store_inner_copy.clone(), posix_fs_copy.clone(), dir.clone())
              .and_then(move |(fingerprint, proto_size_bytes)| {
                let mut dir_node = bazel_protos::remote_execution::DirectoryNode::new();
                dir_node.set_name(file_name_as_utf8(&dir.0)?);
                dir_node.set_digest(fingerprint_to_digest(&fingerprint, proto_size_bytes as i64));
                Ok(dir_node)
              })
          })
          .collect();

        let file_futures: Vec<_> = files
          .into_iter()
          .map(move |file| {
            file_to_file_node(store_copy.clone(), posix_fs.clone(), file)
          })
          .collect();

        join_all(dir_futures)
          .join(join_all(file_futures))
          .to_boxed()
      })
      .and_then(move |(dirs, files)| {
        let mut directory = bazel_protos::remote_execution::Directory::new();
        directory.set_directories(protobuf::RepeatedField::from_vec(dirs));
        directory.set_files(protobuf::RepeatedField::from_vec(files));
        store.record_directory(&directory)
      }),
  )
}

fn file_to_file_node(
  store: Arc<Store>,
  posix_fs: Arc<fs::PosixFS>,
  file: fs::File,
) -> BoxFuture<bazel_protos::remote_execution::FileNode, String> {
  let file_copy = file.clone();
  save_file(store, &posix_fs, file)
    .and_then(move |(fingerprint, size_bytes)| {
      let mut file_node = bazel_protos::remote_execution::FileNode::new();
      file_node.set_name(file_name_as_utf8(&file_copy.path)?);
      file_node.set_digest(fingerprint_to_digest(&fingerprint, size_bytes as i64));
      file_node.set_is_executable(file_copy.is_executable);
      Ok(file_node)
    })
    .to_boxed()
}

pub fn fingerprint_to_digest(
  fingerprint: &Fingerprint,
  size_bytes: i64,
) -> bazel_protos::remote_execution::Digest {
  let mut digest = bazel_protos::remote_execution::Digest::new();
  digest.set_hash(fingerprint.to_hex());
  digest.set_size_bytes(size_bytes);
  digest
}

fn file_name_as_utf8(path: &Path) -> Result<String, String> {
  match path.file_name() {
    Some(name) => {
      match name.to_str() {
        Some(name_utf8) => Ok(name_utf8.to_string()),
        None => Err(format!(
          "{:?}'s file_name is not representable in UTF8",
          path
        )),
      }
    }
    None => Err(format!("{:?} did not have a file_name", path)),
  }
}
<|endoftext|>


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

#![feature(type_macros)]

// (typeof used because it's surprisingly hard to find an unparsed token after a stmt)
macro_rules! m {
    () => ( i ; typeof );   //~ ERROR `typeof` is a reserved keyword
                            //~| ERROR macro expansion ignores token `typeof`
                            //~| ERROR macro expansion ignores token `typeof`
                            //~| ERROR macro expansion ignores token `;`
                            //~| ERROR macro expansion ignores token `;`
                            //~| ERROR macro expansion ignores token `i`
}

m!();               //~ NOTE the usage of `m!` is likely invalid in this item context

fn main() {
    let a: m!();    //~ NOTE the usage of `m!` is likely invalid in this type context
    let i = m!();   //~ NOTE the usage of `m!` is likely invalid in this expression context
    match 0 {
        m!() => {}  //~ NOTE the usage of `m!` is likely invalid in this pattern context
    }

    m!();           //~ NOTE the usage of `m!` is likely invalid in this statement context
}
<commit_msg>Fix test failures<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(type_macros)]

// (typeof used because it's surprisingly hard to find an unparsed token after a stmt)
macro_rules! m {
    () => ( i ; typeof );   //~ ERROR `typeof` is a reserved keyword
                            //~| ERROR macro expansion ignores token `typeof`
                            //~| ERROR macro expansion ignores token `typeof`
                            //~| ERROR macro expansion ignores token `;`
                            //~| ERROR macro expansion ignores token `;`
                            //~| ERROR macro expansion ignores token `i`
}

m!();               //~ NOTE the usage of `m!` is likely invalid in item context

fn main() {
    let a: m!();    //~ NOTE the usage of `m!` is likely invalid in type context
    let i = m!();   //~ NOTE the usage of `m!` is likely invalid in expression context
    match 0 {
        m!() => {}  //~ NOTE the usage of `m!` is likely invalid in pattern context
    }

    m!();           //~ NOTE the usage of `m!` is likely invalid in statement context
}
<|endoftext|>


<commit_before>extern crate log_domain;
extern crate num_traits;
extern crate rustomata;

use log_domain::LogDomain;
use num_traits::identities::One;
use std::marker::PhantomData;

use rustomata::approximation::ApproximationStrategy;
use rustomata::approximation::equivalence_classes::EquivalenceRelation;
use rustomata::approximation::relabel::RlbElement;
use rustomata::cfg::*;
use rustomata::push_down_automaton::*;
use rustomata::recognisable::*;

#[test]
fn test_relabel_pushdown() {
    //create (and test) initial push down automata
    let r0_string = "S → [Nt A] # 1";
    let r1_string = "A → [T a, Nt A, Nt B  ] # 0.6";
    let r2_string = "A → [T a              ] # 0.4";
    let r3_string = "B → [T b, Nt B, Nt A  ] # 0.3";
    let r4_string = "B → [T b              ] # 0.7";

    let mut g_string = String::from("initial: [S, B]\n\n");
    g_string.push_str(r0_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r1_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r2_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r3_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r4_string.clone());

    let g: CFG<String, String, LogDomain<f64>> = g_string.parse().unwrap();

    let a = PushDownAutomaton::from(g);

    let mut e_string = String::from("S [S]\n");
    e_string.push_str("N [A, B]\n");
    e_string.push_str("R *\n");

    let e: EquivalenceRelation<String, String> = e_string.parse().unwrap();

    let f = |ps: &PushState<_, _>| ps.map(|nt| e.project(nt));
    let rlb = RlbElement::new(&f);

    let (b, _) = rlb.approximate_automaton(&a);

    assert_ne!(None, b.recognise(vec!["a".to_string() ]).next());
    assert_eq!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string() ]).next());
    assert_ne!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string(), "b".to_string(), "b".to_string(), "a".to_string() ]).next());
    assert_ne!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string(), "b".to_string() ]).next());

    assert_ne!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "a".to_string(), "a".to_string() ]).next());
}

#[test]
fn test_relabel_check() {
    //create (and test) initial push down automata
    let r0_string = "S → [Nt A] # 1";
    let r1_string = "A → [T a, Nt A, Nt B  ] # 0.6";
    let r2_string = "A → [T a              ] # 0.4";
    let r3_string = "B → [T b, Nt B, Nt A  ] # 0.3";
    let r4_string = "B → [T b              ] # 0.7";

    let mut g_string = String::from("initial: [S, B]\n\n");
    g_string.push_str(r0_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r1_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r2_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r3_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r4_string.clone());

    let g: CFG<String, String, LogDomain<f64>> = g_string.parse().unwrap();

    let a = PushDownAutomaton::from(g);

    let mut e_string = String::from("S [S]\n");
    e_string.push_str("N [A, B]\n");
    e_string.push_str("R *\n");

    let e: EquivalenceRelation<String, String> = e_string.parse().unwrap();

    let f = |ps: &PushState<_, _>| ps.map(|nt| e.project(nt));
    let rlb = RlbElement::new(&f);

    let (b, _) = rlb.approximate_automaton(&a);

    let itemb = b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "a".to_string(), "a".to_string() ]).next();
    assert_ne!(None, itemb);
}

#[test]
fn test_from_str_cfg() {
    let c0 : CFGComposition<String,String> = CFGComposition {
        composition: vec![LetterT::Label("A".to_string())],
    };

    let c1 : CFGComposition<String,String>  = CFGComposition {
        composition: vec![LetterT::Value("a".to_string()),LetterT::Label("A".to_string()),LetterT::Label("B".to_string())]
    };

    let c2 : CFGComposition<String,String>  = CFGComposition {
        composition: vec![LetterT::Value("a".to_string())],
    };

    let c3 : CFGComposition<String,String>  = CFGComposition {
        composition: vec![LetterT::Value("b".to_string())],
    };

    let r0: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "S".to_string(),
        composition: c0.clone(),
        weight: LogDomain::one(),
    };

    let r1: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "A".to_string(),
        composition: c1.clone(),
        weight: LogDomain::new(0.6).unwrap(),
    };

    let r2: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "A".to_string(),
        composition: c2.clone(),
        weight: LogDomain::new(0.4).unwrap(),
    };

    let r3: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "B".to_string(),
        composition: c3.clone(),
        weight: LogDomain::one(),
    };

    let r0_string = "S → [Nt A]";
    let r1_string = "A → [T a, Nt A, Nt B] # 0.6";
    let r2_string = "A → [T a] # 0.4";
    let r3_string = "B → [T b] # 1";

    assert_eq!(Ok(r0.clone()),
               r0_string.parse::<CFGRule<String, String, LogDomain<f64>>>());
    assert_eq!(Ok(r1.clone()),
               r1_string.parse::<CFGRule<String, String, LogDomain<f64>>>());
    assert_eq!(Ok(r2.clone()),
               r2_string.parse::<CFGRule<String, String, LogDomain<f64>>>());
    assert_eq!(Ok(r3.clone()),
               r3_string.parse::<CFGRule<String, String, LogDomain<f64>>>());

    let g: CFG<String, String, LogDomain<f64>> = CFG {
        _dummy: PhantomData,
        initial: vec!["S".to_string(),"B".to_string()],
        rules: vec![r0.clone(), r1.clone(), r2.clone(), r3.clone()],
    };

    let mut g_string = String::from("initial: [S, B]\n\n");
    g_string.push_str(r0_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r1_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r2_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r3_string.clone());

    assert_eq!(Ok(g.clone()), g_string.parse());

    let a = PushDownAutomaton::from(g);

    assert_ne!(None, a.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string(), "b".to_string()]).next());
}
<commit_msg>tests/cfg.rs: added helper functions<commit_after>extern crate log_domain;
extern crate num_traits;
extern crate rustomata;

use log_domain::LogDomain;
use num_traits::identities::One;
use std::fs::File;
use std::io::Read;
use std::marker::PhantomData;

use rustomata::approximation::ApproximationStrategy;
use rustomata::approximation::equivalence_classes::EquivalenceRelation;
use rustomata::approximation::relabel::RlbElement;
use rustomata::cfg::*;
use rustomata::push_down_automaton::*;
use rustomata::recognisable::*;

fn example_pushdown_automaton()
    -> PushDownAutomaton<PushState<String, String>, String, LogDomain<f64>>
{
    let mut grammar_file = File::open("examples/example2.cfg").unwrap();
    let mut grammar_string = String::new();
    let _ = grammar_file.read_to_string(&mut grammar_string);
    let grammar: CFG<String, String, _> = grammar_string.parse().unwrap();

    PushDownAutomaton::from(grammar)
}

fn example_equivalence_relation() -> EquivalenceRelation<String, String> {
    let mut relation_file = File::open("examples/example.classes").unwrap();
    let mut relation_string = String::new();
    let _ = relation_file.read_to_string(&mut relation_string);

    relation_string.parse().unwrap()
}

#[test]
fn test_relabel_pushdown() {
    //create (and test) initial push down automata
    let r0_string = "S → [Nt A] # 1";
    let r1_string = "A → [T a, Nt A, Nt B  ] # 0.6";
    let r2_string = "A → [T a              ] # 0.4";
    let r3_string = "B → [T b, Nt B, Nt A  ] # 0.3";
    let r4_string = "B → [T b              ] # 0.7";

    let mut g_string = String::from("initial: [S, B]\n\n");
    g_string.push_str(r0_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r1_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r2_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r3_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r4_string.clone());

    let g: CFG<String, String, LogDomain<f64>> = g_string.parse().unwrap();

    let a = PushDownAutomaton::from(g);

    let mut e_string = String::from("S [S]\n");
    e_string.push_str("N [A, B]\n");
    e_string.push_str("R *\n");

    let e: EquivalenceRelation<String, String> = e_string.parse().unwrap();

    let f = |ps: &PushState<_, _>| ps.map(|nt| e.project(nt));
    let rlb = RlbElement::new(&f);

    let (b, _) = rlb.approximate_automaton(&a);

    assert_ne!(None, b.recognise(vec!["a".to_string() ]).next());
    assert_eq!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string() ]).next());
    assert_ne!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string(), "b".to_string(), "b".to_string(), "a".to_string() ]).next());
    assert_ne!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string(), "b".to_string() ]).next());

    assert_ne!(None, b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "a".to_string(), "a".to_string() ]).next());
}

#[test]
fn test_relabel_check() {
    //create (and test) initial push down automata
    let r0_string = "S → [Nt A] # 1";
    let r1_string = "A → [T a, Nt A, Nt B  ] # 0.6";
    let r2_string = "A → [T a              ] # 0.4";
    let r3_string = "B → [T b, Nt B, Nt A  ] # 0.3";
    let r4_string = "B → [T b              ] # 0.7";

    let mut g_string = String::from("initial: [S, B]\n\n");
    g_string.push_str(r0_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r1_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r2_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r3_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r4_string.clone());

    let g: CFG<String, String, LogDomain<f64>> = g_string.parse().unwrap();

    let a = PushDownAutomaton::from(g);

    let mut e_string = String::from("S [S]\n");
    e_string.push_str("N [A, B]\n");
    e_string.push_str("R *\n");

    let e: EquivalenceRelation<String, String> = e_string.parse().unwrap();

    let f = |ps: &PushState<_, _>| ps.map(|nt| e.project(nt));
    let rlb = RlbElement::new(&f);

    let (b, _) = rlb.approximate_automaton(&a);

    let itemb = b.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "a".to_string(), "a".to_string() ]).next();
    assert_ne!(None, itemb);
}

#[test]
fn test_from_str_cfg() {
    let c0 : CFGComposition<String,String> = CFGComposition {
        composition: vec![LetterT::Label("A".to_string())],
    };

    let c1 : CFGComposition<String,String>  = CFGComposition {
        composition: vec![LetterT::Value("a".to_string()),LetterT::Label("A".to_string()),LetterT::Label("B".to_string())]
    };

    let c2 : CFGComposition<String,String>  = CFGComposition {
        composition: vec![LetterT::Value("a".to_string())],
    };

    let c3 : CFGComposition<String,String>  = CFGComposition {
        composition: vec![LetterT::Value("b".to_string())],
    };

    let r0: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "S".to_string(),
        composition: c0.clone(),
        weight: LogDomain::one(),
    };

    let r1: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "A".to_string(),
        composition: c1.clone(),
        weight: LogDomain::new(0.6).unwrap(),
    };

    let r2: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "A".to_string(),
        composition: c2.clone(),
        weight: LogDomain::new(0.4).unwrap(),
    };

    let r3: CFGRule<String, String, LogDomain<f64>> = CFGRule {
        head: "B".to_string(),
        composition: c3.clone(),
        weight: LogDomain::one(),
    };

    let r0_string = "S → [Nt A]";
    let r1_string = "A → [T a, Nt A, Nt B] # 0.6";
    let r2_string = "A → [T a] # 0.4";
    let r3_string = "B → [T b] # 1";

    assert_eq!(Ok(r0.clone()),
               r0_string.parse::<CFGRule<String, String, LogDomain<f64>>>());
    assert_eq!(Ok(r1.clone()),
               r1_string.parse::<CFGRule<String, String, LogDomain<f64>>>());
    assert_eq!(Ok(r2.clone()),
               r2_string.parse::<CFGRule<String, String, LogDomain<f64>>>());
    assert_eq!(Ok(r3.clone()),
               r3_string.parse::<CFGRule<String, String, LogDomain<f64>>>());

    let g: CFG<String, String, LogDomain<f64>> = CFG {
        _dummy: PhantomData,
        initial: vec!["S".to_string(),"B".to_string()],
        rules: vec![r0.clone(), r1.clone(), r2.clone(), r3.clone()],
    };

    let mut g_string = String::from("initial: [S, B]\n\n");
    g_string.push_str(r0_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r1_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r2_string.clone());
    g_string.push_str("\n");
    g_string.push_str(r3_string.clone());

    assert_eq!(Ok(g.clone()), g_string.parse());

    let a = PushDownAutomaton::from(g);

    assert_ne!(None, a.recognise(vec!["a".to_string(), "a".to_string(), "a".to_string(), "b".to_string(), "b".to_string()]).next());
}
<|endoftext|>


<commit_before>use delivery::git::git_command;
use delivery::utils::copy_recursive;
use delivery::utils::say;
use std::io::prelude::*;
use tempdir::TempDir;
use std::fs::File;
use std::path::Path;
use support::paths::fixture_file;
use std::process::{Command, Output};
use std::env;
use rustc_serialize::json::Json;
use delivery::utils::path_join_many::PathJoinMany;

// ** Functions used in tests **

fn setup() {
    say::turn_off_spinner();
}

/// Sets up a mock delivery git project from the test_repo fixture.
/// Includes copying in the `.delivery/config.json` that you plan
/// on using.
fn setup_mock_delivery_project_git(dot_config: &str) -> TempDir {
    let tmpdir = TempDir::new("mock-delivery-remote").unwrap();
    let test_repo_path = fixture_file("test_repo");
    panic_on_error!(copy_recursive(&test_repo_path.join(".delivery"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("README.md"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("cookbooks"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&fixture_file(dot_config), &tmpdir.path().join_many(&[".delivery", "config.json"])));
    panic_on_error!(git_command(&["init", tmpdir.path().to_str().unwrap()], tmpdir.path()));
    panic_on_error!(git_command(&["add", "."], tmpdir.path()));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], tmpdir.path()));
    tmpdir
}

/// Given a path, it copies the build cookbook into it, and turns it
/// into a git repository
fn setup_build_cookbook_project(tmpdir: &Path) {
    let build_cookbook_path = fixture_file("delivery_test");
    panic_on_error!(copy_recursive(&build_cookbook_path, &tmpdir.to_path_buf()));
    panic_on_error!(git_command(&["init", tmpdir.join("delivery_test").to_str().unwrap()], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["add", "."], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], &tmpdir.join("delivery_test")));
}

/// Clones a mock delivery git project to a local copy, as if it was
/// on a workstation. Also creates a mock delivery remote pointing at
/// the on-disk mocked delivery project.
fn setup_local_project_clone(delivery_project_git: &Path) -> TempDir {
    let tmpdir = TempDir::new("local-project").unwrap();
    panic_on_error!(git_command(&["clone",
                                  delivery_project_git.to_str().unwrap(),
                                  tmpdir.path().to_str().unwrap()
                                 ], tmpdir.path()));
    panic_on_error!(git_command(&["remote", "add", "delivery", delivery_project_git.to_str().unwrap()], tmpdir.path()));
    let mut command = delivery_cmd();
    command.arg("setup")
           .arg("--user").arg("cavalera")
           .arg("--server").arg("localhost")
           .arg("--ent").arg("family")
           .arg("--org").arg("sepultura")
           .arg("--for").arg("master")
           .arg("--config-path").arg(tmpdir.path().to_str().unwrap());
    assert_command_successful(&mut command, &tmpdir.path());
    tmpdir
}

/// Makes a change to a project on the named branch. Creates a
/// file named `filename` and writes some stuff to it.
///
/// When it returns, the project you pass in will be left on your
/// new branch.
fn setup_change(tmpdir: &Path, branch: &str, filename: &str) {
    panic_on_error!(git_command(&["checkout", "master"], tmpdir));
    panic_on_error!(git_command(&["branch", branch], tmpdir));
    {
        let mut f = panic_on_error!(File::create(&tmpdir.join(filename)));
        panic_on_error!(f.write_all(b"I like cookies"));
    }
    panic_on_error!(git_command(&["add", filename], tmpdir));
    panic_on_error!(git_command(&["commit", "-a", "-m", filename], tmpdir));
}

/// Checks out the named branch
fn setup_checkout_branch(tmpdir: &Path, branch: &str) {
    panic_on_error!(git_command(&["checkout", branch], tmpdir));
}

/// Runs the `Command` in `Dir`, and makes sure it exists with 0
/// Returns the result
fn assert_command_successful(command: &mut Command, dir: &Path) -> Output {
    let result = panic_on_error!(command.current_dir(&dir).output());
    if ! result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Failed command {:?}\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.to_str().unwrap());
    };
    result
}

/// Same as `assert_command_successful` above, excepts it asserts the command
/// exits with a non-0 status code
fn assert_command_failed(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Command {:?} should have failed!\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Builds the command to run delivery review
fn delivery_review_command(pipeline: &str) -> Command {
    let mut command = delivery_cmd();
    command.arg("review").arg("--no-open").arg("--for").arg(pipeline);
    command
}

/// Builds the command to run a sample job
fn delivery_verify_command(job_root: &TempDir) -> Command {
    let mut command = delivery_cmd();
    command.arg("job")
           .arg("verify")
           .arg("unit")
           .arg("--no-spinner")
           .arg("--job-root").arg(job_root.path().to_str().unwrap());
    command
}

/// Calls delivery review, and creates the two stub branches that the
/// api would create (`_reviews/PIPELINE/BRANCH/1` and `_reviews/PIPELINE/BRANCH/latest`)
fn delivery_review(local: &TempDir, remote: &TempDir, branch: &str, pipeline: &str) {
    panic_on_error!(git_command(&["checkout", branch], local.path()));
    let mut command = delivery_review_command(pipeline);
    assert_command_successful(&mut command, local.path());

    // Stub out the behavior of the delivery-api
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/1", pipeline, branch)], remote.path()));
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/latest", pipeline, branch)], remote.path()));
}

/// Returns a Command set to the delivery binary created when you
/// ran `cargo test`.
fn delivery_cmd() -> Command {
    let mut delivery_path = env::current_exe().unwrap();
    delivery_path.pop();
    Command::new(delivery_path.join("delivery").to_str().unwrap())
}

/// A handy debugging function. Insert it when you want to sleep,
/// pass it a tmpdir, and you can inspect it.
///
/// Make sure you run `cargo test -- --nocapture` to see the output.
#[allow(dead_code)]
fn debug_sleep(tmpdir: &TempDir) {
    println!("Sleeping for 1000 seconds for {:?}", tmpdir.path());
    panic_on_error!(Command::new("sleep").arg("1000").output());
}

// ** Actual tests **

// Tests `delivery review`. Fails if the command fails, or if we fail to create
// the remote branch _for/master/rust/test, which is what we need to push to
// the API server.
test!(review {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_a_v1_config {
    let delivery_project_git = setup_mock_delivery_project_git("v1_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_without_dependencies {
    let delivery_project_git = setup_mock_delivery_project_git("no_deps_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_an_invalid_config {
    let delivery_project_git = setup_mock_delivery_project_git("invalid_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_review_command("rust/test");
    assert_command_failed(&mut command, &local_project);
});

test!(job_verify_unit_with_path_config {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
});

test!(job_verify_unit_with_git_config {
    let delivery_project_git = setup_mock_delivery_project_git("git_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_build_cookbook_project(&job_root.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
});

// TODO: This test requires internet access... we should move it out
// into an Acceptance-stage test instead of here in unit tests. It's
// impossible to run on a plane, for instance :(
test!(job_verify_unit_with_supermarket_config {
    let delivery_project_git = setup_mock_delivery_project_git("supermarket_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_failed(&mut command, &local_project);
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd"]).is_dir());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd", "templates", "default", "magic.erb"]).is_file());
});

test!(job_verify_dna_json {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
    let mut dna_file = panic_on_error!(File::open(&job_root.path().join_many(&["chef", "dna.json"])));
    let mut dna_json = String::new();
    panic_on_error!(dna_file.read_to_string(&mut dna_json));
    let dna_data = panic_on_error!(Json::from_str(&dna_json));
    match dna_data.find_path(&["delivery", "workspace", "repo"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("repo").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/repo, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "chef"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("chef").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/chef, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "cache"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("cache").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/cache, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "root"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().to_str().unwrap());
        },
        None => panic!("No delivery/workspace/root, {}", dna_data)
    };
    match dna_data.find_path(&["delivery_builder", "build_user"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), "dbuild");
        },
        None => panic!("No delivery_builderl/build_user, {}", dna_data)
    };
});
<commit_msg>Path not TempDir for assert_command_failed<commit_after>use delivery::git::git_command;
use delivery::utils::copy_recursive;
use delivery::utils::say;
use std::io::prelude::*;
use tempdir::TempDir;
use std::fs::File;
use std::path::Path;
use support::paths::fixture_file;
use std::process::{Command, Output};
use std::env;
use rustc_serialize::json::Json;
use delivery::utils::path_join_many::PathJoinMany;

// ** Functions used in tests **

fn setup() {
    say::turn_off_spinner();
}

/// Sets up a mock delivery git project from the test_repo fixture.
/// Includes copying in the `.delivery/config.json` that you plan
/// on using.
fn setup_mock_delivery_project_git(dot_config: &str) -> TempDir {
    let tmpdir = TempDir::new("mock-delivery-remote").unwrap();
    let test_repo_path = fixture_file("test_repo");
    panic_on_error!(copy_recursive(&test_repo_path.join(".delivery"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("README.md"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("cookbooks"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&fixture_file(dot_config), &tmpdir.path().join_many(&[".delivery", "config.json"])));
    panic_on_error!(git_command(&["init", tmpdir.path().to_str().unwrap()], tmpdir.path()));
    panic_on_error!(git_command(&["add", "."], tmpdir.path()));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], tmpdir.path()));
    tmpdir
}

/// Given a path, it copies the build cookbook into it, and turns it
/// into a git repository
fn setup_build_cookbook_project(tmpdir: &Path) {
    let build_cookbook_path = fixture_file("delivery_test");
    panic_on_error!(copy_recursive(&build_cookbook_path, &tmpdir.to_path_buf()));
    panic_on_error!(git_command(&["init", tmpdir.join("delivery_test").to_str().unwrap()], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["add", "."], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], &tmpdir.join("delivery_test")));
}

/// Clones a mock delivery git project to a local copy, as if it was
/// on a workstation. Also creates a mock delivery remote pointing at
/// the on-disk mocked delivery project.
fn setup_local_project_clone(delivery_project_git: &Path) -> TempDir {
    let tmpdir = TempDir::new("local-project").unwrap();
    panic_on_error!(git_command(&["clone",
                                  delivery_project_git.to_str().unwrap(),
                                  tmpdir.path().to_str().unwrap()
                                 ], tmpdir.path()));
    panic_on_error!(git_command(&["remote", "add", "delivery", delivery_project_git.to_str().unwrap()], tmpdir.path()));
    let mut command = delivery_cmd();
    command.arg("setup")
           .arg("--user").arg("cavalera")
           .arg("--server").arg("localhost")
           .arg("--ent").arg("family")
           .arg("--org").arg("sepultura")
           .arg("--for").arg("master")
           .arg("--config-path").arg(tmpdir.path().to_str().unwrap());
    assert_command_successful(&mut command, &tmpdir.path());
    tmpdir
}

/// Makes a change to a project on the named branch. Creates a
/// file named `filename` and writes some stuff to it.
///
/// When it returns, the project you pass in will be left on your
/// new branch.
fn setup_change(tmpdir: &Path, branch: &str, filename: &str) {
    panic_on_error!(git_command(&["checkout", "master"], tmpdir));
    panic_on_error!(git_command(&["branch", branch], tmpdir));
    {
        let mut f = panic_on_error!(File::create(&tmpdir.join(filename)));
        panic_on_error!(f.write_all(b"I like cookies"));
    }
    panic_on_error!(git_command(&["add", filename], tmpdir));
    panic_on_error!(git_command(&["commit", "-a", "-m", filename], tmpdir));
}

/// Checks out the named branch
fn setup_checkout_branch(tmpdir: &Path, branch: &str) {
    panic_on_error!(git_command(&["checkout", branch], tmpdir));
}

/// Runs the `Command` in `Dir`, and makes sure it exists with 0
/// Returns the result
fn assert_command_successful(command: &mut Command, dir: &Path) -> Output {
    let result = panic_on_error!(command.current_dir(&dir).output());
    if ! result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Failed command {:?}\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.to_str().unwrap());
    };
    result
}

/// Same as `assert_command_successful` above, excepts it asserts the command
/// exits with a non-0 status code
fn assert_command_failed(command: &mut Command, dir: &Path) -> Output {
    let result = panic_on_error!(command.current_dir(&dir).output());
    if result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Command {:?} should have failed!\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.to_str().unwrap());
    };
    result
}

/// Builds the command to run delivery review
fn delivery_review_command(pipeline: &str) -> Command {
    let mut command = delivery_cmd();
    command.arg("review").arg("--no-open").arg("--for").arg(pipeline);
    command
}

/// Builds the command to run a sample job
fn delivery_verify_command(job_root: &TempDir) -> Command {
    let mut command = delivery_cmd();
    command.arg("job")
           .arg("verify")
           .arg("unit")
           .arg("--no-spinner")
           .arg("--job-root").arg(job_root.path().to_str().unwrap());
    command
}

/// Calls delivery review, and creates the two stub branches that the
/// api would create (`_reviews/PIPELINE/BRANCH/1` and `_reviews/PIPELINE/BRANCH/latest`)
fn delivery_review(local: &TempDir, remote: &TempDir, branch: &str, pipeline: &str) {
    panic_on_error!(git_command(&["checkout", branch], local.path()));
    let mut command = delivery_review_command(pipeline);
    assert_command_successful(&mut command, local.path());

    // Stub out the behavior of the delivery-api
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/1", pipeline, branch)], remote.path()));
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/latest", pipeline, branch)], remote.path()));
}

/// Returns a Command set to the delivery binary created when you
/// ran `cargo test`.
fn delivery_cmd() -> Command {
    let mut delivery_path = env::current_exe().unwrap();
    delivery_path.pop();
    Command::new(delivery_path.join("delivery").to_str().unwrap())
}

/// A handy debugging function. Insert it when you want to sleep,
/// pass it a tmpdir, and you can inspect it.
///
/// Make sure you run `cargo test -- --nocapture` to see the output.
#[allow(dead_code)]
fn debug_sleep(tmpdir: &TempDir) {
    println!("Sleeping for 1000 seconds for {:?}", tmpdir.path());
    panic_on_error!(Command::new("sleep").arg("1000").output());
}

// ** Actual tests **

// Tests `delivery review`. Fails if the command fails, or if we fail to create
// the remote branch _for/master/rust/test, which is what we need to push to
// the API server.
test!(review {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_a_v1_config {
    let delivery_project_git = setup_mock_delivery_project_git("v1_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_without_dependencies {
    let delivery_project_git = setup_mock_delivery_project_git("no_deps_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_an_invalid_config {
    let delivery_project_git = setup_mock_delivery_project_git("invalid_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_review_command("rust/test");
    assert_command_failed(&mut command, &local_project.path());
});

test!(job_verify_unit_with_path_config {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
});

test!(job_verify_unit_with_git_config {
    let delivery_project_git = setup_mock_delivery_project_git("git_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_build_cookbook_project(&job_root.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
});

// TODO: This test requires internet access... we should move it out
// into an Acceptance-stage test instead of here in unit tests. It's
// impossible to run on a plane, for instance :(
test!(job_verify_unit_with_supermarket_config {
    let delivery_project_git = setup_mock_delivery_project_git("supermarket_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_failed(&mut command, &local_project.path());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd"]).is_dir());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd", "templates", "default", "magic.erb"]).is_file());
});

test!(job_verify_dna_json {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project.path());
    let mut dna_file = panic_on_error!(File::open(&job_root.path().join_many(&["chef", "dna.json"])));
    let mut dna_json = String::new();
    panic_on_error!(dna_file.read_to_string(&mut dna_json));
    let dna_data = panic_on_error!(Json::from_str(&dna_json));
    match dna_data.find_path(&["delivery", "workspace", "repo"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("repo").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/repo, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "chef"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("chef").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/chef, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "cache"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("cache").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/cache, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "root"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().to_str().unwrap());
        },
        None => panic!("No delivery/workspace/root, {}", dna_data)
    };
    match dna_data.find_path(&["delivery_builder", "build_user"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), "dbuild");
        },
        None => panic!("No delivery_builderl/build_user, {}", dna_data)
    };
});
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

use nom::{IResult,Needed,FlatMapOpt,not_line_ending, space, alphanumeric, multispace};
use nom::IResult::*;

use std::str;
use std::str::from_utf8;
use std::collections::HashMap;

take_until_and_leave!(category_bytes "]");
take_until_either_and_leave!(value_bytes "\n;");

named!(value_parser<&[u8],&str>, map_res!(value_bytes, from_utf8));

named!(parameter_parser<&[u8],&str>, map_res!(alphanumeric, from_utf8));

named!(category     <&[u8], &str>,
  chain!(
          tag!("[")       ~
    name: map_res!(
      category_bytes,
      from_utf8)          ~
          tag!("]")       ~
          multispace?     ,
    ||{ name }
  )
);

named!(key_value    <&[u8],(&str,&str)>,
  chain!(
    key: parameter_parser     ~
         space?               ~
         tag!("=")            ~
         space?               ~
    val: value_parser         ~
         space?               ~
         chain!(
           tag!(";")        ~
           not_line_ending  ,
           ||{}
         )?                   ~
         multispace?          ,
    ||{(key, val)}
  )
);

named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
  let mut h: HashMap<&str, &str> = HashMap::new();

  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,v) in tuple_vec.iter() {
        h.insert(k, v);
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
  chain!(
    category: category    ~
    keys: keys_and_values ,
    move ||{(category, keys)}
  )
);

named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
  let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();

  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,ref v) in tuple_vec.iter() {
        h.insert(k, v.clone());
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_category_test() {
  let ini_file = b"[category]

parameter=value
key = value2";

  let ini_without_category = b"parameter=value
key = value2";

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category, "category"));
}

#[test]
fn parse_value_test() {
  let ini_file1 = b"value
key =";
  let end = b"
key =";

  let res = value_parser(ini_file1);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?})", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(end,  "value"));

  let ini_file2 = b"value;blah
key =";
  let end2 = b";blah
key =";

  let res2 = value_parser(ini_file2);
  println!("{:?}", res2);
  match res2 {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res2, Done(end2,  "value"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = b"parameter=value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = b"parameter = value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = b"parameter=value;abc
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = b"parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = b"[category]";

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = b"[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = b"[category]";

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let ini_after_parser = b"";

  let res = categories(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Done(ini_after_parser, expected_h));
}

<commit_msg>remove two sueless functions from the ini parser<commit_after>#[macro_use]
extern crate nom;

use nom::{IResult,Needed,FlatMapOpt,not_line_ending, space, alphanumeric, multispace};
use nom::IResult::*;

use std::str;
use std::str::from_utf8;
use std::collections::HashMap;

take_until_and_leave!(category_bytes "]");
take_until_either_and_leave!(value_bytes "\n;");

named!(category     <&[u8], &str>,
  chain!(
          tag!("[")       ~
    name: map_res!(
      category_bytes,
      from_utf8)          ~
          tag!("]")       ~
          multispace?     ,
    ||{ name }
  )
);

named!(key_value    <&[u8],(&str,&str)>,
  chain!(
    key: map_res!(alphanumeric, from_utf8) ~
         space?                            ~
         tag!("=")                         ~
         space?                            ~
    val: map_res!(value_bytes, from_utf8)  ~
         space?                            ~
         chain!(
           tag!(";")        ~
           not_line_ending  ,
           ||{}
         ) ?                               ~
         multispace?                       ,
    ||{(key, val)}
  )
);

named!(keys_and_values_aggregator<&[u8], Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&[u8]) -> IResult<&[u8], HashMap<&str, &str> > {
  let mut h: HashMap<&str, &str> = HashMap::new();

  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,v) in tuple_vec.iter() {
        h.insert(k, v);
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
  chain!(
    category: category    ~
    keys: keys_and_values ,
    move ||{(category, keys)}
  )
);

named!(categories_aggregator<&[u8], Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str> > > {
  let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();

  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,ref v) in tuple_vec.iter() {
        h.insert(k, v.clone());
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_category_test() {
  let ini_file = b"[category]

parameter=value
key = value2";

  let ini_without_category = b"parameter=value
key = value2";

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category, "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = b"parameter=value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = b"parameter = value
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = b"parameter=value;abc
key = value2";

  let ini_without_key_value = b"key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = b"parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = b"[category]";

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = b"[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = b"[category]";

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = b"[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let ini_after_parser = b"";

  let res = categories(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, Done(ini_after_parser, expected_h));
}

<|endoftext|>


<commit_before>extern crate rust_monster;
#[cfg(test)]
mod tests
{
    use rust_monster::ga::GeneticAlgorithm;
	use rust_monster::ga;

	#[allow(dead_code)]
	struct Foo { a : i32}
	impl ga::GASolution for Foo
	{
	    fn evaluate(&mut self) -> f32 { 0.0 }
	#[allow(unused_variables)]
	    fn crossover(&self, other : &Self) -> &Self { return &self }
	    fn mutate(&mut self) {}
	}

	#[test]
	fn init_test()
	{
	    let mut ga : ga::SimpleGeneticAlgorithm<Foo> =
	                 ga::SimpleGeneticAlgorithm::new(ga::SimpleGeneticAlgorithmCfg {
	                                                   d_seed : 1,
	                                                   flags : ga::DEBUG_FLAG,
	                                                   max_generations: 100,
	                                                   ..Default::default()
	                                                 }
	    );
	    ga.initialize();
	    assert_eq!(ga.step(), 1);
	    assert_eq!(ga.done(), false);
	}
}
<commit_msg>Indent fixups<commit_after>extern crate rust_monster;
#[cfg(test)]
mod tests
{
    use rust_monster::ga::GeneticAlgorithm;
    use rust_monster::ga;

    #[allow(dead_code)]
    struct Foo { a : i32}
    impl ga::GASolution for Foo
    {
        fn evaluate(&mut self) -> f32 { 0.0 }
    #[allow(unused_variables)]
        fn crossover(&self, other : &Self) -> &Self { return &self }
        fn mutate(&mut self) {}
    }

    #[test]
    fn init_test()
    {
        let mut ga : ga::SimpleGeneticAlgorithm<Foo> =
                     ga::SimpleGeneticAlgorithm::new(ga::SimpleGeneticAlgorithmCfg {
                                                       d_seed : 1,
                                                       flags : ga::DEBUG_FLAG,
                                                       max_generations: 100,
                                                       ..Default::default()
                                                     }
        );
        ga.initialize();
        assert_eq!(ga.step(), 1);
        assert_eq!(ga.done(), false);
    }
}
<|endoftext|>