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