Separating binary into library for doctests

This commit is contained in:
vandomej 2021-08-12 19:27:30 -07:00
parent f751e46389
commit 5e204a2c28
10 changed files with 292 additions and 233 deletions

View file

@ -1,15 +1,6 @@
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
extern crate regex; extern crate gemla;
#[macro_use]
pub mod tree;
pub mod bracket;
pub mod constants;
pub mod file_linked;
#[cfg(test)]
mod tests;
use clap::App; use clap::App;
use std::fs::metadata; use std::fs::metadata;
@ -21,11 +12,11 @@ use std::fs::metadata;
fn main() { fn main() {
// Command line arguments are parsed with the clap crate. And this program uses // Command line arguments are parsed with the clap crate. And this program uses
// the yaml method with clap. // the yaml method with clap.
let yaml = load_yaml!("../cli.yml"); let yaml = load_yaml!("../../cli.yml");
let matches = App::from_yaml(yaml).get_matches(); let matches = App::from_yaml(yaml).get_matches();
// Checking that the first argument <DIRECTORY> is a valid directory // Checking that the first argument <DIRECTORY> is a valid directory
let directory = matches.value_of(constants::args::DIRECTORY).unwrap(); let directory = matches.value_of(gemla::constants::args::DIRECTORY).unwrap();
let metadata = metadata(directory); let metadata = metadata(directory);
match &metadata { match &metadata {
Ok(m) if m.is_dir() => { Ok(m) if m.is_dir() => {

View file

@ -8,6 +8,46 @@ use std::fmt;
/// A trait used to interact with the internal state of nodes within the genetic bracket /// A trait used to interact with the internal state of nodes within the genetic bracket
pub trait GeneticNode { pub trait GeneticNode {
/// Initializes a new instance of a genetic state. /// Initializes a new instance of a genetic state.
///
/// # Examples
///
/// ```
/// # use gemla::bracket::genetic_node::GeneticNode;
/// #
/// struct Node {
/// pub fit_score: f64,
/// }
///
/// impl GeneticNode for Node {
/// fn initialize() -> Result<Box<Self>, String> {
/// Ok(Box::new(Node {fit_score: 0.0}))
/// }
///
/// //...
/// #
/// # fn simulate(&mut self, iterations: u64) -> Result<(), String> {
/// # Ok(())
/// # }
/// #
/// # fn get_fit_score(&self) -> f64 {
/// # self.fit_score
/// # }
/// #
/// # fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
/// # Ok(())
/// # }
/// #
/// # fn mutate(&mut self) -> Result<(), String> {
/// # Ok(())
/// # }
/// }
///
/// # fn main() -> Result<(), String> {
/// let node = Node::initialize()?;
/// assert_eq!(node.get_fit_score(), 0.0);
/// # Ok(())
/// # }
/// ```
fn initialize() -> Result<Box<Self>, String>; fn initialize() -> Result<Box<Self>, String>;
/// Runs a simulation on the state object in order to guage it's fitness. /// Runs a simulation on the state object in order to guage it's fitness.

View file

@ -122,3 +122,120 @@ where
Ok(self) Ok(self)
} }
} }
#[cfg(test)]
mod tests
{
use super::*;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use std::string::ToString;
#[derive(Default, Deserialize, Serialize, Clone)]
struct TestState {
pub score: f64,
}
impl FromStr for TestState {
type Err = String;
fn from_str(s: &str) -> Result<TestState, Self::Err> {
toml::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
}
}
impl fmt::Display for TestState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.score)
}
}
impl TestState {
fn new(score: f64) -> TestState {
TestState { score: score }
}
}
impl genetic_node::GeneticNode for TestState {
fn simulate(&mut self, iterations: u64) -> Result<(), String> {
self.score += iterations as f64;
Ok(())
}
fn get_fit_score(&self) -> f64 {
self.score
}
fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
Ok(())
}
fn mutate(&mut self) -> Result<(), String> {
Ok(())
}
fn initialize() -> Result<Box<Self>, String> {
Ok(Box::new(TestState { score: 0.0 }))
}
}
#[test]
fn test_new() {
let bracket = Bracket::<TestState>::initialize("./temp".to_string())
.expect("Bracket failed to initialize");
assert_eq!(
format!("{}", bracket),
format!("{{\"tree\":{},\"step\":0,\"iteration_scaling\":{{\"enumType\":\"Linear\",\"enumContent\":1}}}}",
btree!(TestState::new(0.0)))
);
std::fs::remove_file("./temp").expect("Unable to remove file");
}
#[test]
fn test_run() {
let mut bracket = Bracket::<TestState>::initialize("./temp2".to_string())
.expect("Bracket failed to initialize");
bracket
.mutate(|b| drop(b.iteration_scaling(IterationScaling::Linear(2))))
.expect("Failed to set iteration scaling");
for _ in 0..3 {
bracket
.mutate(|b| drop(b.run_simulation_step()))
.expect("Failed to run step");
}
assert_eq!(
format!("{}", bracket),
format!("{{\"tree\":{},\"step\":3,\"iteration_scaling\":{{\"enumType\":\"Linear\",\"enumContent\":2}}}}",
btree!(
TestState::new(12.0),
btree!(
TestState::new(12.0),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0))),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0)))
),
btree!(
TestState::new(12.0),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0))),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0))))
)
)
);
std::fs::remove_file("./temp2").expect("Unable to remove file");
}
}

