mod common;
use std::sync::{Arc, Mutex};
use futures::executor::block_on;
use tango::dev::DevSource;
use tango::value::{Scalar, Value};
use tango::server::ClassDef;
use tango::error::Result;
use tango::testing::TestContext;
use common::{local_and_wire, test_context, test_server, exported, unexported};
fn admin_command(ctx: &TestContext, name: &str, input: Value) -> Result<Value> {
let admin = ctx.backend_dev("dserver/Test/test").unwrap();
block_on(admin.command(Scalar::from(name), input, DevSource::Dev))
}
fn strings(value: Value) -> Vec<String> {
Vec::<String>::try_from(value).unwrap()
}
fn class(name: &'static str, deleted: &Arc<Mutex<Vec<String>>>) -> ClassDef<()> {
let deleted = Arc::clone(deleted);
ClassDef::new_default(name)
.delete(move |(), dev| deleted.lock().unwrap().push(dev.name().to_string()))
}
local_and_wire! {
fn queries(local: bool) {
let deleted = Arc::new(Mutex::new(Vec::new()));
let ctx = test_context(&[("b/dev/2", "B"), ("a/dev/1", "A"), ("b/dev/1", "B")]);
let _server = ctx.serve(test_server(local)
.class(class("A", &deleted)).unwrap()
.class(class("B", &deleted)).unwrap())
.unwrap();
assert_eq!(strings(admin_command(&ctx, "QueryClass", Value::Void).unwrap()), ["A", "B"]);
assert_eq!(strings(admin_command(&ctx, "QueryDevice", Value::Void).unwrap()),
["A::a/dev/1", "B::b/dev/1", "B::b/dev/2"]);
}
}
local_and_wire! {
fn dev_restart(local: bool) {
let deleted = Arc::new(Mutex::new(Vec::new()));
let ctx = test_context(&[("a/dev/1", "A")]);
let _server = ctx.serve(test_server(local)
.class(class("A", &deleted)).unwrap())
.unwrap();
admin_command(&ctx, "DevRestart", Value::from("tango://db:10000/A/dev/1#dbase=yes")).unwrap();
assert_eq!(*deleted.lock().unwrap(), ["a/dev/1"]);
assert_eq!(unexported(ctx.test_database()), ["a/dev/1"]);
assert_eq!(exported(ctx.test_database()), ["dserver/Test/test", "a/dev/1", "a/dev/1"]);
assert!(admin_command(&ctx, "DevRestart", Value::from("a/dev/2")).is_err());
assert!(admin_command(&ctx, "DevRestart", Value::from("dserver/Test/test")).is_err());
}
}
local_and_wire! {
fn restart_server(local: bool) {
let deleted = Arc::new(Mutex::new(Vec::new()));
let ctx = test_context(&[("a/dev/1", "A"), ("a/dev/2", "A")]);
let server = ctx.serve(test_server(local)
.class(class("A", &deleted)).unwrap())
.unwrap();
let database = ctx.database();
database.delete_dev("a/dev/1").unwrap();
database.add_dev("a/dev/3", "A", "Test/test").unwrap();
admin_command(&ctx, "RestartServer", Value::Void).unwrap();
assert_eq!(exported(ctx.test_database()).len(), 5);
let mut deleted = deleted.lock().unwrap().clone();
deleted.sort();
assert_eq!(deleted, ["a/dev/1", "a/dev/2"]);
assert_eq!(server.devs().iter().map(|dev| dev.name().to_string()).collect::<Vec<_>>(),
["dserver/Test/test", "a/dev/2", "a/dev/3"]);
}
}
local_and_wire! {
#[cfg_attr(miri, ignore = "Miri cannot run ZMQ, a C library")]
fn zmq_event_subscription_change(local: bool) {
let deleted = Arc::new(Mutex::new(Vec::new()));
let ctx = test_context(&[("a/dev/1", "A")]);
let _server = ctx.serve(test_server(local)
.class(class("A", &deleted)).unwrap())
.unwrap();
let input = Value::from(["A/dev/1", "Attr", "unsubscribe", "change", "6"]);
let Value::LongString(numbers, strings) = admin_command(&ctx, "ZmqEventSubscriptionChange", input).unwrap() else {
panic!("Not a DevVarLongStringArray");
};
let numbers = numbers.iter().collect::<Vec<_>>();
let strings = strings.iter().collect::<Vec<_>>();
assert_eq!(numbers[1], 6);
assert!(strings[0].starts_with("tcp://"));
assert!(strings[1].starts_with("tcp://"));
assert!(strings[strings.len() - 2].ends_with("/a/dev/1/attr.idl5_change"));
assert!(strings[strings.len() - 1].ends_with("/dserver/test/test"));
let input = Value::from(["a/dev/1", "attr", "idl5_change"]);
assert!(admin_command(&ctx, "EventConfirmSubscription", input).is_err());
let input = Value::from(["a/dev/1", "attr", "subscribe", "change", "6"]);
assert!(admin_command(&ctx, "ZmqEventSubscriptionChange", input).is_err());
let input = Value::from(["a/dev/2", "attr", "unsubscribe", "change", "6"]);
assert!(admin_command(&ctx, "ZmqEventSubscriptionChange", input).is_err());
}
}