settings tests

macos-click-through
AnhQuan Nguyen 4 years ago
parent 2c196f84f1
commit 2428225fbe

96
Cargo.lock generated

@ -538,6 +538,12 @@ dependencies = [
"syn 1.0.17",
]
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "dirs"
version = "2.0.2"
@ -575,6 +581,12 @@ dependencies = [
"libloading",
]
[[package]]
name = "downcast"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]]
name = "downcast-rs"
version = "1.1.1"
@ -676,6 +688,15 @@ dependencies = [
"regex",
]
[[package]]
name = "float-cmp"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"
dependencies = [
"num-traits",
]
[[package]]
name = "float-ord"
version = "0.2.0"
@ -728,6 +749,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "fragile"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
[[package]]
name = "freetype"
version = "0.4.1"
@ -1229,6 +1256,33 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "mockall"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c9eefc7768ee7a28a09d64e40203d57ad20af8525b7428d5f2f55d8c621984"
dependencies = [
"cfg-if",
"downcast",
"fragile",
"lazy_static",
"mockall_derive",
"predicates",
"predicates-tree",
]
[[package]]
name = "mockall_derive"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447326d4e6d99ea272b6e5599cbbfc1e3407c23a856ccf1eb9427ad73267376f"
dependencies = [
"cfg-if",
"proc-macro2 1.0.10",
"quote 1.0.3",
"syn 1.0.17",
]
[[package]]
name = "neovide"
version = "0.5.0"
@ -1244,6 +1298,7 @@ dependencies = [
"lazy_static",
"log",
"lru",
"mockall",
"nvim-rs",
"parking_lot",
"rmpv",
@ -1291,6 +1346,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-derive"
version = "0.2.5"
@ -1519,6 +1580,35 @@ dependencies = [
"inflate",
]
[[package]]
name = "predicates"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "347a1b6f0b21e636bc9872fb60b83b8e185f6f5516298b8238699f7f9a531030"
dependencies = [
"difference",
"float-cmp",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178"
[[package]]
name = "predicates-tree"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124"
dependencies = [
"predicates-core",
"treeline",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.15"
@ -2172,6 +2262,12 @@ dependencies = [
"serde",
]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "unicode-normalization"
version = "0.1.12"

@ -31,6 +31,9 @@ anyhow = "1.0.26"
parking_lot="0.10.0"
cfg-if = "0.1.10"
[dev-dependencies]
mockall = "0.7.0"
[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"

@ -72,7 +72,7 @@ fn build_nvim_cmd() -> Command {
}
}
fn create_nvim_command() -> Command {
pub fn create_nvim_command() -> Command {
let mut cmd = build_nvim_cmd();
cmd.arg("--embed")

@ -0,0 +1,182 @@
use super::Value;
use log::error;
// Trait to allow for conversion from rmpv::Value to any other data type.
// Note: Feel free to implement this trait for custom types in each subsystem.
// The reverse conversion (MyType->Value) can be performed by implementing `From<MyType> for Value`
pub trait FromValue {
fn from_value(&mut self, value: Value);
}
// FromValue implementations for most typical types
impl FromValue for f32 {
fn from_value(&mut self, value: Value) {
if value.is_f64() {
*self = value.as_f64().unwrap() as f32;
} else if value.is_i64() {
*self = value.as_i64().unwrap() as f32;
} else if value.is_u64() {
*self = value.as_u64().unwrap() as f32;
} else {
error!("Setting expected an f32, but received {:?}", value);
}
}
}
impl FromValue for u64 {
fn from_value(&mut self, value: Value) {
if value.is_u64() {
*self = value.as_u64().unwrap();
} else {
error!("Setting expected a u64, but received {:?}", value);
}
}
}
impl FromValue for u32 {
fn from_value(&mut self, value: Value) {
if value.is_u64() {
*self = value.as_u64().unwrap() as u32;
} else {
error!("Setting expected a u32, but received {:?}", value);
}
}
}
impl FromValue for i32 {
fn from_value(&mut self, value: Value) {
if value.is_i64() {
*self = value.as_i64().unwrap() as i32;
} else {
error!("Setting expected an i32, but received {:?}", value);
}
}
}
impl FromValue for String {
fn from_value(&mut self, value: Value) {
if value.is_str() {
*self = String::from(value.as_str().unwrap());
} else {
error!("Setting expected a string, but received {:?}", value);
}
}
}
impl FromValue for bool {
fn from_value(&mut self, value: Value) {
if value.is_bool() {
*self = value.as_bool().unwrap();
} else if value.is_u64() {
*self = value.as_u64().unwrap() != 0;
} else {
error!("Setting expected a string, but received {:?}", value);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_value_f32() {
let mut v0: f32 = 0.0;
let v1 = Value::from(1.0);
let v2 = Value::from(-1);
let v3 = Value::from(u64::MAX);
let v1p = 1.0;
let v2p = -1.0;
let v3p = u64::MAX as f32;
v0.from_value(v1);
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
v0.from_value(v2);
assert_eq!(v0, v2p, "v0 should equal {} but is actually {}", v2p, v0);
v0.from_value(v3);
assert_eq!(v0, v3p, "v0 should equal {} but is actually {}", v3p, v0);
// This is a noop and prints an error
v0.from_value(Value::from("asd"));
assert_eq!(v0, v3p, "v0 should equal {} but is actually {}", v3p, v0);
}
#[test]
fn test_from_value_u64() {
let mut v0: u64 = 0;
let v1 = Value::from(u64::MAX);
let v1p = u64::MAX;
v0.from_value(v1);
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
// This is a noop and prints an error
v0.from_value(Value::from(-1));
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
}
#[test]
fn test_from_value_u32() {
let mut v0: u32 = 0;
let v1 = Value::from(u64::MAX);
let v1p = u64::MAX as u32;
v0.from_value(v1);
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
// This is a noop and prints an error
v0.from_value(Value::from(-1));
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
}
#[test]
fn test_from_value_i32() {
let mut v0: i32 = 0;
let v1 = Value::from(i64::MAX);
let v1p = i64::MAX as i32;
v0.from_value(v1);
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
// This is a noop and prints an error
v0.from_value(Value::from(-1));
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
}
#[test]
fn test_from_value_string() {
let mut v0: String = "foo".to_string();
let v1 = Value::from("bar");
let v1p = "bar";
v0.from_value(v1);
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
// This is a noop and prints an error
v0.from_value(Value::from(-1));
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
}
#[test]
fn test_from_value_bool() {
let mut v0: bool = false;
let v1 = Value::from(true);
let v1p = true;
let v2 = Value::from(0);
let v2p = false;
let v3 = Value::from(1);
let v3p = true;
v0.from_value(v1);
assert_eq!(v0, v1p, "v0 should equal {} but is actually {}", v1p, v0);
v0.from_value(v2);
assert_eq!(v0, v2p, "v0 should equal {} but is actually {}", v2p, v0);
v0.from_value(v3);
assert_eq!(v0, v3p, "v0 should equal {} but is actually {}", v3p, v0);
// This is a noop and prints an error
v0.from_value(Value::from(-1));
assert_eq!(v0, v3p, "v0 should equal {} but is actually {}", v3p, v0);
}
}

@ -2,12 +2,16 @@ use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::convert::TryInto;
#[cfg(not(test))]
use flexi_logger::{Cleanup, Criterion, Duplicate, Logger, Naming};
use log::{error, warn};
use log::warn;
use nvim_rs::compat::tokio::Compat;
use nvim_rs::Neovim;
use parking_lot::RwLock;
pub use rmpv::Value;
mod from_value;
pub use from_value::FromValue;
use tokio::process::ChildStdin;
use crate::error_handling::ResultPanicExplanation;
@ -16,79 +20,6 @@ lazy_static! {
pub static ref SETTINGS: Settings = Settings::new();
}
// Trait to allow for conversion from rmpv::Value to any other data type.
// Note: Feel free to implement this trait for custom types in each subsystem.
// The reverse conversion (MyType->Value) can be performed by implementing `From<MyType> for Value`
pub trait FromValue {
fn from_value(&mut self, value: Value);
}
// FromValue implementations for most typical types
impl FromValue for f32 {
fn from_value(&mut self, value: Value) {
if value.is_f64() {
*self = value.as_f64().unwrap() as f32;
} else if value.is_i64() {
*self = value.as_i64().unwrap() as f32;
} else if value.is_u64() {
*self = value.as_u64().unwrap() as f32;
} else {
error!("Setting expected an f32, but received {:?}", value);
}
}
}
impl FromValue for u64 {
fn from_value(&mut self, value: Value) {
if value.is_u64() {
*self = value.as_u64().unwrap();
} else {
error!("Setting expected a u64, but received {:?}", value);
}
}
}
impl FromValue for u32 {
fn from_value(&mut self, value: Value) {
if value.is_u64() {
*self = value.as_u64().unwrap() as u32;
} else {
error!("Setting expected a u32, but received {:?}", value);
}
}
}
impl FromValue for i32 {
fn from_value(&mut self, value: Value) {
if value.is_i64() {
*self = value.as_i64().unwrap() as i32;
} else {
error!("Setting expected an i32, but received {:?}", value);
}
}
}
impl FromValue for String {
fn from_value(&mut self, value: Value) {
if value.is_str() {
*self = String::from(value.as_str().unwrap());
} else {
error!("Setting expected a string, but received {:?}", value);
}
}
}
impl FromValue for bool {
fn from_value(&mut self, value: Value) {
if value.is_bool() {
*self = value.as_bool().unwrap();
} else if value.is_u64() {
*self = value.as_u64().unwrap() != 0;
} else {
error!("Setting expected a string, but received {:?}", value);
}
}
}
// Macro to register settings changed handlers.
// Note: Invocations to this macro must happen before the call to Settings::read_initial_values.
@ -131,7 +62,7 @@ pub struct Settings {
}
impl Settings {
fn new() -> Settings {
fn new() -> Self {
let mut log_to_file = false;
let neovim_arguments = std::env::args()
.filter(|arg| {
@ -148,6 +79,19 @@ impl Settings {
})
.collect::<Vec<String>>();
#[cfg(not(test))]
Settings::init_logger(log_to_file);
Self {
neovim_arguments,
settings: RwLock::new(HashMap::new()),
listeners: RwLock::new(HashMap::new()),
readers: RwLock::new(HashMap::new()),
}
}
#[cfg(not(test))]
fn init_logger(log_to_file: bool) {
if log_to_file {
Logger::with_env_or_str("neovide")
.duplicate_to_stderr(Duplicate::Error)
@ -164,13 +108,6 @@ impl Settings {
.start()
.expect("Could not start logger");
}
Settings {
neovim_arguments,
settings: RwLock::new(HashMap::new()),
listeners: RwLock::new(HashMap::new()),
readers: RwLock::new(HashMap::new()),
}
}
pub fn set_setting_handlers(
@ -190,7 +127,12 @@ impl Settings {
pub fn set<T: Clone + Send + Sync + 'static>(&self, t: &T) {
let type_id: TypeId = TypeId::of::<T>();
let t: T = (*t).clone();
self.settings.write().insert(type_id, Box::new(t));
unsafe {
self.settings.force_unlock_write();
}
let mut write_lock = self.settings.write();
write_lock.insert(type_id, Box::new(t));
}
pub fn get<'a, T: Clone + Send + Sync + 'static>(&'a self) -> T {
@ -255,3 +197,156 @@ impl Settings {
self.listeners.read().get(&name).unwrap()(value);
}
}
#[cfg(test)]
mod tests {
use super::*;
use nvim_rs::{create::tokio as create};
use crate::bridge::create_nvim_command;
use async_trait::async_trait;
use nvim_rs::{compat::tokio::Compat, Handler, Neovim};
#[derive(Clone)]
pub struct NeovimHandler();
#[async_trait]
impl Handler for NeovimHandler {
type Writer = Compat<ChildStdin>;
async fn handle_notify(
&self,
_event_name: String,
_arguments: Vec<Value>,
_neovim: Neovim<Compat<ChildStdin>>,
) {
}
}
use tokio;
#[test]
fn test_set_setting_handlers() {
let settings = Settings::new();
let property_name = "foo";
fn noop_update(_v: Value) {
}
fn noop_read() -> Value {
Value::Nil
}
settings.set_setting_handlers(property_name, noop_update, noop_read);
let listeners = settings.listeners.read();
let readers = settings.readers.read();
let listener = listeners.get(property_name).unwrap();
let reader = readers.get(property_name).unwrap();
assert_eq!(&(noop_update as UpdateHandlerFunc), listener);
assert_eq!(&(noop_read as ReaderFunc), reader);
}
#[test]
fn test_set() {
let settings = Settings::new();
let v1: u32 = 1;
let v2: f32 = 1.0;
let vt1 = TypeId::of::<u32>();
let vt2 = TypeId::of::<f32>();
let v3: u32 = 2;
settings.set(&v1);
let values = settings.settings.read();
let r1 = values.get(&vt1).unwrap().downcast_ref::<u32>().unwrap();
assert_eq!(v1, *r1);
settings.set(&v2);
settings.set(&v3);
let r2 = values.get(&vt1).unwrap().downcast_ref::<u32>().unwrap();
let r3 = values.get(&vt2).unwrap().downcast_ref::<f32>().unwrap();
assert_eq!(v3, *r2);
assert_eq!(v2, *r3);
}
#[test]
fn test_get() {
let settings = Settings::new();
let v1: u32 = 1;
let v2: f32 = 1.0;
let vt1 = TypeId::of::<u32>();
let vt2 = TypeId::of::<f32>();
let mut values = settings.settings.write();
values.insert(vt1, Box::new(v1.clone()));
values.insert(vt2, Box::new(v2.clone()));
unsafe {
settings.settings.force_unlock_write();
}
let r1 = settings.get::<u32>();
let r2 = settings.get::<f32>();
assert_eq!(v1, r1);
assert_eq!(v2, r2);
}
#[tokio::test]
async fn test_read_initial_values() {
let settings = Settings::new();
let v1: String = "foo".to_string();
let v2: String = "bar".to_string();
let v3: String = "baz".to_string();
let v4: String = format!("neovide_{}", v1);
let v5: String = format!("neovide_{}", v2);
let (nvim, _, _) =
create::new_child_cmd(&mut create_nvim_command(), NeovimHandler())
.await
.unwrap_or_explained_panic("Could not locate or start the neovim process");
nvim.set_var(&v4, Value::from(v2.clone())).await.ok();
fn noop_update(_v: Value) {
}
fn noop_read() -> Value {
Value::from("baz".to_string())
}
let mut listeners = settings.listeners.write();
listeners.insert(v1.clone(), noop_update);
listeners.insert(v2.clone(), noop_update);
unsafe {
settings.listeners.force_unlock_write();
}
let mut readers = settings.readers.write();
readers.insert(v1.clone(), noop_read);
readers.insert(v2.clone(), noop_read);
unsafe {
settings.readers.force_unlock_write();
}
settings.read_initial_values(&nvim).await;
let rt1 = nvim.get_var(&v4).await.unwrap();
let rt2 = nvim.get_var(&v5).await.unwrap();
let r1 = rt1.as_str().unwrap();
let r2 = rt2.as_str().unwrap();
assert_eq!(r1, v2);
assert_eq!(r2, v3);
}
}
Loading…
Cancel
Save