View file

@ -103,3 +103,43 @@ impl<T: fmt::Display> fmt::Display for FileLinked<T> {
write!(f, "{}", self.val) write!(f, "{}", self.val)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_mutate() -> Result<(), String> {
let tree = btree!(1, btree!(2), btree!(3, btree!(4),));
let mut linked_tree = FileLinked::new(tree, String::from("test.txt"))?;
assert_eq!(
format!("{}", linked_tree.readonly()),
"{\"val\":1,\"left\":{\"val\":2,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}"
);
linked_tree.mutate(|v1| v1.val = 10)?;
assert_eq!(
format!("{}", linked_tree.readonly()),
"{\"val\":10,\"left\":{\"val\":2,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}"
);
linked_tree.mutate(|v1| {
let mut left = v1.left.clone().unwrap();
left.val = 13;
v1.left = Some(left);
})?;
assert_eq!(
format!("{}", linked_tree.readonly()),
"{\"val\":10,\"left\":{\"val\":13,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}"
);
fs::remove_file("test.txt").expect("Unable to remove file");
Ok(())
}
}

7
gemla/src/lib.rs Normal file
View file

@ -0,0 +1,7 @@
extern crate regex;
#[macro_use]
pub mod tree;
pub mod bracket;
pub mod constants;
pub mod file_linked;

View file

@ -1,111 +0,0 @@
use super::super::bracket;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use std::string::ToString;
#[derive(Default, Deserialize, Serialize, Clone)]
struct TestState {
pub score: f64,
}
impl FromStr for TestState {
type Err = String;
fn from_str(s: &str) -> Result<TestState, Self::Err> {
toml::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
}
}
impl fmt::Display for TestState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.score)
}
}
impl TestState {
fn new(score: f64) -> TestState {
TestState { score: score }
}
}
impl bracket::genetic_node::GeneticNode for TestState {
fn simulate(&mut self, iterations: u64) -> Result<(), String> {
self.score += iterations as f64;
Ok(())
}
fn get_fit_score(&self) -> f64 {
self.score
}
fn calculate_scores_and_trim(&mut self) -> Result<(), String> {
Ok(())
}
fn mutate(&mut self) -> Result<(), String> {
Ok(())
}
fn initialize() -> Result<Box<Self>, String> {
Ok(Box::new(TestState { score: 0.0 }))
}
}
#[test]
fn test_new() {
let bracket = bracket::Bracket::<TestState>::initialize("./temp".to_string())
.expect("Bracket failed to initialize");
assert_eq!(
format!("{}", bracket),
format!("{{\"tree\":{},\"step\":0,\"iteration_scaling\":{{\"enumType\":\"Linear\",\"enumContent\":1}}}}",
btree!(TestState::new(0.0)))
);
std::fs::remove_file("./temp").expect("Unable to remove file");
}
#[test]
fn test_run() {
let mut bracket = bracket::Bracket::<TestState>::initialize("./temp".to_string())
.expect("Bracket failed to initialize");
bracket
.mutate(|b| drop(b.iteration_scaling(bracket::IterationScaling::Linear(2))))
.expect("Failed to set iteration scaling");
for _ in 0..3 {
bracket
.mutate(|b| drop(b.run_simulation_step()))
.expect("Failed to run step");
}
assert_eq!(
format!("{}", bracket),
format!("{{\"tree\":{},\"step\":3,\"iteration_scaling\":{{\"enumType\":\"Linear\",\"enumContent\":2}}}}",
btree!(
TestState::new(12.0),
btree!(
TestState::new(12.0),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0))),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0)))
),
btree!(
TestState::new(12.0),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0))),
btree!(TestState::new(6.0),
btree!(TestState::new(2.0)),
btree!(TestState::new(2.0))))
)
)
);
std::fs::remove_file("./temp").expect("Unable to remove file");
}

