Skip to main content

tango/testing/
database.rs

1//! The Tango database of a test, which answers the commands of the database device the
2//! way `Databaseds` does, see [`super::TestContext`]. It is held in memory and made to tell
3//! what it was asked and to fail on demand, which is what a test needs, and not what a
4//! database meant to be used for real does.
5//!
6//! Its content is only ever altered through those very commands, that is through the
7//! methods of a [`crate::database::DatabaseProxy`], so that what a test sets up is what a
8//! real database would have been told.
9
10use std::collections::HashMap;
11use std::sync::{Arc, Mutex};
12use std::time::SystemTime;
13use std::fmt;
14
15use futures::future::{ready, BoxFuture};
16use futures::stream::BoxStream;
17
18use crate::dev::{AttrConfig, AttrInfo, AttrRead, AttrValueEvent, CommandInfo, DevInfo, DevSource, DevState, Named, NamedResult};
19use crate::error::{Error, ErrorKind, Result, reason};
20use crate::value::{Scalar, Spectrum, Value};
21use crate::backend::BackendDev;
22
23
24/// The name the database device answers with, the one of a real database.
25pub const NAME: &str = "sys/database/2";
26
27/// The class of the admin device of every server.
28const ADMIN_CLASS: &str = "DServer";
29
30// =========================== //
31//          Database           //
32// =========================== //
33
34/// The database of a test, held in memory, every name being looked up regardless of its
35/// case, as `Databaseds` does.
36///
37/// Besides answering the commands of the database device, it records them, see
38/// [`Self::log`], and can be made to fail some of them, see [`Self::add_fail`], which
39/// is what testing how a server or a client copes with its database takes.
40#[derive(Default)]
41pub struct TestDatabase {
42    state: Mutex<State>,
43    /// Every command answered, in order, with its argument.
44    log: Mutex<Vec<(String, Value)>>,
45    /// The error each failing command fails with, by name.
46    failing: Mutex<HashMap<String, FailWith>>,
47}
48
49/// What makes the error a failing command fails with, once for every time it is sent.
50type FailWith = Box<dyn Fn() -> Error + Send + Sync>;
51
52impl fmt::Debug for TestDatabase {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        f.debug_struct("TestDatabase")
55            .field("state", &self.state)
56            .field("log", &self.log)
57            .field("failing", &self.failing.lock().unwrap().keys())
58            .finish()
59    }
60}
61
62impl TestDatabase {
63
64    /// Every command answered so far, in order, with its argument, those that failed
65    /// included.
66    pub fn log(&self) -> Vec<(String, Value)> {
67        self.log.lock().unwrap().clone()
68    }
69
70    /// Forget every command answered so far, see [`Self::log`].
71    pub fn clear_log(&self) {
72        self.log.lock().unwrap().clear();
73    }
74
75    /// Fail every command of that name from now on, with the error that makes, as a broken
76    /// database does, until [`Self::remove_fail`].
77    pub fn add_fail(&self, name: &str, err: impl Fn() -> Error + Send + Sync + 'static) {
78        self.failing.lock().unwrap().insert(name.to_string(), Box::new(err));
79    }
80
81    /// Answer the commands of that name again, see [`Self::add_fail`].
82    pub fn remove_fail(&self, name: &str) {
83        self.failing.lock().unwrap().remove(name);
84    }
85
86    /// Answer a command of the database device.
87    pub fn command(&self, name: &str, input: Value) -> Result<Value> {
88
89        self.log.lock().unwrap().push((name.to_string(), input.clone()));
90
91        if let Some(err) = self.failing.lock().unwrap().get(name) {
92            return Err(err());
93        }
94
95        let mut state = self.state.lock().unwrap();
96
97        let res = match name {
98
99            // Servers and devices.
100            "DbAddServer" => state.add_server_cmd(input),
101            "DbDeleteServer" => state.delete_server_cmd(input),
102            "DbAddDevice" => state.add_dev_cmd(input),
103            "DbDeleteDevice" => state.delete_dev_cmd(input),
104            "DbGetDeviceClassList" => state.list_server_dev(input),
105
106            // Exporting and importing.
107            "DbExportDevice" | "DbExportServer" => state.export(input),
108            "DbUnExportDevice" => state.unexport_dev(input),
109            "DbUnExportServer" => state.unexport_server(input),
110            "DbImportDevice" => state.import_dev(input),
111
112            // Properties of devices and classes.
113            "DbGetDeviceProperty" | "DbGetClassProperty" => state.get_prop(Kind::of(name), input),
114            "DbGetDevicePropertyList" | "DbGetClassPropertyList" => state.list_prop(Kind::of(name), input),
115            "DbPutDeviceProperty" | "DbPutClassProperty" => state.put_prop(Kind::of(name), input),
116            "DbDeleteDeviceProperty" | "DbDeleteClassProperty" => state.delete_prop(Kind::of(name), input),
117
118            // Properties of the attributes of devices and classes.
119            "DbGetDeviceAttributeProperty2" | "DbGetClassAttributeProperty2" => state.get_attr_prop(Kind::of(name), input),
120            "DbGetDeviceAttributeList" | "DbGetClassAttributeList" => state.list_attr(Kind::of(name), input),
121            "DbPutDeviceAttributeProperty2" | "DbPutClassAttributeProperty2" => state.put_attr_prop(Kind::of(name), input),
122            "DbDeleteDeviceAttributeProperty" | "DbDeleteClassAttributeProperty" => state.delete_attr_prop(Kind::of(name), input),
123
124            _ => Err(Error::not_implemented()),
125
126        };
127
128        res.map_err(|err| err.with_append_description(format_args!("The command {name} of the database of a test")))
129
130    }
131
132}
133
134/// What the database holds.
135#[derive(Debug, Default)]
136struct State {
137    /// The lowercase names of the devices of each server, by lowercase `exe/inst` name, in
138    /// the order they were added.
139    servers: HashMap<String, Vec<String>>,
140    /// The devices, by lowercase name.
141    devs: HashMap<String, Dev>,
142    /// The properties of each object.
143    props: HashMap<Object, Props>,
144    /// The properties of the attributes of each object.
145    attr_props: HashMap<Object, Entries<Props>>,
146}
147
148/// One device, as it was defined and as it was last exported.
149#[derive(Debug)]
150struct Dev {
151    name: String,
152    class: String,
153    server: String,
154    /// What the device was last exported with, which a real database keeps once it is
155    /// unexported, only the flag telling it is not running anymore.
156    export: Option<Export>,
157    exported: bool,
158}
159
160#[derive(Debug)]
161struct Export {
162    itr: String,
163    host: String,
164    version: String,
165    pid: i32,
166}
167
168/// An object properties are of, a device or a class, by its lowercase name.
169type Object = (Kind, String);
170
171/// The kind of object a property is of.
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173enum Kind {
174    Device,
175    Class,
176}
177
178impl Kind {
179
180    /// What a property command is about.
181    fn of(command: &str) -> Self {
182        if command.contains("Class") { Self::Class } else { Self::Device }
183    }
184
185    fn object(self, name: &str) -> Object {
186        (self, name.to_lowercase())
187    }
188
189}
190
191/// The properties of an object or an attribute, each with its values.
192type Props = Entries<Vec<String>>;
193
194/// Named entries, kept in the order they were first written and looked up regardless of
195/// the case of their name, which keeps the case it was first written with.
196#[derive(Debug)]
197struct Entries<T>(Vec<(String, T)>);
198
199impl<T> Default for Entries<T> {
200    fn default() -> Self {
201        Self(Vec::new())
202    }
203}
204
205impl<T> Entries<T> {
206
207    fn get(&self, name: &str) -> Option<&T> {
208        self.0.iter().find(|(other, _)| other.eq_ignore_ascii_case(name)).map(|(_, value)| value)
209    }
210
211    /// The entry of that name, added empty if there is none.
212    fn entry(&mut self, name: &str) -> &mut T where T: Default {
213        let index = match self.0.iter().position(|(other, _)| other.eq_ignore_ascii_case(name)) {
214            Some(index) => index,
215            None => {
216                self.0.push((name.to_string(), T::default()));
217                self.0.len() - 1
218            }
219        };
220        &mut self.0[index].1
221    }
222
223    /// Remove the entries of those names.
224    fn remove(&mut self, names: &[String]) {
225        self.0.retain(|(name, _)| !names.iter().any(|removed| removed.eq_ignore_ascii_case(name)));
226    }
227
228    /// The names matching a filter, see [`wildcard`].
229    fn names(&self, filter: &str) -> Vec<&str> {
230        self.0.iter().map(|(name, _)| name.as_str()).filter(|name| wildcard(filter, name)).collect()
231    }
232
233}
234
235// -- Servers and devices --
236
237impl State {
238
239    /// The server, then a device and its class, as many times as it has some.
240    fn add_server_cmd(&mut self, input: Value) -> Result<Value> {
241        let args = strings(input)?;
242        let [server, pairs @ ..] = &args[..] else { return Err(malformed_err()) };
243        if pairs.len() % 2 != 0 {
244            return Err(malformed_err());
245        }
246        self.add_server(server);
247        for pair in pairs.chunks_exact(2) {
248            self.add_dev(server, &pair[0], &pair[1]);
249        }
250        Ok(Value::Void)
251    }
252
253    fn delete_server_cmd(&mut self, input: Value) -> Result<Value> {
254        let server = String::try_from(input)?;
255        for dev in self.servers.remove(&server.to_lowercase()).unwrap_or_default() {
256            self.devs.remove(&dev);
257        }
258        Ok(Value::Void)
259    }
260
261    /// The server, the device and its class.
262    fn add_dev_cmd(&mut self, input: Value) -> Result<Value> {
263        let args = strings(input)?;
264        let [server, dev, class] = &args[..] else { return Err(malformed_err()) };
265        self.add_server(server);
266        self.add_dev(server, dev, class);
267        Ok(Value::Void)
268    }
269
270    fn delete_dev_cmd(&mut self, input: Value) -> Result<Value> {
271        let dev = String::try_from(input)?.to_lowercase();
272        if let Some(removed) = self.devs.remove(&dev) {
273            self.server_devs(&removed.server).retain(|other| *other != dev);
274        }
275        Ok(Value::Void)
276    }
277
278    /// Every device of the server, then its class, one after the other.
279    fn list_server_dev(&mut self, input: Value) -> Result<Value> {
280        let server = String::try_from(input)?;
281        let listing = self.servers.get(&server.to_lowercase())
282            .into_iter()
283            .flatten()
284            .filter_map(|dev| self.devs.get(dev))
285            .flat_map(|dev| [dev.name.as_str(), dev.class.as_str()])
286            .collect::<Vec<_>>();
287        Ok(Value::from(Spectrum::<str>::from(&listing[..])))
288    }
289
290    /// Define a server if it is not already, along with its admin device, as the tools
291    /// registering a server do, since a server always exports it.
292    fn add_server(&mut self, server: &str) {
293        if !self.servers.contains_key(&server.to_lowercase()) {
294            self.add_dev(server, &format!("dserver/{server}"), ADMIN_CLASS);
295        }
296    }
297
298    /// Define a device in a server, moving it there if it was already defined in another
299    /// one, as `Databaseds` does, what it was last exported with being kept.
300    fn add_dev(&mut self, server: &str, name: &str, class: &str) {
301
302        let key = name.to_lowercase();
303        let previous = self.devs.remove(&key);
304
305        if let Some(previous) = &previous {
306            self.server_devs(&previous.server).retain(|other| *other != key);
307        }
308        self.server_devs(server).push(key.clone());
309
310        let (export, exported) = previous.map_or((None, false), |dev| (dev.export, dev.exported));
311        self.devs.insert(key, Dev {
312            name: name.to_string(),
313            class: class.to_string(),
314            server: server.to_string(),
315            export,
316            exported,
317        });
318
319    }
320
321    /// The devices of a server, which is defined with none if it was not.
322    fn server_devs(&mut self, server: &str) -> &mut Vec<String> {
323        self.servers.entry(server.to_lowercase()).or_default()
324    }
325
326}
327
328// -- Exporting and importing --
329
330impl State {
331
332    /// Five strings per device: its name, its reference, its host, the process running it
333    /// and its version.
334    fn export(&mut self, input: Value) -> Result<Value> {
335        let args = strings(input)?;
336        if args.len() % 5 != 0 {
337            return Err(malformed_err());
338        }
339        for info in args.chunks_exact(5) {
340            let [name, itr, host, pid, version] = info else { unreachable!() };
341            let dev = self.devs.get_mut(&name.to_lowercase())
342                .ok_or_else(|| not_defined_err(name))?;
343            dev.export = Some(Export {
344                itr: itr.clone(),
345                host: host.clone(),
346                pid: pid.parse().map_err(|_| malformed_err())?,
347                version: version.clone(),
348            });
349            dev.exported = true;
350        }
351        Ok(Value::Void)
352    }
353
354    fn unexport_dev(&mut self, input: Value) -> Result<Value> {
355        let dev = String::try_from(input)?;
356        if let Some(dev) = self.devs.get_mut(&dev.to_lowercase()) {
357            dev.exported = false;
358        }
359        Ok(Value::Void)
360    }
361
362    fn unexport_server(&mut self, input: Value) -> Result<Value> {
363        let server = String::try_from(input)?;
364        for dev in self.servers.get(&server.to_lowercase()).into_iter().flatten() {
365            if let Some(dev) = self.devs.get_mut(dev) {
366                dev.exported = false;
367            }
368        }
369        Ok(Value::Void)
370    }
371
372    /// The exported flag and the process, then the name, the reference, the version, the
373    /// server, the host and the class.
374    fn import_dev(&mut self, input: Value) -> Result<Value> {
375        let name = String::try_from(input)?;
376        let dev = self.devs.get(&name.to_lowercase())
377            .ok_or_else(|| not_defined_err(&name))?;
378        let (itr, version, host, pid) = match &dev.export {
379            Some(export) => (&*export.itr, &*export.version, &*export.host, export.pid),
380            None => ("nada", "0", "nada", 0),
381        };
382        let strings = [&*dev.name, itr, version, &*dev.server, host, &*dev.class];
383        Ok(Value::LongString(
384            Spectrum::from(vec![i32::from(dev.exported), pid]),
385            Spectrum::<str>::from(&strings[..]),
386        ))
387    }
388
389}
390
391// -- Properties --
392
393impl State {
394
395    /// The object, then the properties, answered with the object and how many properties,
396    /// then each property with how many values it has and its values, a property that has
397    /// none being said to have none.
398    fn get_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
399        let args = strings(input)?;
400        let [obj_name, prop_names @ ..] = &args[..] else { return Err(malformed_err()) };
401        let props = self.props.get(&kind.object(obj_name));
402        let mut listing = vec![obj_name.clone(), prop_names.len().to_string()];
403        for prop_name in prop_names {
404            let values = props.and_then(|props| props.get(prop_name)).map_or(&[][..], Vec::as_slice);
405            listing.push(prop_name.clone());
406            listing.push(values.len().to_string());
407            listing.extend(values.iter().cloned());
408        }
409        Ok(Value::from(Spectrum::<str>::from(&listing[..])))
410    }
411
412    /// The object and a filter.
413    fn list_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
414        let args = strings(input)?;
415        let [obj_name, filter] = &args[..] else { return Err(malformed_err()) };
416        let names = self.props.get(&kind.object(obj_name)).map_or_else(Vec::new, |props| props.names(filter));
417        Ok(Value::from(Spectrum::<str>::from(&names[..])))
418    }
419
420    /// The object and how many properties, then each property with how many values it has
421    /// and its values.
422    fn put_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
423        let args = strings(input)?;
424        let mut cursor = Cursor::new(&args);
425        let props = self.props.entry(kind.object(cursor.next()?)).or_default();
426        for _ in 0..cursor.count()? {
427            let prop_name = cursor.next()?;
428            *props.entry(prop_name) = cursor.values()?.to_vec();
429        }
430        cursor.end()?;
431        Ok(Value::Void)
432    }
433
434    /// The object, then the properties.
435    fn delete_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
436        let args = strings(input)?;
437        let [obj_name, prop_names @ ..] = &args[..] else { return Err(malformed_err()) };
438        if let Some(props) = self.props.get_mut(&kind.object(obj_name)) {
439            props.remove(prop_names);
440        }
441        Ok(Value::Void)
442    }
443
444}
445
446// -- Properties of attributes --
447
448impl State {
449
450    /// The object, then the attributes, answered with the object and how many attributes,
451    /// then each attribute with how many properties it has, then each property with how
452    /// many values it has and its values.
453    fn get_attr_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
454        let args = strings(input)?;
455        let [obj_name, attr_names @ ..] = &args[..] else { return Err(malformed_err()) };
456        let attrs = self.attr_props.get(&kind.object(obj_name));
457        let mut listing = vec![obj_name.clone(), attr_names.len().to_string()];
458        for attr_name in attr_names {
459            let props = attrs.and_then(|attrs| attrs.get(attr_name)).map_or(&[][..], |props| &props.0[..]);
460            listing.push(attr_name.clone());
461            listing.push(props.len().to_string());
462            for (prop_name, values) in props {
463                listing.push(prop_name.clone());
464                listing.push(values.len().to_string());
465                // A property holding no value is followed by a placeholder, which is what
466                // the database server sends for one.
467                if values.is_empty() {
468                    listing.push(" ".to_string());
469                }
470                listing.extend(values.iter().cloned());
471            }
472        }
473        Ok(Value::from(Spectrum::<str>::from(&listing[..])))
474    }
475
476    /// The object and a filter.
477    fn list_attr(&mut self, kind: Kind, input: Value) -> Result<Value> {
478        let args = strings(input)?;
479        let [obj_name, filter] = &args[..] else { return Err(malformed_err()) };
480        let names = self.attr_props.get(&kind.object(obj_name)).map_or_else(Vec::new, |attrs| attrs.names(filter));
481        Ok(Value::from(Spectrum::<str>::from(&names[..])))
482    }
483
484    /// The object and how many attributes, then each attribute with how many properties it
485    /// has, then each property with how many values it has and its values.
486    fn put_attr_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
487        let args = strings(input)?;
488        let mut cursor = Cursor::new(&args);
489        let attrs = self.attr_props.entry(kind.object(cursor.next()?)).or_default();
490        for _ in 0..cursor.count()? {
491            let props = attrs.entry(cursor.next()?);
492            for _ in 0..cursor.count()? {
493                let prop_name = cursor.next()?;
494                *props.entry(prop_name) = cursor.values()?.to_vec();
495            }
496        }
497        cursor.end()?;
498        Ok(Value::Void)
499    }
500
501    /// The object and the attribute, then the properties.
502    fn delete_attr_prop(&mut self, kind: Kind, input: Value) -> Result<Value> {
503        let args = strings(input)?;
504        let [obj_name, attr_name, prop_names @ ..] = &args[..] else { return Err(malformed_err()) };
505        if let Some(attrs) = self.attr_props.get_mut(&kind.object(obj_name)) {
506            attrs.entry(attr_name).remove(prop_names);
507            // An attribute left with no property is no longer listed.
508            attrs.0.retain(|(_, props)| !props.0.is_empty());
509        }
510        Ok(Value::Void)
511    }
512
513}
514
515// -- Arguments --
516
517/// Reading a command argument made of counted runs of strings.
518struct Cursor<'a> {
519    args: &'a [String],
520    pos: usize,
521}
522
523impl<'a> Cursor<'a> {
524
525    fn new(args: &'a [String]) -> Self {
526        Self { args, pos: 0 }
527    }
528
529    fn next(&mut self) -> Result<&'a str> {
530        let arg = self.args.get(self.pos).ok_or_else(malformed_err)?;
531        self.pos += 1;
532        Ok(arg)
533    }
534
535    fn count(&mut self) -> Result<usize> {
536        self.next()?.parse().map_err(|_| malformed_err())
537    }
538
539    /// A count, then that many values.
540    fn values(&mut self) -> Result<&'a [String]> {
541        let len = self.count()?;
542        let values = self.args.get(self.pos..self.pos + len).ok_or_else(malformed_err)?;
543        self.pos += len;
544        Ok(values)
545    }
546
547    /// Fail if anything was left unread, which is a count that does not match.
548    fn end(&self) -> Result<()> {
549        match self.pos == self.args.len() {
550            true => Ok(()),
551            false => Err(malformed_err()),
552        }
553    }
554
555}
556
557/// The strings of a command argument.
558fn strings(input: Value) -> Result<Vec<String>> {
559    Vec::<String>::try_from(input)
560}
561
562/// Whether a name matches a filter of the database, in which `*` stands for anything,
563/// regardless of case.
564fn wildcard(filter: &str, name: &str) -> bool {
565
566    let (filter, name) = (filter.to_lowercase(), name.to_lowercase());
567    let mut parts = filter.split('*');
568
569    // Without any star the filter is the name itself.
570    let first = parts.next().unwrap_or_default();
571    let Some(mut rest) = name.strip_prefix(first) else { return false };
572
573    let parts = parts.collect::<Vec<_>>();
574    let Some((last, middle)) = parts.split_last() else { return rest.is_empty() };
575
576    for part in middle {
577        match rest.find(part) {
578            Some(index) => rest = &rest[index + part.len()..],
579            None => return false,
580        }
581    }
582
583    rest.ends_with(last)
584
585}
586
587fn malformed_err() -> Error {
588    Error::new(ErrorKind::InvalidData)
589        .with_description("The argument is malformed")
590}
591
592fn not_defined_err(dev: &str) -> Error {
593    Error::new(ErrorKind::Reason(reason::DB_DeviceNotDefined))
594        .with_description(format!("The device {dev} is not defined in the database"))
595}
596
597// =========================== //
598//      Device and backend     //
599// =========================== //
600
601/// The database device, as a client reaches it.
602#[derive(Debug)]
603pub struct TestDatabaseDev(pub Arc<TestDatabase>);
604
605fn not_implemented<'a, T: Send + 'a>(what: &'static str) -> BoxFuture<'a, Result<T>> {
606    Box::pin(ready(Err(Error::not_implemented()
607        .with_append_description(format_args!("{what} of the database of a test")))))
608}
609
610impl BackendDev for TestDatabaseDev {
611
612    fn name(&self) -> BoxFuture<'_, Result<Arc<str>>> {
613        Box::pin(ready(Ok(Arc::from(NAME))))
614    }
615
616    fn admin_dev_name(&self) -> BoxFuture<'_, Result<Arc<str>>> {
617        not_implemented("The admin device")
618    }
619
620    fn description(&self) -> BoxFuture<'_, Result<Arc<str>>> {
621        Box::pin(ready(Ok(Arc::from("The Tango database of a test"))))
622    }
623
624    fn info(&self) -> BoxFuture<'_, Result<DevInfo>> {
625        not_implemented("The info")
626    }
627
628    fn ping(&self) -> BoxFuture<'_, Result<()>> {
629        Box::pin(ready(Ok(())))
630    }
631
632    fn state(&self) -> BoxFuture<'_, Result<DevState>> {
633        Box::pin(ready(Ok(DevState::On)))
634    }
635
636    fn status(&self) -> BoxFuture<'_, Result<String>> {
637        Box::pin(ready(Ok("The database of a test".to_string())))
638    }
639
640    fn black_box(&self, _max: Option<usize>) -> BoxFuture<'_, Result<Vec<String>>> {
641        not_implemented("The black box")
642    }
643
644    fn command(&self, name: Scalar<str>, input: Value, _source: DevSource) -> BoxFuture<'_, Result<Value>> {
645        Box::pin(ready(self.0.command(name.value(), input)))
646    }
647
648    fn read_attr(&self, _names: Option<Spectrum<str>>, _source: DevSource) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>> {
649        not_implemented("Reading the attributes")
650    }
651
652    fn write_attr(&self, _values: Vec<Named<Value>>) -> BoxFuture<'_, Result<Vec<Result<()>>>> {
653        not_implemented("Writing the attributes")
654    }
655
656    fn write_read_attr(&self, _values: Vec<Named<Value>>) -> BoxFuture<'_, Result<Vec<NamedResult<AttrRead<Value>>>>> {
657        not_implemented("Writing the attributes")
658    }
659
660    fn command_info(&self, _names: Option<Spectrum<str>>) -> BoxFuture<'_, Result<Vec<NamedResult<CommandInfo>>>> {
661        not_implemented("The commands")
662    }
663
664    fn attr_info(&self, _names: Option<Spectrum<str>>) -> BoxFuture<'_, Result<Vec<NamedResult<AttrInfo>>>> {
665        not_implemented("The attributes")
666    }
667
668    fn set_attr_config(&self, _configs: Vec<Named<AttrConfig>>) -> BoxFuture<'_, Result<Vec<Result<()>>>> {
669        Box::pin(ready(Err(Error::not_implemented()
670            .with_append_description("Writing the configuration of an attribute"))))
671    }
672
673    fn command_history<'a>(&'a self, _name: &'a str, _max: usize) -> BoxFuture<'a, Result<Vec<(SystemTime, Result<Value>)>>> {
674        Box::pin(ready(Err(Error::not_implemented()
675            .with_append_description("The recorded history of a command"))))
676    }
677
678    fn attr_history<'a>(&'a self, _name: &'a str, _max: usize) -> BoxFuture<'a, Result<Vec<Result<AttrRead<Value>>>>> {
679        Box::pin(ready(Err(Error::not_implemented()
680            .with_append_description("The recorded history of an attribute"))))
681    }
682
683    fn subscribe_attr_value<'a>(&'a self, _admin: Arc<dyn BackendDev>, _name: &'a str, _event: AttrValueEvent) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrRead<Value>>>>> {
684        Box::pin(ready(Err(Error::not_implemented()
685            .with_append_description("Subscribing to the readings of an attribute"))))
686    }
687
688    fn subscribe_attr_ready<'a>(&'a self, _admin: Arc<dyn BackendDev>, _name: &'a str) -> BoxFuture<'a, Result<BoxStream<'static, Result<()>>>> {
689        Box::pin(ready(Err(Error::not_implemented()
690            .with_append_description("Subscribing to an attribute being ready"))))
691    }
692
693    fn subscribe_attr_conf<'a>(&'a self, _admin: Arc<dyn BackendDev>, _name: &'a str) -> BoxFuture<'a, Result<BoxStream<'static, Result<AttrInfo>>>> {
694        Box::pin(ready(Err(Error::not_implemented()
695            .with_append_description("Subscribing to the configuration of an attribute"))))
696    }
697
698}
699
700#[cfg(test)]
701mod tests {
702
703    use super::*;
704
705    use crate::database::ExportDevInfo;
706    use crate::testing::TestContext;
707
708    fn context() -> TestContext {
709        TestContext::new().unwrap()
710    }
711
712    #[test]
713    fn wildcards() {
714        assert!(wildcard("*", "anything"));
715        assert!(wildcard("Pos*", "position"));
716        assert!(wildcard("*tion", "Position"));
717        assert!(wildcard("p*s*n", "position"));
718        assert!(wildcard("position", "POSITION"));
719        assert!(!wildcard("pos", "position"));
720        assert!(!wildcard("p*x*n", "position"));
721    }
722
723    #[test]
724    fn servers_and_devices() {
725
726        let ctx = context();
727        let db = ctx.database();
728        db.add_server("Motor/test", &[("test/motor/1", "Motor"), ("test/motor/2", "Motor")]).unwrap();
729        db.add_dev("test/axis/1", "Axis", "Motor/test").unwrap();
730
731        let listed = db.list_server_dev("motor/TEST").unwrap();
732        let listed = listed.iter().map(|dev| (dev.name.value(), dev.value.value())).collect::<Vec<_>>();
733        assert_eq!(listed, [
734            ("dserver/Motor/test", "DServer"),
735            ("test/motor/1", "Motor"),
736            ("test/motor/2", "Motor"),
737            ("test/axis/1", "Axis"),
738        ]);
739
740        db.delete_dev("TEST/MOTOR/2").unwrap();
741        assert_eq!(db.list_server_dev("Motor/test").unwrap().len(), 3);
742
743        db.delete_server("Motor/test").unwrap();
744        assert!(db.list_server_dev("Motor/test").unwrap().is_empty());
745        assert_eq!(db.import_dev("test/motor/1").unwrap_err().kind(), ErrorKind::DevNotFound);
746
747    }
748
749    #[test]
750    fn export_and_import() {
751
752        let ctx = context();
753        let db = ctx.database();
754        db.add_dev("test/motor/1", "Motor", "Motor/test").unwrap();
755
756        let info = db.import_dev("test/motor/1").unwrap();
757        assert!(!info.exported);
758        assert_eq!(info.class.as_ref().map(|class| class.value()), Some("Motor"));
759
760        db.export_server(&[ExportDevInfo {
761            name: "test/motor/1".into(),
762            itr: "IOR:0123".into(),
763            host: "localhost".into(),
764            version: "6".into(),
765            pid: 42,
766        }]).unwrap();
767
768        let info = db.import_dev("test/motor/1").unwrap();
769        assert!(info.exported);
770        assert_eq!((info.itr.value(), info.host.value(), info.version.value(), info.server.value(), info.pid),
771            ("IOR:0123", "localhost", "6", "Motor/test", 42));
772
773        // Unexported, the reference stays, only the flag goes down.
774        db.unexport_server("motor/test").unwrap();
775        let info = db.import_dev("test/motor/1").unwrap();
776        assert!(!info.exported);
777        assert_eq!(info.itr.value(), "IOR:0123");
778
779    }
780
781    #[test]
782    fn properties() {
783
784        let ctx = context();
785        let db = ctx.database();
786
787        db.set_dev_prop("test/motor/1", [("Limit", "90"), ("Unit", "deg")]).unwrap();
788        db.set_dev_prop("TEST/motor/1", ("limit", "45")).unwrap();
789        assert_eq!(db.get_dev_prop::<f64, _>("test/motor/1", "Limit").unwrap(), 45.0);
790        assert_eq!(db.list_dev_prop("test/motor/1", "*").unwrap().len(), 2);
791
792        db.del_dev_prop("test/motor/1", "unit").unwrap();
793        assert_eq!(db.list_dev_prop("test/motor/1", "*").unwrap().len(), 1);
794
795        // Classes and devices do not share their properties.
796        db.set_class_prop("Motor", ("Limit", "10")).unwrap();
797        assert_eq!(db.get_class_prop::<f64, _>("Motor", "Limit").unwrap(), 10.0);
798        assert_eq!(db.get_dev_prop::<f64, _>("test/motor/1", "Limit").unwrap(), 45.0);
799
800        db.set_dev_attr_prop("test/motor/1", ("Position", [("unit", "mm"), ("min_value", "-5")])).unwrap();
801        assert_eq!(db.get_dev_attr_prop::<String, _, _>("test/motor/1", "position", "Unit").unwrap(), "mm");
802
803        db.del_dev_attr_prop("test/motor/1", "Position", ["unit", "min_value"]).unwrap();
804        // A property that has no value anymore reads as one holding nothing.
805        assert_eq!(db.get_dev_attr_prop::<String, _, _>("test/motor/1", "Position", "unit").unwrap(), "");
806
807    }
808
809    /// Every clone of the proxy reaches the same database, and two databases are two.
810    #[test]
811    fn clones_share() {
812        let ctx = context();
813        let db = ctx.database();
814        let clone = db.clone();
815        clone.add_dev("test/motor/1", "Motor", "Motor/test").unwrap();
816        assert_eq!(db.list_server_dev("Motor/test").unwrap().len(), 2);
817        let other = context();
818        assert!(other.database().list_server_dev("Motor/test").unwrap().is_empty());
819        assert_ne!(db, other.database());
820    }
821
822}