**Rationale:** Having two separate servers and communication methods resulted in additional maintenance & the need to convert often between backend & frontend data types. By moving the backend communication off of gRPC and to just use websockets it both gives more control & allows for simplification of the implementation. #8 **Changes:** - Replaces gRPC backend. - New implementation automatically handles reconnect logic - Implements an api layer - Migrates examples to the api layer - Implements a proc macro to make command handling easier - Implements unit tests for the api layer (90+% coverage) - Implements integration tests for the proc macro (90+% coverage) Reviewed-on: #10 Co-authored-by: Sergey Savelyev <sergeysav.nn@gmail.com> Co-committed-by: Sergey Savelyev <sergeysav.nn@gmail.com>
171 lines
4.8 KiB
Rust
171 lines
4.8 KiB
Rust
use api_core::command::{
|
|
Command, CommandHeader, CommandParameterDefinition, IntoCommandDefinition,
|
|
};
|
|
use api_core::data_type::DataType;
|
|
use api_core::data_value::DataValue;
|
|
use api_proc_macro::IntoCommandDefinition;
|
|
use std::collections::HashMap;
|
|
|
|
#[test]
|
|
fn test_enum_fails() {
|
|
let t = trybuild::TestCases::new();
|
|
t.compile_fail("tests/into_command_definition/enum_fails.rs");
|
|
}
|
|
|
|
#[test]
|
|
fn test_union_fails() {
|
|
let t = trybuild::TestCases::new();
|
|
t.compile_fail("tests/into_command_definition/union_fails.rs");
|
|
}
|
|
|
|
#[test]
|
|
fn test_unnamed_struct_fails() {
|
|
let t = trybuild::TestCases::new();
|
|
t.compile_fail("tests/into_command_definition/unnamed_struct_fails.rs");
|
|
}
|
|
|
|
#[test]
|
|
fn test_basic_command() {
|
|
#[derive(IntoCommandDefinition)]
|
|
struct TestStruct {
|
|
#[allow(unused)]
|
|
a: f32,
|
|
#[allow(unused)]
|
|
b: f64,
|
|
#[allow(unused)]
|
|
c: bool,
|
|
}
|
|
|
|
let command_definition = TestStruct::create("Test".to_string());
|
|
|
|
assert_eq!(command_definition.name, "Test");
|
|
assert_eq!(command_definition.parameters.capacity(), 3);
|
|
assert_eq!(
|
|
command_definition.parameters[0],
|
|
CommandParameterDefinition {
|
|
name: "a".to_string(),
|
|
data_type: DataType::Float32,
|
|
}
|
|
);
|
|
assert_eq!(
|
|
command_definition.parameters[1],
|
|
CommandParameterDefinition {
|
|
name: "b".to_string(),
|
|
data_type: DataType::Float64,
|
|
}
|
|
);
|
|
assert_eq!(
|
|
command_definition.parameters[2],
|
|
CommandParameterDefinition {
|
|
name: "c".to_string(),
|
|
data_type: DataType::Boolean,
|
|
}
|
|
);
|
|
|
|
let mut parameters = HashMap::new();
|
|
parameters.insert("a".to_string(), DataValue::Float32(1.0));
|
|
parameters.insert("b".to_string(), DataValue::Float64(2.0));
|
|
parameters.insert("c".to_string(), DataValue::Boolean(true));
|
|
let result = TestStruct::parse(Command {
|
|
header: CommandHeader {
|
|
timestamp: Default::default(),
|
|
},
|
|
parameters,
|
|
})
|
|
.unwrap();
|
|
assert_eq!(result.a, 1.0f32);
|
|
assert_eq!(result.b, 2.0f64);
|
|
assert_eq!(result.c, true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_generic_command() {
|
|
#[derive(IntoCommandDefinition)]
|
|
struct TestStruct<T> {
|
|
#[allow(unused)]
|
|
a: T,
|
|
}
|
|
|
|
let command_definition = TestStruct::<f32>::create("Test".to_string());
|
|
assert_eq!(command_definition.name, "Test");
|
|
assert_eq!(command_definition.parameters.capacity(), 1);
|
|
assert_eq!(
|
|
command_definition.parameters[0],
|
|
CommandParameterDefinition {
|
|
name: "a".to_string(),
|
|
data_type: DataType::Float32,
|
|
}
|
|
);
|
|
let mut parameters = HashMap::new();
|
|
parameters.insert("a".to_string(), DataValue::Float32(1.0));
|
|
let result = TestStruct::<f32>::parse(Command {
|
|
header: CommandHeader {
|
|
timestamp: Default::default(),
|
|
},
|
|
parameters,
|
|
})
|
|
.unwrap();
|
|
assert_eq!(result.a, 1.0f32);
|
|
|
|
let command_definition = TestStruct::<f64>::create("Test2".to_string());
|
|
assert_eq!(command_definition.name, "Test2");
|
|
assert_eq!(command_definition.parameters.capacity(), 1);
|
|
assert_eq!(
|
|
command_definition.parameters[0],
|
|
CommandParameterDefinition {
|
|
name: "a".to_string(),
|
|
data_type: DataType::Float64,
|
|
}
|
|
);
|
|
let mut parameters = HashMap::new();
|
|
parameters.insert("a".to_string(), DataValue::Float64(2.0));
|
|
let result = TestStruct::<f64>::parse(Command {
|
|
header: CommandHeader {
|
|
timestamp: Default::default(),
|
|
},
|
|
parameters,
|
|
})
|
|
.unwrap();
|
|
assert_eq!(result.a, 2.0f64);
|
|
|
|
let command_definition = TestStruct::<bool>::create("Test3".to_string());
|
|
assert_eq!(command_definition.name, "Test3");
|
|
assert_eq!(command_definition.parameters.capacity(), 1);
|
|
assert_eq!(
|
|
command_definition.parameters[0],
|
|
CommandParameterDefinition {
|
|
name: "a".to_string(),
|
|
data_type: DataType::Boolean,
|
|
}
|
|
);
|
|
let mut parameters = HashMap::new();
|
|
parameters.insert("a".to_string(), DataValue::Boolean(true));
|
|
let result = TestStruct::<bool>::parse(Command {
|
|
header: CommandHeader {
|
|
timestamp: Default::default(),
|
|
},
|
|
parameters,
|
|
})
|
|
.unwrap();
|
|
assert_eq!(result.a, true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_unit_command() {
|
|
#[derive(IntoCommandDefinition)]
|
|
struct TestStruct;
|
|
|
|
let command_definition = TestStruct::create("Test".to_string());
|
|
|
|
assert_eq!(command_definition.name, "Test");
|
|
assert_eq!(command_definition.parameters.capacity(), 0);
|
|
|
|
TestStruct::parse(Command {
|
|
header: CommandHeader {
|
|
timestamp: Default::default(),
|
|
},
|
|
parameters: HashMap::new(),
|
|
})
|
|
.unwrap();
|
|
}
|