View file

@ -1,36 +0,0 @@
use super::super::file_linked::FileLinked;
use std::fs;
#[test]
fn test_mutate() -> Result<(), String> {
let tree = btree!(1, btree!(2), btree!(3, btree!(4),));
let mut linked_tree = FileLinked::new(tree, String::from("test.txt"))?;
assert_eq!(
format!("{}", linked_tree.readonly()),
"{\"val\":1,\"left\":{\"val\":2,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}"
);
linked_tree.mutate(|v1| v1.val = 10)?;
assert_eq!(
format!("{}", linked_tree.readonly()),
"{\"val\":10,\"left\":{\"val\":2,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}"
);
linked_tree.mutate(|v1| {
let mut left = v1.left.clone().unwrap();
left.val = 13;
v1.left = Some(left);
})?;
assert_eq!(
format!("{}", linked_tree.readonly()),
"{\"val\":10,\"left\":{\"val\":13,\"left\":null,\"right\":null},\"right\":{\"val\":3,\"left\":{\"val\":4,\"left\":null,\"right\":null},\"right\":null}}"
);
fs::remove_file("test.txt").expect("Unable to remove file");
Ok(())
}

View file

@ -1,3 +0,0 @@
mod bracket;
mod file_linked;
mod tree;

View file

@ -1,36 +0,0 @@
use super::super::tree::Tree;
#[test]
fn test_new() {
assert_eq!(
Tree::new(30, None, Some(Box::new(Tree::new(20, None, None)))),
Tree {
val: 30,
left: None,
right: Some(Box::new(Tree {
val: 20,
left: None,
right: None,
})),
}
);
}
#[test]
fn test_fmt() {
assert_eq!(
format!("{}", btree!("foo", btree!("bar"),),),
"{\"val\":\"foo\",\"left\":{\"val\":\"bar\",\"left\":null,\"right\":null},\"right\":null}"
);
}
#[test]
fn test_fmt_node() {
let t = btree!(17, btree!(16), btree!(12));
assert_eq!(Tree::fmt_node(&t.left), "16");
assert_eq!(
Tree::fmt_node(&Some(Box::new(btree!(btree!("foo"))))),
"{\"val\":\"foo\",\"left\":null,\"right\":null}"
);
assert_eq!(Tree::<i32>::fmt_node(&None), "_");
}

View file

