Skip to content

Commit

Permalink
Add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mkermani144 committed Apr 17, 2022
1 parent 81f370d commit d486020
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 39 deletions.
48 changes: 13 additions & 35 deletions src/config.rs → src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use rand::prelude::*;
use rand::distributions::WeightedIndex;
use confy::{load, store};

mod utils;

mod default_chance {
pub const AJI: f64 = 50.0;
pub const KI: f64 = 5.0;
Expand Down Expand Up @@ -40,14 +42,9 @@ fn write_config(config: AkcConfig) {
store(CONFIG_NAME, &config).unwrap_or_else(|_| panic!("Failed to write config file"));
}

fn is_name_duplicate(name: &str) -> bool {
let config = read_config();
config.friends.iter().any(| friend_info | friend_info.name == name)
}

fn add_friend(friend_info: FriendInfo) {
let mut config = read_config();
let is_duplicate = is_name_duplicate(&friend_info.name);
let is_duplicate = utils::is_name_duplicate(&config, &friend_info.name);

if is_duplicate {
println!("Name \"{}\" already exists, please use a different name", friend_info.name)
Expand Down Expand Up @@ -83,17 +80,16 @@ pub fn add_chi(name: String) {

pub fn suggest() {
let config = read_config();
let filtered_config: Vec<&FriendInfo> = config.friends.iter().filter(| friend_info | friend_info.chance > default_reduction::TEXT ).collect();
let filtered_config = utils::filter_config_by_enough_chance(&config);
let mut rng = thread_rng();

let weighted_dist = WeightedIndex::new(filtered_config.iter().map(| friend_info | friend_info.chance)).expect("Failed to suggest a friend");
println!("Suggested friend: {}", config.friends[weighted_dist.sample(&mut rng)].name);
}

fn add_memory(reduction: f64, names: Vec<String>) {
fn add_memory(reduction: f64, names: &[String]) {
let mut config = read_config();
let all_names = config.friends.iter().map(| friend_info | friend_info.name.clone()).collect::<Vec<String>>();
let unknown_names = names.iter().filter(| name | !all_names.iter().any(| inner_name | inner_name == *name )).collect::<Vec<&String>>();
let unknown_names = utils::get_unknown_names(&config, names);

if !unknown_names.is_empty() {
let unknown_names_string = unknown_names.iter()
Expand All @@ -103,46 +99,28 @@ fn add_memory(reduction: f64, names: Vec<String>) {
println!("The following names are not added yet: {}", unknown_names_string);
} else {
let total_reduction = reduction * names.len() as f64;
let current_total_chance = config.friends.iter()
.filter(| friend_info | !names.contains(&friend_info.name))
.map(| friend_info | friend_info.chance)
.sum::<f64>();
let current_total_chance = utils::get_config_total_chance(&config, names);
let unit_added_chance = total_reduction / current_total_chance;

config.friends.iter_mut()
.filter(| friend_info | !names.contains(&friend_info.name))
.for_each(| friend_info | {
let level_chance = match friend_info.level.as_str() {
"aji" => default_chance::AJI,
"ki" => default_chance::KI,
"chi" => default_chance::CHI,
_ => 0.0
};
friend_info.chance += level_chance * unit_added_chance;
});

config.friends.iter_mut()
.filter(| friend_info | names.contains(&friend_info.name))
.for_each(| friend_info | {
friend_info.chance -= reduction;
});
utils::increase_chances_by_unit(&mut config, unit_added_chance, names);
utils::decrease_chances_by_reduction(&mut config, reduction, names);

write_config(config)
}
}

pub fn add_hangout(names: Vec<String>) {
pub fn add_hangout(names: &[String]) {
add_memory(default_reduction::HANGOUT, names)
}

pub fn add_video_call(names: Vec<String>) {
pub fn add_video_call(names: &[String]) {
add_memory(default_reduction::VIDEO_CALL, names)
}

pub fn add_call(names: Vec<String>) {
pub fn add_call(names: &[String]) {
add_memory(default_reduction::CALL, names)
}

pub fn add_text(names: Vec<String>) {
pub fn add_text(names: &[String]) {
add_memory(default_reduction::TEXT, names)
}
207 changes: 207 additions & 0 deletions src/config/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use super::{AkcConfig, FriendInfo, default_chance, default_reduction};

pub fn is_name_duplicate(config: &AkcConfig, name: &str) -> bool {
config.friends.iter().any(| friend_info | friend_info.name == name)
}

pub fn filter_config_by_enough_chance(config: &AkcConfig) -> Vec<&FriendInfo> {
config.friends.iter().filter(| friend_info | friend_info.chance >= default_reduction::TEXT).collect()
}

pub fn get_unknown_names<'a>(config: &AkcConfig, names: &'a[String]) -> Vec<&'a String> {
let all_names = config.friends.iter().map(| friend_info | friend_info.name.clone()).collect::<Vec<String>>();
names.iter().filter(| name | !all_names.iter().any(| inner_name | inner_name == *name )).collect::<Vec<&String>>()
}

pub fn get_config_total_chance(config: &AkcConfig, excluded_names: &[String]) -> f64 {
config.friends.iter()
.filter(| friend_info | !excluded_names.contains(&friend_info.name))
.map(| friend_info | friend_info.chance)
.sum::<f64>()
}

pub fn increase_chances_by_unit(config: &mut AkcConfig, unit_added_chance: f64, excluded_names: &[String]) {
config.friends.iter_mut()
.filter(| friend_info | !excluded_names.contains(&friend_info.name))
.for_each(| friend_info | {
let level_chance = match friend_info.level.as_str() {
"aji" => default_chance::AJI,
"ki" => default_chance::KI,
"chi" => default_chance::CHI,
_ => 0.0
};
friend_info.chance += level_chance * unit_added_chance;
})
}

pub fn decrease_chances_by_reduction(config: &mut AkcConfig, reduction: f64, names: &[String]) {
config.friends.iter_mut()
.filter(| friend_info | names.contains(&friend_info.name))
.for_each(| friend_info | {
friend_info.chance -= reduction;
})
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_is_name_duplicate() {
let config = AkcConfig {
friends: vec![
FriendInfo {
name: "John".to_owned(),
chance: default_chance::AJI,
level: "aji".to_owned()
},
FriendInfo {
name: "Doe".to_owned(),
chance: default_chance::KI,
level: "ki".to_owned()
}
]
};

assert!(is_name_duplicate(&config, "John"));
assert!(is_name_duplicate(&config, "Doe"));
assert!(!is_name_duplicate(&config, "John Doe"));
}

#[test]
fn test_filter_config_by_enough_chance() {
let config = AkcConfig {
friends: vec![
FriendInfo {
name: "John".to_owned(),
chance: default_chance::AJI,
level: "aji".to_owned()
},
FriendInfo {
name: "Doe".to_owned(),
chance: default_reduction::TEXT,
level: "ki".to_owned()
},
FriendInfo {
name: "Jane".to_owned(),
chance: 0.0,
level: "chi".to_owned()
}
]
};

let filtered_config = filter_config_by_enough_chance(&config);
assert_eq!(filtered_config.len(), 2);
}

#[test]
fn test_get_unknown_names() {
let config = AkcConfig {
friends: vec![
FriendInfo {
name: "John".to_owned(),
chance: default_chance::AJI,
level: "aji".to_owned()
},
FriendInfo {
name: "Doe".to_owned(),
chance: default_chance::KI,
level: "ki".to_owned()
}
]
};

let names = vec![
"John".to_owned(),
"Doe".to_owned(),
"Jane".to_owned()
];
let unknown_names = get_unknown_names(&config, &names);

assert_eq!(unknown_names.len(), 1);
assert_eq!(unknown_names[0], "Jane");
}

#[test]
fn test_get_config_total_chance() {
let config = AkcConfig {
friends: vec![
FriendInfo {
name: "John".to_owned(),
chance: default_chance::AJI,
level: "aji".to_owned()
},
FriendInfo {
name: "Doe".to_owned(),
chance: default_chance::KI,
level: "ki".to_owned()
},
FriendInfo {
name: "Doe2".to_owned(),
chance: default_chance::CHI,
level: "chi".to_owned()
},
]
};

let total_chance = get_config_total_chance(&config, &["Doe".to_owned()]);
assert_eq!(total_chance, default_chance::AJI + default_chance::CHI);
}

#[test]
fn test_increase_chances_by_unit() {
let mut config = AkcConfig {
friends: vec![
FriendInfo {
name: "John".to_owned(),
chance: default_chance::AJI,
level: "aji".to_owned()
},
FriendInfo {
name: "Doe".to_owned(),
chance: default_chance::KI,
level: "ki".to_owned()
},
FriendInfo {
name: "Doe2".to_owned(),
chance: default_chance::CHI,
level: "chi".to_owned()
},
]
};

increase_chances_by_unit(&mut config, 0.1, &["Doe".to_owned()]);
// println("{}", config.friends[0].chance);
assert_eq!(config.friends[0].chance, default_chance::AJI + 0.1 * default_chance::AJI);
assert_eq!(config.friends[1].chance, default_chance::KI);
assert_eq!(config.friends[2].chance, default_chance::CHI + 0.1 * default_chance::CHI);
}

#[test]
fn test_decrease_chances_by_reduction() {
let mut config = AkcConfig {
friends: vec![
FriendInfo {
name: "John".to_owned(),
chance: default_chance::AJI,
level: "aji".to_owned()
},
FriendInfo {
name: "Doe".to_owned(),
chance: default_chance::KI,
level: "ki".to_owned()
},
FriendInfo {
name: "Doe2".to_owned(),
chance: default_chance::CHI,
level: "chi".to_owned()
},
]
};

decrease_chances_by_reduction(&mut config, 1.0, &["Doe".to_owned()]);
assert_eq!(config.friends[0].chance, default_chance::AJI);
assert_eq!(config.friends[1].chance, default_chance::KI - 1.0);
assert_eq!(config.friends[2].chance, default_chance::CHI);
}
}
8 changes: 4 additions & 4 deletions src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ pub struct Memory {

pub fn handle(args: Memory) {
match args.command {
MemoryCommand::Hangout(names_wrapper) => config::add_hangout(names_wrapper.names),
MemoryCommand::VideoCall(names_wrapper) => config::add_video_call(names_wrapper.names),
MemoryCommand::Call(names_wrapper) => config::add_call(names_wrapper.names),
MemoryCommand::Text(names_wrapper) => config::add_text(names_wrapper.names),
MemoryCommand::Hangout(names_wrapper) => config::add_hangout(&names_wrapper.names),
MemoryCommand::VideoCall(names_wrapper) => config::add_video_call(&names_wrapper.names),
MemoryCommand::Call(names_wrapper) => config::add_call(&names_wrapper.names),
MemoryCommand::Text(names_wrapper) => config::add_text(&names_wrapper.names),
}
}

0 comments on commit d486020

Please sign in to comment.