@ -2,26 +2,27 @@
//! //!
//! # Examples //! # Examples
//! //!
//! ``` //! ```no_run
//! let mut t = Tree::new(1, None, None); //! //let mut t = Tree::new(1, None, None);
//! let t2 = Tree::new(2, Some(Box::new(t)), Some(Box::new(Tree::new(3, None, None)))); //! //let t2 = Tree::new(2, Some(Box::new(t)), Some(Box::new(Tree::new(3, None, None))));
//! let s = format!("{}", t2); //! //let s = format!("{}", t2);
//! //!
//! assert_eq!(s, "(2: (1: _|_)|(3: _|_))"); //! //assert_eq!(s, "(2: (1: _|_)|(3: _|_))");
//! t.left = Some(Box::new(Tree::new(4, None, None))); //! //t.left = Some(Box::new(Tree::new(4, None, None)));
//! assert_eq!(Tree::fmt_node(t.left), 4); //! //assert_eq!(Tree::fmt_node(t.left), 4);
//! assert_eq!(Tree::from_str(s), t2); //! //assert_eq!(Tree::from_str(s), t2);
//! ``` //! ```
//! //!
//! Additionally the `btree!` macro can be used to conveniently initialize trees: //! Additionally the `btree!` macro can be used to conveniently initialize trees:
//! //!
//! ```no_run
//! //# #[macro_use] extern crate tree;
//! //# fn main() {
//! //let t1 = btree!(1,btree!(2),btree!(3))
//! //assert_eq!(format!("{}", t1), "(1: (2: _|_)|(3: _|_)")
//! //# }
//! ``` //! ```
//! # #[macro_use] extern crate tree;
//! # fn main() {
//! let t1 = btree!(1,btree!(2),btree!(3))
//! assert_eq!(format!("{}", t1), "(1: (2: _|_)|(3: _|_)")
//! # }
//! ```
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -33,21 +34,21 @@ use std::str::FromStr;
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// let mut t = Tree::new(1, None, None); /// //let mut t = Tree::new(1, None, None);
/// let t2 = Tree::new(2, Some(Box::new(t)), Some(Box::new(Tree::new(3, None, None)))); /// //let t2 = Tree::new(2, Some(Box::new(t)), Some(Box::new(Tree::new(3, None, None))));
/// let s = format!("{}", t2); /// //let s = format!("{}", t2);
/// ///
/// assert_eq!(s, "(2: (1: _|_)|(3: _|_))"); /// //assert_eq!(s, "(2: (1: _|_)|(3: _|_))");
/// t.left = Some(Box::new(Tree::new(4, None, None))); /// //t.left = Some(Box::new(Tree::new(4, None, None)));
/// assert_eq!(Tree::fmt_node(t.left), 4); /// //assert_eq!(Tree::fmt_node(t.left), 4);
/// assert_eq!(Tree::from_str(s), t2); /// //assert_eq!(Tree::from_str(s), t2);
/// ``` /// ```
/// ///
/// Additionally the `btree!` macro can be used to conveniently initialize trees: /// Additionally the `btree!` macro can be used to conveniently initialize trees:
/// ///
/// ``` /// ```
/// let t1 = btree!(1,btree!(2),btree!(3)) /// //let t1 = btree!(1,btree!(2),btree!(3))
/// assert_eq!(format!("{}", t1), "(1: (2: _|_)|(3: _|_)") /// //assert_eq!(format!("{}", t1), "(1: (2: _|_)|(3: _|_)")
/// ``` /// ```
#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Debug)] #[derive(Default, Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Tree<T> { pub struct Tree<T> {
@ -60,18 +61,25 @@ pub struct Tree<T> {
/// value of the root node, and the other two being child nodes. The last two arguments are /// value of the root node, and the other two being child nodes. The last two arguments are
/// optional. /// optional.
/// ///
/// ``` /// ```no_run
/// // A tree with two child nodes. /// //#[macro_use]
/// let t = btree!(1, Some(btree!(2)), Some(btree!(3))); /// //extern crate gemla;
/// /// //
/// // A tree with only a left node. /// //use gemla::*;
/// let t_left = btree!(1, Some(btree!(2)),); /// //
/// /// //fn main() {
/// // A tree with only a right node. /// // // A tree with two child nodes.
/// let t_right = btree!(1, ,Some(btree!(3))); /// // let t = btree!(1, Some(btree!(2)), Some(btree!(3)));
/// /// //
/// // A tree with no children nodes. /// // // A tree with only a left node.
/// let t_single = btree!(1) /// // let t_left = btree!(1, Some(btree!(2)),);
/// //
/// // // A tree with only a right node.
/// // let t_right = btree!(1, ,Some(btree!(3)));
/// //
/// // // A tree with no children nodes.
/// // let t_single = btree!(1);
/// //}
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! btree { macro_rules! btree {
@ -127,3 +135,45 @@ where
serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s)) serde_json::from_str(s).map_err(|_| format!("Unable to parse string {}", s))
} }
} }
#[cfg(test)]
mod tests
{
use super::*;
#[test]
fn test_new() {
assert_eq!(
Tree::new(30, None, Some(Box::new(Tree::new(20, None, None)))),
Tree {
val: 30,
left: None,
right: Some(Box::new(Tree {
val: 20,
left: None,
right: None,
})),
}
);
}
#[test]
fn test_fmt() {
assert_eq!(
format!("{}", btree!("foo", btree!("bar"),),),
"{\"val\":\"foo\",\"left\":{\"val\":\"bar\",\"left\":null,\"right\":null},\"right\":null}"
);
}
#[test]
fn test_fmt_node() {
let t = btree!(17, btree!(16), btree!(12));
assert_eq!(Tree::fmt_node(&t.left), "16");
assert_eq!(
Tree::fmt_node(&Some(Box::new(btree!(btree!("foo"))))),
"{\"val\":\"foo\",\"left\":null,\"right\":null}"
);
assert_eq!(Tree::<i32>::fmt_node(&None), "_");
}
}