1use crate::metrics::Metrics;
6use crate::puppet::DiffType;
7use anyhow::{bail, format_err, Error};
8use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
9use base64::engine::Engine as _;
10use diagnostics_hierarchy::{
11 ArrayContent, DiagnosticsHierarchy, ExponentialHistogram, LinearHistogram,
12 Property as iProperty,
13};
14use fidl_diagnostics_validate::{self as validate, Value};
15use inspect_format::{ArrayFormat, BlockIndex, LinkNodeDisposition};
16use num_traits::Zero;
17use std::collections::{HashMap, HashSet};
18use std::fmt;
19
20mod scanner;
21pub use scanner::Scanner;
22mod fetch;
23pub use fetch::LazyNode;
24
25#[cfg(test)]
26use num_traits::ToPrimitive;
27
28const ROOT_NAME: &str = "root";
29
30#[derive(Debug, Clone)]
36pub struct Data {
37 nodes: HashMap<BlockIndex, Node>,
38 properties: HashMap<BlockIndex, Property>,
39 tombstone_nodes: HashSet<BlockIndex>,
40 tombstone_properties: HashSet<BlockIndex>,
41}
42
43#[derive(Debug, Clone)]
60pub struct Node {
61 name: String,
62 parent: BlockIndex,
63 children: HashSet<BlockIndex>,
64 properties: HashSet<BlockIndex>,
65}
66
67#[derive(Debug, Clone)]
68pub struct Property {
69 name: String,
70 #[allow(unused)]
72 id: BlockIndex,
73 parent: BlockIndex,
74 payload: Payload,
75}
76
77#[derive(Debug, Clone)]
78enum Payload {
81 String(#[allow(unused)] String),
82 Bytes(Vec<u8>),
83 Int(i64),
84 Uint(u64),
85 Double(f64),
86 Bool(#[allow(unused)] bool),
87 IntArray(Vec<i64>, ArrayFormat),
88 UintArray(Vec<u64>, ArrayFormat),
89 DoubleArray(Vec<f64>, ArrayFormat),
90 StringArray(Vec<String>),
91 Link { disposition: LinkNodeDisposition, parsed_data: Data },
92
93 GenericNumber(#[allow(unused)] String),
97 GenericArray(#[allow(unused)] Vec<String>),
98 GenericLinearHistogram(#[allow(unused)] Vec<String>),
99 GenericExponentialHistogram(#[allow(unused)] Vec<String>),
100}
101
102fn to_string<T: std::fmt::Display>(v: T) -> String {
103 format!("{}", v)
104}
105
106fn stringify_list<T: std::fmt::Display>(values: Vec<T>) -> Vec<String> {
107 values.into_iter().map(|x| to_string(x)).collect()
108}
109
110fn stringify_linear_histogram<T: Clone + std::fmt::Display + Zero>(
111 histogram: LinearHistogram<T>,
112) -> Vec<String> {
113 let mut values = vec![T::zero(); histogram.size + 2];
114 values[0] = histogram.floor;
115 values[1] = histogram.step;
116 if let Some(indexes) = histogram.indexes {
117 for (index, count) in indexes.iter().zip(histogram.counts) {
118 values[index + 2] = count.clone();
119 }
120 } else {
121 for (index, count) in histogram.counts.iter().enumerate() {
122 values[index + 2] = count.clone();
123 }
124 }
125 stringify_list(values)
126}
127
128fn stringify_exponential_histogram<T: Clone + std::fmt::Display + Zero>(
129 histogram: ExponentialHistogram<T>,
130) -> Vec<String> {
131 let mut values = vec![T::zero(); histogram.size + 3];
132 values[0] = histogram.floor;
133 values[1] = histogram.initial_step;
134 values[2] = histogram.step_multiplier;
135 if let Some(indexes) = histogram.indexes {
136 for (index, count) in indexes.iter().zip(histogram.counts) {
137 values[index + 3] = count.clone();
138 }
139 } else {
140 for (index, count) in histogram.counts.iter().enumerate() {
141 values[index + 3] = count.clone();
142 }
143 }
144 stringify_list(values)
145}
146
147fn handle_array(values: Vec<String>, format: ArrayFormat) -> Payload {
148 match format {
149 ArrayFormat::LinearHistogram => Payload::GenericLinearHistogram(values),
150 ArrayFormat::ExponentialHistogram => Payload::GenericExponentialHistogram(values),
151 _ => Payload::GenericArray(values),
152 }
153}
154
155trait ToGeneric {
156 fn to_generic(self) -> Payload;
157}
158
159impl ToGeneric for Payload {
160 fn to_generic(self) -> Self {
162 match self {
163 Payload::IntArray(val, format) => handle_array(stringify_list(val), format),
164 Payload::UintArray(val, format) => handle_array(stringify_list(val), format),
165 Payload::DoubleArray(val, format) => handle_array(stringify_list(val), format),
166 Payload::StringArray(val) => handle_array(val, ArrayFormat::Default),
167 Payload::Bytes(val) => Payload::String(format!("b64:{}", BASE64_STANDARD.encode(&val))),
168 Payload::Int(val) => Payload::GenericNumber(to_string(val)),
169 Payload::Uint(val) => Payload::GenericNumber(to_string(val)),
170 Payload::Double(val) => Payload::GenericNumber(to_string(val)),
171 Payload::Link { disposition, parsed_data } => {
172 Payload::Link { disposition, parsed_data: parsed_data.clone_generic() }
173 }
174 val => val,
175 }
176 }
177}
178
179impl<T: std::fmt::Display + Clone + Zero> ToGeneric for ArrayContent<T> {
180 fn to_generic(self) -> Payload {
181 match self {
182 ArrayContent::Values(values) => Payload::GenericArray(stringify_list(values)),
183 ArrayContent::LinearHistogram(histogram) => {
184 Payload::GenericLinearHistogram(stringify_linear_histogram(histogram))
185 }
186 ArrayContent::ExponentialHistogram(histogram) => {
187 Payload::GenericExponentialHistogram(stringify_exponential_histogram(histogram))
188 }
189 }
190 }
191}
192
193struct FormattedEntries {
194 nodes: Vec<String>,
195 properties: Vec<String>,
196}
197
198impl Property {
199 fn to_string(&self, prefix: &str) -> String {
200 format!("{}{}: {:?}", prefix, self.name, &self.payload)
201 }
202
203 fn format_entries(&self, prefix: &str) -> FormattedEntries {
206 match &self.payload {
207 Payload::Link { disposition, parsed_data } => match disposition {
208 LinkNodeDisposition::Child => FormattedEntries {
210 nodes: vec![format!(
211 "{}{} ->\n{}",
212 prefix,
213 self.name,
214 parsed_data.nodes[&0.into()].to_string(prefix, parsed_data, true)
215 )],
216 properties: vec![],
217 },
218 LinkNodeDisposition::Inline => {
221 let root = &parsed_data.nodes[&BlockIndex::ROOT];
222 let mut nodes = root
223 .children
224 .iter()
225 .map(|v| {
226 parsed_data.nodes.get(v).map_or("Missing child".into(), |n| {
227 n.to_string(prefix, parsed_data, false)
228 })
229 })
230 .collect::<Vec<_>>();
231 let mut properties = vec![];
232 for FormattedEntries { nodes: mut n, properties: mut p } in
233 root.properties.iter().map(|v| {
234 parsed_data.properties.get(v).map_or(
235 FormattedEntries {
236 nodes: vec![],
237 properties: vec!["Missing property".into()],
238 },
239 |p| p.format_entries(prefix),
240 )
241 })
242 {
243 nodes.append(&mut n);
244 properties.append(&mut p);
245 }
246 FormattedEntries { nodes, properties }
247 }
248 },
249 _ => FormattedEntries { nodes: vec![], properties: vec![self.to_string(prefix)] },
251 }
252 }
253}
254
255impl Node {
256 fn to_string(&self, prefix: &str, tree: &Data, hide_root: bool) -> String {
260 let sub_prefix = format!("{}> ", prefix);
261 let mut nodes = vec![];
262 for node_id in self.children.iter() {
263 nodes.push(
264 tree.nodes
265 .get(node_id)
266 .map_or("Missing child".into(), |n| n.to_string(&sub_prefix, tree, hide_root)),
267 );
268 }
269 let mut properties = vec![];
270
271 for property_id in self.properties.iter() {
272 let FormattedEntries { nodes: mut n, properties: mut p } =
273 tree.properties.get(property_id).map_or(
274 FormattedEntries {
275 nodes: vec![],
276 properties: vec!["Missing property".to_string()],
277 },
278 |p| p.format_entries(&sub_prefix),
279 );
280 properties.append(&mut p);
281 nodes.append(&mut n);
282 }
283
284 nodes.sort_unstable();
285 properties.sort_unstable();
286
287 let mut output_lines = vec![];
288
289 if self.name != ROOT_NAME || !hide_root {
290 output_lines.push(format!("{}{} ->", prefix, self.name));
291 }
292 output_lines.append(&mut properties);
293 output_lines.append(&mut nodes);
294
295 output_lines.join("\n")
296 }
297}
298
299struct Op {
300 int: fn(i64, i64) -> i64,
301 uint: fn(u64, u64) -> u64,
302 double: fn(f64, f64) -> f64,
303 string: fn(String, String) -> String,
304 name: &'static str,
305}
306
307const ADD: Op = Op {
308 int: |a, b| a + b,
309 uint: |a, b| a + b,
310 double: |a, b| a + b,
311 string: |_, _| unreachable!(),
312 name: "add",
313};
314const SUBTRACT: Op = Op {
315 int: |a, b| a - b,
316 uint: |a, b| a - b,
317 double: |a, b| a - b,
318 string: |_, _| unreachable!(),
319 name: "subtract",
320};
321const SET: Op =
322 Op { int: |_a, b| b, uint: |_a, b| b, double: |_a, b| b, string: |_a, b| b, name: "set" };
323
324macro_rules! insert_linear_fn {
325 ($name:ident, $type:ident) => {
326 fn $name(numbers: &mut [$type], value: $type, count: u64) -> Result<(), Error> {
327 let buckets: $type = (numbers.len() as i32 - 4).try_into().unwrap();
328 let floor = numbers[0];
329 let step_size = numbers[1];
330 let index: usize = if value < floor {
331 2
332 } else if value >= floor + buckets * step_size {
333 numbers.len() - 1
334 } else {
335 (((value - floor) / step_size) as $type + 3 as $type) as i32 as usize
336 };
337 numbers[index] += count as $type;
338 Ok(())
339 }
340 };
341}
342
343insert_linear_fn! {insert_linear_i, i64}
344insert_linear_fn! {insert_linear_u, u64}
345insert_linear_fn! {insert_linear_d, f64}
346
347macro_rules! insert_exponential_fn {
356 ($name:ident, $type:ident, $fudge_factor:expr) => {
357 fn $name(numbers: &mut [$type], value: $type, count: u64) -> Result<(), Error> {
358 let buckets = numbers.len() - 5;
359 let floor = numbers[0];
360 let initial_step = numbers[1];
361 let step_multiplier = numbers[2];
362 let index = if value < floor {
363 3
364 } else if value < floor + initial_step {
365 4
366 } else if value
367 >= floor + initial_step * (step_multiplier as f64).powi(buckets as i32 - 1) as $type
368 {
369 numbers.len() - 1
370 } else {
371 ((((value as f64 - floor as f64) / initial_step as f64) as f64).log2()
372 / (step_multiplier as f64 + $fudge_factor).log2())
373 .trunc() as usize
374 + 5
375 };
376 numbers[index] += count as $type;
377 Ok(())
378 }
379 };
380}
381
382insert_exponential_fn! {insert_exponential_i, i64, 0.0000000000000000000001}
383insert_exponential_fn! {insert_exponential_u, u64, 0.0000000000000000000001}
384insert_exponential_fn! {insert_exponential_d, f64, 0.0}
385
386impl Data {
387 pub fn apply(&mut self, action: &validate::Action) -> Result<(), Error> {
391 match action {
392 validate::Action::CreateNode(validate::CreateNode { parent, id, name }) => {
393 self.create_node(parent.into(), id.into(), name)
394 }
395 validate::Action::DeleteNode(validate::DeleteNode { id }) => {
396 self.delete_node(id.into())
397 }
398 validate::Action::CreateNumericProperty(validate::CreateNumericProperty {
399 parent,
400 id,
401 name,
402 value,
403 }) => self.add_property(
404 parent.into(),
405 id.into(),
406 name,
407 match value {
408 validate::Value::IntT(value) => Payload::Int(*value),
409 validate::Value::UintT(value) => Payload::Uint(*value),
410 validate::Value::DoubleT(value) => Payload::Double(*value),
411 unknown => return Err(format_err!("Unknown number type {:?}", unknown)),
412 },
413 ),
414 validate::Action::CreateBytesProperty(validate::CreateBytesProperty {
415 parent,
416 id,
417 name,
418 value,
419 }) => self.add_property(parent.into(), id.into(), name, Payload::Bytes(value.clone())),
420 validate::Action::CreateStringProperty(validate::CreateStringProperty {
421 parent,
422 id,
423 name,
424 value,
425 }) => self.add_property(
426 parent.into(),
427 id.into(),
428 name,
429 Payload::String(value.to_string()),
430 ),
431 validate::Action::CreateBoolProperty(validate::CreateBoolProperty {
432 parent,
433 id,
434 name,
435 value,
436 }) => self.add_property(parent.into(), id.into(), name, Payload::Bool(*value)),
437 validate::Action::DeleteProperty(validate::DeleteProperty { id }) => {
438 self.delete_property(id.into())
439 }
440 validate::Action::SetNumber(validate::SetNumber { id, value }) => {
441 self.modify_number(id.into(), value, SET)
442 }
443 validate::Action::AddNumber(validate::AddNumber { id, value }) => {
444 self.modify_number(id.into(), value, ADD)
445 }
446 validate::Action::SubtractNumber(validate::SubtractNumber { id, value }) => {
447 self.modify_number(id.into(), value, SUBTRACT)
448 }
449 validate::Action::SetBytes(validate::SetBytes { id, value }) => {
450 self.set_bytes(id.into(), value)
451 }
452 validate::Action::SetString(validate::SetString { id, value }) => {
453 self.set_string(id.into(), value)
454 }
455 validate::Action::SetBool(validate::SetBool { id, value }) => {
456 self.set_bool(id.into(), *value)
457 }
458 validate::Action::CreateArrayProperty(validate::CreateArrayProperty {
459 parent,
460 id,
461 name,
462 slots,
463 value_type,
464 }) => self.add_property(
465 parent.into(),
466 id.into(),
467 name,
468 match value_type {
469 validate::ValueType::Int => {
470 Payload::IntArray(vec![0; *slots as usize], ArrayFormat::Default)
471 }
472 validate::ValueType::Uint => {
473 Payload::UintArray(vec![0; *slots as usize], ArrayFormat::Default)
474 }
475 validate::ValueType::Double => {
476 Payload::DoubleArray(vec![0.0; *slots as usize], ArrayFormat::Default)
477 }
478 validate::ValueType::String => {
479 Payload::StringArray(vec![String::new(); *slots as usize])
480 }
481 unknown => return Err(format_err!("unknown value type {unknown:?}")),
482 },
483 ),
484 validate::Action::ArrayAdd(validate::ArrayAdd { id, index, value }) => {
485 self.modify_array(id.into(), *index, value, ADD)
486 }
487 validate::Action::ArraySubtract(validate::ArraySubtract { id, index, value }) => {
488 self.modify_array(id.into(), *index, value, SUBTRACT)
489 }
490 validate::Action::ArraySet(validate::ArraySet { id, index, value }) => {
491 self.modify_array(id.into(), *index, value, SET)
492 }
493 validate::Action::CreateLinearHistogram(validate::CreateLinearHistogram {
494 parent,
495 id,
496 name,
497 floor,
498 step_size,
499 buckets,
500 }) => self.add_property(
501 parent.into(),
502 id.into(),
503 name,
504 match (floor, step_size) {
505 (validate::Value::IntT(floor), validate::Value::IntT(step_size)) => {
506 let mut data = vec![0; *buckets as usize + 4];
507 data[0] = *floor;
508 data[1] = *step_size;
509 Payload::IntArray(data, ArrayFormat::LinearHistogram)
510 }
511 (validate::Value::UintT(floor), validate::Value::UintT(step_size)) => {
512 let mut data = vec![0; *buckets as usize + 4];
513 data[0] = *floor;
514 data[1] = *step_size;
515 Payload::UintArray(data, ArrayFormat::LinearHistogram)
516 }
517 (validate::Value::DoubleT(floor), validate::Value::DoubleT(step_size)) => {
518 let mut data = vec![0.0; *buckets as usize + 4];
519 data[0] = *floor;
520 data[1] = *step_size;
521 Payload::DoubleArray(data, ArrayFormat::LinearHistogram)
522 }
523 unexpected => {
524 return Err(format_err!(
525 "Bad types in CreateLinearHistogram: {:?}",
526 unexpected
527 ))
528 }
529 },
530 ),
531 validate::Action::CreateExponentialHistogram(
532 validate::CreateExponentialHistogram {
533 parent,
534 id,
535 name,
536 floor,
537 initial_step,
538 step_multiplier,
539 buckets,
540 },
541 ) => self.add_property(
542 parent.into(),
543 id.into(),
544 name,
545 match (floor, initial_step, step_multiplier) {
546 (
547 validate::Value::IntT(floor),
548 validate::Value::IntT(initial_step),
549 validate::Value::IntT(step_multiplier),
550 ) => {
551 let mut data = vec![0i64; *buckets as usize + 5];
552 data[0] = *floor;
553 data[1] = *initial_step;
554 data[2] = *step_multiplier;
555 Payload::IntArray(data, ArrayFormat::ExponentialHistogram)
556 }
557 (
558 validate::Value::UintT(floor),
559 validate::Value::UintT(initial_step),
560 validate::Value::UintT(step_multiplier),
561 ) => {
562 let mut data = vec![0u64; *buckets as usize + 5];
563 data[0] = *floor;
564 data[1] = *initial_step;
565 data[2] = *step_multiplier;
566 Payload::UintArray(data, ArrayFormat::ExponentialHistogram)
567 }
568 (
569 validate::Value::DoubleT(floor),
570 validate::Value::DoubleT(initial_step),
571 validate::Value::DoubleT(step_multiplier),
572 ) => {
573 let mut data = vec![0.0f64; *buckets as usize + 5];
574 data[0] = *floor;
575 data[1] = *initial_step;
576 data[2] = *step_multiplier;
577 Payload::DoubleArray(data, ArrayFormat::ExponentialHistogram)
578 }
579 unexpected => {
580 return Err(format_err!(
581 "Bad types in CreateExponentialHistogram: {:?}",
582 unexpected
583 ))
584 }
585 },
586 ),
587 validate::Action::Insert(validate::Insert { id, value }) => {
588 if let Some(mut property) = self.properties.get_mut(&id.into()) {
589 match (&mut property, value) {
590 (
591 Property {
592 payload: Payload::IntArray(numbers, ArrayFormat::LinearHistogram),
593 ..
594 },
595 Value::IntT(value),
596 ) => insert_linear_i(numbers, *value, 1),
597 (
598 Property {
599 payload:
600 Payload::IntArray(numbers, ArrayFormat::ExponentialHistogram),
601 ..
602 },
603 Value::IntT(value),
604 ) => insert_exponential_i(numbers, *value, 1),
605 (
606 Property {
607 payload: Payload::UintArray(numbers, ArrayFormat::LinearHistogram),
608 ..
609 },
610 Value::UintT(value),
611 ) => insert_linear_u(numbers, *value, 1),
612 (
613 Property {
614 payload:
615 Payload::UintArray(numbers, ArrayFormat::ExponentialHistogram),
616 ..
617 },
618 Value::UintT(value),
619 ) => insert_exponential_u(numbers, *value, 1),
620 (
621 Property {
622 payload: Payload::DoubleArray(numbers, ArrayFormat::LinearHistogram),
623 ..
624 },
625 Value::DoubleT(value),
626 ) => insert_linear_d(numbers, *value, 1),
627 (
628 Property {
629 payload:
630 Payload::DoubleArray(numbers, ArrayFormat::ExponentialHistogram),
631 ..
632 },
633 Value::DoubleT(value),
634 ) => insert_exponential_d(numbers, *value, 1),
635 unexpected => {
636 Err(format_err!("Type mismatch {:?} trying to insert", unexpected))
637 }
638 }
639 } else {
640 Err(format_err!("Tried to insert number on nonexistent property {}", id))
641 }
642 }
643 validate::Action::InsertMultiple(validate::InsertMultiple { id, value, count }) => {
644 if let Some(mut property) = self.properties.get_mut(&id.into()) {
645 match (&mut property, value) {
646 (
647 Property {
648 payload: Payload::IntArray(numbers, ArrayFormat::LinearHistogram),
649 ..
650 },
651 Value::IntT(value),
652 ) => insert_linear_i(numbers, *value, *count),
653 (
654 Property {
655 payload:
656 Payload::IntArray(numbers, ArrayFormat::ExponentialHistogram),
657 ..
658 },
659 Value::IntT(value),
660 ) => insert_exponential_i(numbers, *value, *count),
661 (
662 Property {
663 payload: Payload::UintArray(numbers, ArrayFormat::LinearHistogram),
664 ..
665 },
666 Value::UintT(value),
667 ) => insert_linear_u(numbers, *value, *count),
668 (
669 Property {
670 payload:
671 Payload::UintArray(numbers, ArrayFormat::ExponentialHistogram),
672 ..
673 },
674 Value::UintT(value),
675 ) => insert_exponential_u(numbers, *value, *count),
676 (
677 Property {
678 payload: Payload::DoubleArray(numbers, ArrayFormat::LinearHistogram),
679 ..
680 },
681 Value::DoubleT(value),
682 ) => insert_linear_d(numbers, *value, *count),
683 (
684 Property {
685 payload:
686 Payload::DoubleArray(numbers, ArrayFormat::ExponentialHistogram),
687 ..
688 },
689 Value::DoubleT(value),
690 ) => insert_exponential_d(numbers, *value, *count),
691 unexpected => Err(format_err!(
692 "Type mismatch {:?} trying to insert multiple",
693 unexpected
694 )),
695 }
696 } else {
697 Err(format_err!(
698 "Tried to insert_multiple number on nonexistent property {}",
699 id
700 ))
701 }
702 }
703 _ => Err(format_err!("Unknown action {:?}", action)),
704 }
705 }
706
707 pub fn apply_lazy(&mut self, lazy_action: &validate::LazyAction) -> Result<(), Error> {
708 match lazy_action {
709 validate::LazyAction::CreateLazyNode(validate::CreateLazyNode {
710 parent,
711 id,
712 name,
713 disposition,
714 actions,
715 }) => self.add_lazy_node(parent.into(), id.into(), name, disposition, actions),
716 validate::LazyAction::DeleteLazyNode(validate::DeleteLazyNode { id }) => {
717 self.delete_property(id.into())
718 }
719 _ => Err(format_err!("Unknown lazy action {:?}", lazy_action)),
720 }
721 }
722
723 fn create_node(&mut self, parent: BlockIndex, id: BlockIndex, name: &str) -> Result<(), Error> {
724 let node = Node {
725 name: name.to_owned(),
726 parent,
727 children: HashSet::new(),
728 properties: HashSet::new(),
729 };
730 if self.tombstone_nodes.contains(&id) {
731 return Err(format_err!("Tried to create implicitly deleted node {}", id));
732 }
733 if self.nodes.insert(id, node).is_some() {
734 return Err(format_err!("Create called when node already existed at {}", id));
735 }
736 if let Some(parent_node) = self.nodes.get_mut(&parent) {
737 parent_node.children.insert(id);
738 } else {
739 return Err(format_err!("Parent {} of created node {} doesn't exist", parent, id));
740 }
741 Ok(())
742 }
743
744 fn delete_node(&mut self, id: BlockIndex) -> Result<(), Error> {
745 if id == BlockIndex::ROOT {
746 return Err(format_err!("Do not try to delete node 0"));
747 }
748 if self.tombstone_nodes.remove(&id) {
749 return Ok(());
750 }
751 if let Some(node) = self.nodes.remove(&id) {
752 for child in node.children.clone().iter() {
755 self.make_tombstone_node(*child)?;
756 }
757 for property in node.properties.clone().iter() {
758 self.make_tombstone_property(*property)?;
759 }
760 if let Some(parent) = self.nodes.get_mut(&node.parent) {
761 if !parent.children.remove(&id) {
762 bail!(
768 "Internal error! Parent {} didn't know about this child {}",
769 node.parent,
770 id
771 );
772 }
773 }
774 } else {
775 return Err(format_err!("Delete of nonexistent node {}", id));
776 }
777 Ok(())
778 }
779
780 fn make_tombstone_node(&mut self, id: BlockIndex) -> Result<(), Error> {
781 if id == BlockIndex::ROOT {
782 return Err(format_err!("Internal error! Do not try to delete node 0."));
783 }
784 if let Some(node) = self.nodes.remove(&id) {
785 for child in node.children.clone().iter() {
786 self.make_tombstone_node(*child)?;
787 }
788 for property in node.properties.clone().iter() {
789 self.make_tombstone_property(*property)?;
790 }
791 } else {
792 return Err(format_err!("Internal error! Tried to tombstone nonexistent node {}", id));
793 }
794 self.tombstone_nodes.insert(id);
795 Ok(())
796 }
797
798 fn make_tombstone_property(&mut self, id: BlockIndex) -> Result<(), Error> {
799 if self.properties.remove(&id).is_none() {
800 return Err(format_err!(
801 "Internal error! Tried to tombstone nonexistent property {}",
802 id
803 ));
804 }
805 self.tombstone_properties.insert(id);
806 Ok(())
807 }
808
809 fn add_property(
810 &mut self,
811 parent: BlockIndex,
812 id: BlockIndex,
813 name: &str,
814 payload: Payload,
815 ) -> Result<(), Error> {
816 if let Some(node) = self.nodes.get_mut(&parent) {
817 node.properties.insert(id);
818 } else {
819 return Err(format_err!("Parent {} of property {} not found", parent, id));
820 }
821 if self.tombstone_properties.contains(&id) {
822 return Err(format_err!("Tried to create implicitly deleted property {}", id));
823 }
824 let property = Property { parent, id, name: name.into(), payload };
825 if self.properties.insert(id, property).is_some() {
826 return Err(format_err!("Property insert called on existing id {}", id));
827 }
828 Ok(())
829 }
830
831 fn delete_property(&mut self, id: BlockIndex) -> Result<(), Error> {
832 if self.tombstone_properties.remove(&id) {
833 return Ok(());
834 }
835 if let Some(property) = self.properties.remove(&id) {
836 if let Some(node) = self.nodes.get_mut(&property.parent) {
837 if !node.properties.remove(&id) {
838 bail!(
839 "Internal error! Property {}'s parent {} didn't have it as child",
840 id,
841 property.parent
842 );
843 }
844 } else {
845 bail!(
846 "Internal error! Property {}'s parent {} doesn't exist on delete",
847 id,
848 property.parent
849 );
850 }
851 } else {
852 return Err(format_err!("Delete of nonexistent property {}", id));
853 }
854 Ok(())
855 }
856
857 fn modify_number(
858 &mut self,
859 id: BlockIndex,
860 value: &validate::Value,
861 op: Op,
862 ) -> Result<(), Error> {
863 if let Some(property) = self.properties.get_mut(&id) {
864 match (&property, value) {
865 (Property { payload: Payload::Int(old), .. }, Value::IntT(value)) => {
866 property.payload = Payload::Int((op.int)(*old, *value));
867 }
868 (Property { payload: Payload::Uint(old), .. }, Value::UintT(value)) => {
869 property.payload = Payload::Uint((op.uint)(*old, *value));
870 }
871 (Property { payload: Payload::Double(old), .. }, Value::DoubleT(value)) => {
872 property.payload = Payload::Double((op.double)(*old, *value));
873 }
874 unexpected => {
875 return Err(format_err!("Bad types {:?} trying to set number", unexpected))
876 }
877 }
878 } else {
879 return Err(format_err!("Tried to {} number on nonexistent property {}", op.name, id));
880 }
881 Ok(())
882 }
883
884 fn modify_array(
885 &mut self,
886 id: BlockIndex,
887 index64: u64,
888 value: &validate::Value,
889 op: Op,
890 ) -> Result<(), Error> {
891 if let Some(mut property) = self.properties.get_mut(&id) {
892 let index = index64 as usize;
893 let array_len = match &property {
895 Property { payload: Payload::IntArray(numbers, ArrayFormat::Default), .. } => {
896 numbers.len()
897 }
898 Property { payload: Payload::UintArray(numbers, ArrayFormat::Default), .. } => {
899 numbers.len()
900 }
901 Property {
902 payload: Payload::DoubleArray(numbers, ArrayFormat::Default), ..
903 } => numbers.len(),
904 Property { payload: Payload::StringArray(values), .. } => values.len(),
905 unexpected => {
906 return Err(format_err!(
907 "Bad types {:?} trying to set number of elements",
908 unexpected
909 ))
910 }
911 };
912 if index >= array_len {
913 return Ok(());
914 }
915 match (&mut property, value) {
916 (Property { payload: Payload::IntArray(numbers, _), .. }, Value::IntT(value)) => {
917 numbers[index] = (op.int)(numbers[index], *value);
918 }
919 (Property { payload: Payload::UintArray(numbers, _), .. }, Value::UintT(value)) => {
920 numbers[index] = (op.uint)(numbers[index], *value);
921 }
922 (
923 Property { payload: Payload::DoubleArray(numbers, _), .. },
924 Value::DoubleT(value),
925 ) => {
926 numbers[index] = (op.double)(numbers[index], *value);
927 }
928 (Property { payload: Payload::StringArray(values), .. }, Value::StringT(value)) => {
929 values[index] = (op.string)(values[index].clone(), value.clone());
930 }
931 unexpected => {
932 return Err(format_err!("Type mismatch {:?} trying to set value", unexpected))
933 }
934 }
935 } else {
936 return Err(format_err!("Tried to {} number on nonexistent property {}", op.name, id));
937 }
938 Ok(())
939 }
940
941 fn set_string(&mut self, id: BlockIndex, value: &String) -> Result<(), Error> {
942 if let Some(property) = self.properties.get_mut(&id) {
943 match &property {
944 Property { payload: Payload::String(_), .. } => {
945 property.payload = Payload::String(value.to_owned())
946 }
947 unexpected => {
948 return Err(format_err!("Bad type {:?} trying to set string", unexpected))
949 }
950 }
951 } else {
952 return Err(format_err!("Tried to set string on nonexistent property {}", id));
953 }
954 Ok(())
955 }
956
957 fn set_bytes(&mut self, id: BlockIndex, value: &Vec<u8>) -> Result<(), Error> {
958 if let Some(property) = self.properties.get_mut(&id) {
959 match &property {
960 Property { payload: Payload::Bytes(_), .. } => {
961 property.payload = Payload::Bytes(value.to_owned())
962 }
963 unexpected => {
964 return Err(format_err!("Bad type {:?} trying to set bytes", unexpected))
965 }
966 }
967 } else {
968 return Err(format_err!("Tried to set bytes on nonexistent property {}", id));
969 }
970 Ok(())
971 }
972
973 fn set_bool(&mut self, id: BlockIndex, value: bool) -> Result<(), Error> {
974 if let Some(property) = self.properties.get_mut(&id) {
975 match &property {
976 Property { payload: Payload::Bool(_), .. } => {
977 property.payload = Payload::Bool(value)
978 }
979 unexpected => {
980 return Err(format_err!("Bad type {:?} trying to set bool", unexpected))
981 }
982 }
983 } else {
984 return Err(format_err!("Tried to set bool on nonexistent property {}", id));
985 }
986 Ok(())
987 }
988
989 fn add_lazy_node(
990 &mut self,
991 parent: BlockIndex,
992 id: BlockIndex,
993 name: &str,
994 disposition: &validate::LinkDisposition,
995 actions: &Vec<validate::Action>,
996 ) -> Result<(), Error> {
997 let mut parsed_data = Data::new();
998 parsed_data.apply_multiple(actions)?;
999 self.add_property(
1000 parent,
1001 id,
1002 name,
1003 Payload::Link {
1004 disposition: match disposition {
1005 validate::LinkDisposition::Child => LinkNodeDisposition::Child,
1006 validate::LinkDisposition::Inline => LinkNodeDisposition::Inline,
1007 },
1008 parsed_data,
1009 },
1010 )?;
1011 Ok(())
1012 }
1013
1014 fn clone_generic(&self) -> Self {
1019 let mut clone = self.clone();
1020
1021 let mut to_remove = vec![];
1022 let mut names = HashSet::new();
1023
1024 clone.properties = clone
1025 .properties
1026 .into_iter()
1027 .filter_map(|(id, mut v)| {
1028 if !names.insert((v.parent, v.name.clone())) {
1034 to_remove.push((v.parent, id));
1035 None
1036 } else {
1037 v.payload = v.payload.to_generic();
1038 Some((id, v))
1039 }
1040 })
1041 .collect::<HashMap<BlockIndex, Property>>();
1042
1043 for (parent, id) in to_remove {
1045 if clone.nodes.contains_key(&parent) {
1046 clone.nodes.get_mut(&parent).unwrap().properties.remove(&id);
1047 }
1048 }
1049
1050 clone
1051 }
1052
1053 pub fn compare_to_json(&self, other: &Data, diff_type: DiffType) -> Result<(), Error> {
1058 self.clone_generic().compare(other, diff_type)
1059 }
1060
1061 pub fn compare(&self, other: &Data, diff_type: DiffType) -> Result<(), Error> {
1065 let self_string = self.to_string();
1066 let other_string = other.to_string();
1067
1068 let difference::Changeset { diffs, distance, .. } =
1069 difference::Changeset::new(&self_string, &other_string, "\n");
1070
1071 if distance == 0 {
1072 Ok(())
1073 } else {
1074 let diff_lines = diffs
1075 .into_iter()
1076 .flat_map(|diff| {
1077 let (prefix, val) = match diff {
1078 difference::Difference::Same(val) => (" same", val),
1080 difference::Difference::Add(val) => ("other", val),
1081 difference::Difference::Rem(val) => ("local", val),
1082 };
1083 val.split("\n")
1084 .map(|line| format!("{}: {:?}", prefix, line))
1085 .collect::<Vec<_>>()
1086 })
1087 .collect::<Vec<_>>();
1088
1089 match diff_type {
1090 DiffType::Full => Err(format_err!(
1091 "Trees differ:\n-- LOCAL --\n{}\n-- OTHER --\n{}",
1092 self_string,
1093 other_string
1094 )),
1095 DiffType::Diff => {
1096 Err(format_err!("Trees differ:\n-- DIFF --\n{}", diff_lines.join("\n")))
1097 }
1098 DiffType::Both => Err(format_err!(
1099 "Trees differ:\n-- LOCAL --\n{}\n-- OTHER --\n{}\n-- DIFF --\n{}",
1100 self_string,
1101 other_string,
1102 diff_lines.join("\n")
1103 )),
1104 }
1105 }
1106 }
1107
1108 pub fn new() -> Data {
1111 let mut ret = Data {
1112 nodes: HashMap::new(),
1113 properties: HashMap::new(),
1114 tombstone_nodes: HashSet::new(),
1115 tombstone_properties: HashSet::new(),
1116 };
1117 ret.nodes.insert(
1118 BlockIndex::ROOT,
1119 Node {
1120 name: ROOT_NAME.into(),
1121 parent: BlockIndex::ROOT,
1122 children: HashSet::new(),
1123 properties: HashSet::new(),
1124 },
1125 );
1126 ret
1127 }
1128
1129 fn build(nodes: HashMap<BlockIndex, Node>, properties: HashMap<BlockIndex, Property>) -> Data {
1130 Data {
1131 nodes,
1132 properties,
1133 tombstone_nodes: HashSet::new(),
1134 tombstone_properties: HashSet::new(),
1135 }
1136 }
1137
1138 fn apply_multiple(&mut self, actions: &Vec<validate::Action>) -> Result<(), Error> {
1139 for action in actions {
1140 self.apply(action)?;
1141 }
1142 Ok(())
1143 }
1144
1145 pub fn remove_tree(&mut self, name: &str) {
1147 if self.is_empty() {
1148 return;
1149 }
1150 let mut target_node_id = None;
1151 let root = &self.nodes[&BlockIndex::ROOT];
1152 for child_id in &root.children {
1153 if let Some(node) = self.nodes.get(child_id) {
1154 if node.name == name {
1155 target_node_id = Some(*child_id);
1156 break;
1157 }
1158 }
1159 }
1160 if let Some(id) = target_node_id {
1161 self.delete_node(id).unwrap();
1162 }
1163 }
1164
1165 pub fn is_empty(&self) -> bool {
1167 if !self.nodes.contains_key(&BlockIndex::ROOT) {
1168 return true;
1170 }
1171
1172 if self.properties.len() == 0 {
1175 return true;
1176 }
1177
1178 let root = &self.nodes[&BlockIndex::ROOT];
1180 root.children.is_subset(&self.tombstone_nodes) && root.properties.len() == 0
1181 }
1182}
1183
1184impl fmt::Display for Data {
1185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1186 let s = if let Some(node) = self.nodes.get(&BlockIndex::ROOT) {
1187 node.to_string("", self, false)
1188 } else {
1189 "No root node; internal error\n".to_owned()
1190 };
1191 write!(f, "{s}",)
1192 }
1193}
1194
1195impl From<DiagnosticsHierarchy> for Data {
1196 fn from(hierarchy: DiagnosticsHierarchy) -> Self {
1197 let mut nodes = HashMap::new();
1198 let mut properties = HashMap::new();
1199
1200 nodes.insert(
1201 0.into(),
1202 Node {
1203 name: hierarchy.name.clone(),
1204 parent: 0.into(),
1205 children: HashSet::new(),
1206 properties: HashSet::new(),
1207 },
1208 );
1209
1210 let mut queue = vec![(0.into(), &hierarchy)];
1211 let mut next_id = BlockIndex::new(1);
1212
1213 while let Some((id, value)) = queue.pop() {
1214 for node in value.children.iter() {
1215 let child_id = next_id;
1216 next_id += 1;
1217 nodes.insert(
1218 child_id,
1219 Node {
1220 name: node.name.clone(),
1221 parent: id,
1222 children: HashSet::new(),
1223 properties: HashSet::new(),
1224 },
1225 );
1226 nodes.get_mut(&id).expect("parent must exist").children.insert(child_id);
1227 queue.push((child_id, node));
1228 }
1229 for property in value.properties.iter() {
1230 let prop_id = next_id;
1231 next_id += 1;
1232
1233 let (name, payload) = match property.clone() {
1234 iProperty::String(n, v) => (n, Payload::String(v).to_generic()),
1235 iProperty::Bytes(n, v) => (n, Payload::Bytes(v).to_generic()),
1236 iProperty::Int(n, v) => (n, Payload::Int(v).to_generic()),
1237 iProperty::Uint(n, v) => (n, Payload::Uint(v).to_generic()),
1238 iProperty::Double(n, v) => (n, Payload::Double(v).to_generic()),
1239 iProperty::Bool(n, v) => (n, Payload::Bool(v).to_generic()),
1240 iProperty::IntArray(n, content) => (n, content.to_generic()),
1241 iProperty::UintArray(n, content) => (n, content.to_generic()),
1242 iProperty::DoubleArray(n, content) => (n, content.to_generic()),
1243 iProperty::StringList(n, content) => {
1244 (n, Payload::StringArray(content).to_generic())
1245 }
1246 };
1247
1248 properties.insert(prop_id, Property { name, id: prop_id, parent: id, payload });
1249 nodes.get_mut(&id).expect("parent must exist").properties.insert(prop_id);
1250 }
1251 }
1252
1253 Data {
1254 nodes,
1255 properties,
1256 tombstone_nodes: HashSet::new(),
1257 tombstone_properties: HashSet::new(),
1258 }
1259 }
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264 use super::*;
1265 use crate::*;
1266 use fidl_diagnostics_validate::{ValueType, ROOT_ID};
1267 use fuchsia_inspect::reader::ArrayContent as iArrayContent;
1268 use inspect_format::BlockType;
1269 use num_derive::{FromPrimitive, ToPrimitive};
1270
1271 #[fuchsia::test]
1272 fn test_basic_data_strings() -> Result<(), Error> {
1273 let mut info = Data::new();
1274 assert_eq!(info.to_string(), "root ->");
1275
1276 info.apply(&create_node!(parent: ROOT_ID, id: 1, name: "foo"))?;
1277 assert_eq!(info.to_string(), "root ->\n> foo ->");
1278
1279 info.apply(&delete_node!( id: 1 ))?;
1280 assert_eq!(info.to_string(), "root ->");
1281
1282 Ok(())
1283 }
1284
1285 const EXPECTED_HIERARCHY: &str = r#"root ->
1286> double: GenericNumber("2.5")
1287> int: GenericNumber("-5")
1288> string: String("value")
1289> uint: GenericNumber("10")
1290> child ->
1291> > bytes: String("b64:AQI=")
1292> > grandchild ->
1293> > > double_a: GenericArray(["0.5", "1"])
1294> > > double_eh: GenericExponentialHistogram(["0.5", "0.5", "2", "1", "2", "3"])
1295> > > double_lh: GenericLinearHistogram(["0.5", "0.5", "1", "2", "3"])
1296> > > int_a: GenericArray(["-1", "-2"])
1297> > > int_eh: GenericExponentialHistogram(["-1", "1", "2", "1", "2", "3"])
1298> > > int_lh: GenericLinearHistogram(["-1", "1", "1", "2", "3"])
1299> > > uint_a: GenericArray(["1", "2"])
1300> > > uint_eh: GenericExponentialHistogram(["1", "1", "2", "1", "2", "3"])
1301> > > uint_lh: GenericLinearHistogram(["1", "1", "1", "2", "3"])"#;
1302
1303 #[derive(Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
1306 enum ArrayType {
1307 Int = 4,
1308 Uint = 5,
1309 Double = 6,
1310 }
1311
1312 #[fuchsia::test]
1313 fn test_parse_hierarchy() -> Result<(), Error> {
1314 let hierarchy = DiagnosticsHierarchy {
1315 name: "root".to_string(),
1316 properties: vec![
1317 iProperty::String("string".to_string(), "value".to_string()),
1318 iProperty::Uint("uint".to_string(), 10u64),
1319 iProperty::Int("int".to_string(), -5i64),
1320 iProperty::Double("double".to_string(), 2.5f64),
1321 ],
1322 children: vec![DiagnosticsHierarchy {
1323 name: "child".to_string(),
1324 properties: vec![iProperty::Bytes("bytes".to_string(), vec![1u8, 2u8])],
1325 children: vec![DiagnosticsHierarchy {
1326 name: "grandchild".to_string(),
1327 properties: vec![
1328 iProperty::UintArray(
1329 "uint_a".to_string(),
1330 iArrayContent::Values(vec![1, 2]),
1331 ),
1332 iProperty::IntArray(
1333 "int_a".to_string(),
1334 iArrayContent::Values(vec![-1i64, -2i64]),
1335 ),
1336 iProperty::DoubleArray(
1337 "double_a".to_string(),
1338 iArrayContent::Values(vec![0.5, 1.0]),
1339 ),
1340 iProperty::UintArray(
1341 "uint_lh".to_string(),
1342 iArrayContent::new(vec![1, 1, 1, 2, 3], ArrayFormat::LinearHistogram)
1343 .unwrap(),
1344 ),
1345 iProperty::IntArray(
1346 "int_lh".to_string(),
1347 iArrayContent::new(
1348 vec![-1i64, 1, 1, 2, 3],
1349 ArrayFormat::LinearHistogram,
1350 )
1351 .unwrap(),
1352 ),
1353 iProperty::DoubleArray(
1354 "double_lh".to_string(),
1355 iArrayContent::new(
1356 vec![0.5, 0.5, 1.0, 2.0, 3.0],
1357 ArrayFormat::LinearHistogram,
1358 )
1359 .unwrap(),
1360 ),
1361 iProperty::UintArray(
1362 "uint_eh".to_string(),
1363 iArrayContent::new(
1364 vec![1, 1, 2, 1, 2, 3],
1365 ArrayFormat::ExponentialHistogram,
1366 )
1367 .unwrap(),
1368 ),
1369 iProperty::IntArray(
1370 "int_eh".to_string(),
1371 iArrayContent::new(
1372 vec![-1i64, 1, 2, 1, 2, 3],
1373 ArrayFormat::ExponentialHistogram,
1374 )
1375 .unwrap(),
1376 ),
1377 iProperty::DoubleArray(
1378 "double_eh".to_string(),
1379 iArrayContent::new(
1380 vec![0.5, 0.5, 2.0, 1.0, 2.0, 3.0],
1381 ArrayFormat::ExponentialHistogram,
1382 )
1383 .unwrap(),
1384 ),
1385 ],
1386 children: vec![],
1387 missing: vec![],
1388 }],
1389 missing: vec![],
1390 }],
1391 missing: vec![],
1392 };
1393
1394 let data: Data = hierarchy.into();
1395 assert_eq!(EXPECTED_HIERARCHY, data.to_string());
1396
1397 Ok(())
1398 }
1399
1400 #[fuchsia::test]
1402 fn test_creation_deletion() -> Result<(), Error> {
1403 let mut info = Data::new();
1404 assert!(!info.to_string().contains("child ->"));
1405 info.apply(&create_node!(parent: ROOT_ID, id: 1, name: "child"))?;
1406 assert!(info.to_string().contains("child ->"));
1407
1408 info.apply(&create_node!(parent: 1, id: 2, name: "grandchild"))?;
1409 assert!(
1410 info.to_string().contains("grandchild ->") && info.to_string().contains("child ->")
1411 );
1412
1413 info.apply(
1414 &create_numeric_property!(parent: ROOT_ID, id: 3, name: "int-42", value: Value::IntT(-42)),
1415 )?;
1416
1417 assert!(info.to_string().contains("int-42: Int(-42)")); info.apply(&create_string_property!(parent: 1, id: 4, name: "stringfoo", value: "foo"))?;
1419 assert_eq!(
1420 info.to_string(),
1421 "root ->\n> int-42: Int(-42)\n> child ->\
1422 \n> > stringfoo: String(\"foo\")\n> > grandchild ->"
1423 );
1424
1425 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 5, name: "uint", value: Value::UintT(1024)))?;
1426 assert!(info.to_string().contains("uint: Uint(1024)"));
1427
1428 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 6, name: "frac", value: Value::DoubleT(0.5)))?;
1429 assert!(info.to_string().contains("frac: Double(0.5)"));
1430
1431 info.apply(
1432 &create_bytes_property!(parent: ROOT_ID, id: 7, name: "bytes", value: vec!(1u8, 2u8)),
1433 )?;
1434 assert!(info.to_string().contains("bytes: Bytes([1, 2])"));
1435
1436 info.apply(&create_array_property!(parent: ROOT_ID, id: 8, name: "i_ntarr", slots: 1, type: ValueType::Int))?;
1437 assert!(info.to_string().contains("i_ntarr: IntArray([0], Default)"));
1438
1439 info.apply(&create_array_property!(parent: ROOT_ID, id: 9, name: "u_intarr", slots: 2, type: ValueType::Uint))?;
1440 assert!(info.to_string().contains("u_intarr: UintArray([0, 0], Default)"));
1441
1442 info.apply(&create_array_property!(parent: ROOT_ID, id: 10, name: "dblarr", slots: 3, type: ValueType::Double))?;
1443 assert!(info.to_string().contains("dblarr: DoubleArray([0.0, 0.0, 0.0], Default)"));
1444
1445 info.apply(&create_linear_histogram!(parent: ROOT_ID, id: 11, name: "ILhist", floor: 12,
1446 step_size: 3, buckets: 2, type: IntT))?;
1447 assert!(info
1448 .to_string()
1449 .contains("ILhist: IntArray([12, 3, 0, 0, 0, 0], LinearHistogram)"));
1450
1451 info.apply(&create_linear_histogram!(parent: ROOT_ID, id: 12, name: "ULhist", floor: 34,
1452 step_size: 5, buckets: 2, type: UintT))?;
1453 assert!(info
1454 .to_string()
1455 .contains("ULhist: UintArray([34, 5, 0, 0, 0, 0], LinearHistogram)"));
1456
1457 info.apply(
1458 &create_linear_histogram!(parent: ROOT_ID, id: 13, name: "DLhist", floor: 56.0,
1459 step_size: 7.0, buckets: 2, type: DoubleT),
1460 )?;
1461 assert!(info
1462 .to_string()
1463 .contains("DLhist: DoubleArray([56.0, 7.0, 0.0, 0.0, 0.0, 0.0], LinearHistogram)"));
1464
1465 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 14, name: "IEhist",
1466 floor: 12, initial_step: 3, step_multiplier: 5, buckets: 2, type: IntT))?;
1467 assert!(info
1468 .to_string()
1469 .contains("IEhist: IntArray([12, 3, 5, 0, 0, 0, 0], ExponentialHistogram)"));
1470
1471 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 15, name: "UEhist",
1472 floor: 34, initial_step: 9, step_multiplier: 6, buckets: 2, type: UintT))?;
1473 assert!(info
1474 .to_string()
1475 .contains("UEhist: UintArray([34, 9, 6, 0, 0, 0, 0], ExponentialHistogram)"));
1476
1477 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 16, name: "DEhist",
1478 floor: 56.0, initial_step: 27.0, step_multiplier: 7.0, buckets: 2, type: DoubleT))?;
1479 assert!(info.to_string().contains(
1480 "DEhist: DoubleArray([56.0, 27.0, 7.0, 0.0, 0.0, 0.0, 0.0], ExponentialHistogram)"
1481 ));
1482
1483 info.apply(&create_bool_property!(parent: ROOT_ID, id: 17, name: "bool", value: true))?;
1484 assert!(info.to_string().contains("bool: Bool(true)"));
1485
1486 info.apply(&delete_property!(id: 3))?;
1487 assert!(!info.to_string().contains("int-42") && info.to_string().contains("stringfoo"));
1488 info.apply(&delete_property!(id: 4))?;
1489 assert!(!info.to_string().contains("stringfoo"));
1490 info.apply(&delete_property!(id: 5))?;
1491 assert!(!info.to_string().contains("uint"));
1492 info.apply(&delete_property!(id: 6))?;
1493 assert!(!info.to_string().contains("frac"));
1494 info.apply(&delete_property!(id: 7))?;
1495 assert!(!info.to_string().contains("bytes"));
1496 info.apply(&delete_property!(id: 8))?;
1497 assert!(!info.to_string().contains("i_ntarr"));
1498 info.apply(&delete_property!(id: 9))?;
1499 assert!(!info.to_string().contains("u_intarr"));
1500 info.apply(&delete_property!(id: 10))?;
1501 assert!(!info.to_string().contains("dblarr"));
1502 info.apply(&delete_property!(id: 11))?;
1503 assert!(!info.to_string().contains("ILhist"));
1504 info.apply(&delete_property!(id: 12))?;
1505 assert!(!info.to_string().contains("ULhist"));
1506 info.apply(&delete_property!(id: 13))?;
1507 assert!(!info.to_string().contains("DLhist"));
1508 info.apply(&delete_property!(id: 14))?;
1509 assert!(!info.to_string().contains("IEhist"));
1510 info.apply(&delete_property!(id: 15))?;
1511 assert!(!info.to_string().contains("UEhist"));
1512 info.apply(&delete_property!(id: 16))?;
1513 assert!(!info.to_string().contains("DEhist"));
1514 info.apply(&delete_property!(id: 17))?;
1515 assert!(!info.to_string().contains("bool"));
1516 info.apply(&delete_node!(id:2))?;
1517 assert!(!info.to_string().contains("grandchild") && info.to_string().contains("child"));
1518 info.apply(&delete_node!( id: 1 ))?;
1519 assert_eq!(info.to_string(), "root ->");
1520 Ok(())
1521 }
1522
1523 #[fuchsia::test]
1524 fn test_basic_int_ops() -> Result<(), Error> {
1525 let mut info = Data::new();
1526 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 3, name: "value",
1527 value: Value::IntT(-42)))?;
1528 assert!(info.apply(&add_number!(id: 3, value: Value::IntT(3))).is_ok());
1529 assert!(info.to_string().contains("value: Int(-39)"));
1530 assert!(info.apply(&add_number!(id: 3, value: Value::UintT(3))).is_err());
1531 assert!(info.apply(&add_number!(id: 3, value: Value::DoubleT(3.0))).is_err());
1532 assert!(info.to_string().contains("value: Int(-39)"));
1533 assert!(info.apply(&subtract_number!(id: 3, value: Value::IntT(5))).is_ok());
1534 assert!(info.to_string().contains("value: Int(-44)"));
1535 assert!(info.apply(&subtract_number!(id: 3, value: Value::UintT(5))).is_err());
1536 assert!(info.apply(&subtract_number!(id: 3, value: Value::DoubleT(5.0))).is_err());
1537 assert!(info.to_string().contains("value: Int(-44)"));
1538 assert!(info.apply(&set_number!(id: 3, value: Value::IntT(22))).is_ok());
1539 assert!(info.to_string().contains("value: Int(22)"));
1540 assert!(info.apply(&set_number!(id: 3, value: Value::UintT(23))).is_err());
1541 assert!(info.apply(&set_number!(id: 3, value: Value::DoubleT(24.0))).is_err());
1542 assert!(info.to_string().contains("value: Int(22)"));
1543 Ok(())
1544 }
1545
1546 #[fuchsia::test]
1547 fn test_array_string_ops() -> Result<(), Error> {
1548 let mut info = Data::new();
1549 info.apply(&create_array_property!(parent: ROOT_ID, id: 3, name: "value", slots: 3,
1550 type: ValueType::String))?;
1551 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::IntT(3))).is_err());
1552 assert!(info.to_string().contains(r#"value: StringArray(["", "", ""])"#));
1553 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::UintT(3))).is_err());
1554 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::DoubleT(3.0))).is_err());
1555 assert!(info.to_string().contains(r#"value: StringArray(["", "", ""]"#));
1556 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::IntT(5))).is_err());
1557 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::UintT(5))).is_err());
1558 assert!(info
1559 .apply(&array_subtract!(id: 3, index: 2,
1560 value: Value::DoubleT(5.0)))
1561 .is_err());
1562 assert!(info.to_string().contains(r#"value: StringArray(["", "", ""])"#));
1563 assert!(info
1564 .apply(&array_set!(id: 3, index: 1, value: Value::StringT("data".into())))
1565 .is_ok());
1566 assert!(info.to_string().contains(r#"value: StringArray(["", "data", ""])"#));
1567 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::UintT(23))).is_err());
1568 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::DoubleT(24.0))).is_err());
1569 assert!(info.to_string().contains(r#"value: StringArray(["", "data", ""])"#));
1570 let long: String = (0..3000).map(|_| ".").collect();
1571 let expected = format!(r#"value: StringArray(["{}", "data", ""])"#, long.clone());
1572 assert!(info.apply(&array_set!(id: 3, index: 0, value: Value::StringT(long))).is_ok());
1573 assert!(info.to_string().contains(&expected));
1574 Ok(())
1575 }
1576
1577 #[fuchsia::test]
1578 fn test_array_int_ops() -> Result<(), Error> {
1579 let mut info = Data::new();
1580 info.apply(&create_array_property!(parent: ROOT_ID, id: 3, name: "value", slots: 3,
1581 type: ValueType::Int))?;
1582 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::IntT(3))).is_ok());
1583 assert!(info.to_string().contains("value: IntArray([0, 3, 0], Default)"));
1584 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::UintT(3))).is_err());
1585 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::DoubleT(3.0))).is_err());
1586 assert!(info.to_string().contains("value: IntArray([0, 3, 0], Default)"));
1587 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::IntT(5))).is_ok());
1588 assert!(info.to_string().contains("value: IntArray([0, 3, -5], Default)"));
1589 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::UintT(5))).is_err());
1590 assert!(info
1591 .apply(&array_subtract!(id: 3, index: 2,
1592 value: Value::DoubleT(5.0)))
1593 .is_err());
1594 assert!(info.to_string().contains("value: IntArray([0, 3, -5], Default)"));
1595 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::IntT(22))).is_ok());
1596 assert!(info.to_string().contains("value: IntArray([0, 22, -5], Default)"));
1597 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::UintT(23))).is_err());
1598 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::DoubleT(24.0))).is_err());
1599 assert!(info.to_string().contains("value: IntArray([0, 22, -5], Default)"));
1600 Ok(())
1601 }
1602
1603 #[fuchsia::test]
1604 fn test_linear_int_ops() -> Result<(), Error> {
1605 let mut info = Data::new();
1606 info.apply(&create_linear_histogram!(parent: ROOT_ID, id: 3, name: "value",
1607 floor: 4, step_size: 2, buckets: 2, type: IntT))?;
1608 assert!(info.to_string().contains("value: IntArray([4, 2, 0, 0, 0, 0], LinearHistogram)"));
1609 assert!(info.apply(&insert!(id: 3, value: Value::IntT(4))).is_ok());
1610 assert!(info.to_string().contains("value: IntArray([4, 2, 0, 1, 0, 0], LinearHistogram)"));
1611 assert!(info.apply(&insert!(id: 3, value: Value::IntT(5))).is_ok());
1612 assert!(info.to_string().contains("value: IntArray([4, 2, 0, 2, 0, 0], LinearHistogram)"));
1613 assert!(info.apply(&insert!(id: 3, value: Value::IntT(6))).is_ok());
1614 assert!(info.to_string().contains("value: IntArray([4, 2, 0, 2, 1, 0], LinearHistogram)"));
1615 assert!(info.apply(&insert!(id: 3, value: Value::IntT(8))).is_ok());
1616 assert!(info.to_string().contains("value: IntArray([4, 2, 0, 2, 1, 1], LinearHistogram)"));
1617 assert!(info.apply(&insert!(id: 3, value: Value::IntT(i64::MAX))).is_ok());
1618 assert!(info.to_string().contains("value: IntArray([4, 2, 0, 2, 1, 2], LinearHistogram)"));
1619 assert!(info.apply(&insert!(id: 3, value: Value::IntT(0))).is_ok());
1620 assert!(info.to_string().contains("value: IntArray([4, 2, 1, 2, 1, 2], LinearHistogram)"));
1621 assert!(info.apply(&insert!(id: 3, value: Value::IntT(i64::MIN))).is_ok());
1622 assert!(info.to_string().contains("value: IntArray([4, 2, 2, 2, 1, 2], LinearHistogram)"));
1623 assert!(info.apply(&insert!(id: 3, value: Value::UintT(0))).is_err());
1624 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(0.0))).is_err());
1625 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::IntT(222))).is_err());
1626 assert!(info.to_string().contains("value: IntArray([4, 2, 2, 2, 1, 2], LinearHistogram)"));
1627 assert!(info.apply(&insert_multiple!(id: 3, value: Value::IntT(7), count: 4)).is_ok());
1628 assert!(info.to_string().contains("value: IntArray([4, 2, 2, 2, 5, 2], LinearHistogram)"));
1629 Ok(())
1630 }
1631
1632 #[fuchsia::test]
1633 fn test_exponential_int_ops() -> Result<(), Error> {
1634 let mut info = Data::new();
1635 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 3, name: "value",
1637 floor: 5, initial_step: 2,
1638 step_multiplier: 4, buckets: 2, type: IntT))?;
1639 assert!(info
1640 .to_string()
1641 .contains("value: IntArray([5, 2, 4, 0, 0, 0, 0], ExponentialHistogram)"));
1642 assert!(info.apply(&insert!(id: 3, value: Value::IntT(5))).is_ok());
1643 assert!(info
1644 .to_string()
1645 .contains("value: IntArray([5, 2, 4, 0, 1, 0, 0], ExponentialHistogram)"));
1646 assert!(info.apply(&insert!(id: 3, value: Value::IntT(6))).is_ok());
1647 assert!(info
1648 .to_string()
1649 .contains("value: IntArray([5, 2, 4, 0, 2, 0, 0], ExponentialHistogram)"));
1650 assert!(info.apply(&insert!(id: 3, value: Value::IntT(7))).is_ok());
1651 assert!(info
1652 .to_string()
1653 .contains("value: IntArray([5, 2, 4, 0, 2, 1, 0], ExponentialHistogram)"));
1654 assert!(info.apply(&insert!(id: 3, value: Value::IntT(13))).is_ok());
1655 assert!(info
1656 .to_string()
1657 .contains("value: IntArray([5, 2, 4, 0, 2, 1, 1], ExponentialHistogram)"));
1658 assert!(info.apply(&insert!(id: 3, value: Value::IntT(i64::MAX))).is_ok());
1659 assert!(info
1660 .to_string()
1661 .contains("value: IntArray([5, 2, 4, 0, 2, 1, 2], ExponentialHistogram)"));
1662 assert!(info.apply(&insert!(id: 3, value: Value::IntT(0))).is_ok());
1663 assert!(info
1664 .to_string()
1665 .contains("value: IntArray([5, 2, 4, 1, 2, 1, 2], ExponentialHistogram)"));
1666 assert!(info.apply(&insert!(id: 3, value: Value::IntT(i64::MIN))).is_ok());
1667 assert!(info
1668 .to_string()
1669 .contains("value: IntArray([5, 2, 4, 2, 2, 1, 2], ExponentialHistogram)"));
1670 assert!(info.apply(&insert!(id: 3, value: Value::UintT(0))).is_err());
1671 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(0.0))).is_err());
1672 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::IntT(222))).is_err());
1673 assert!(info
1674 .to_string()
1675 .contains("value: IntArray([5, 2, 4, 2, 2, 1, 2], ExponentialHistogram)"));
1676 assert!(info.apply(&insert_multiple!(id: 3, value: Value::IntT(12), count: 4)).is_ok());
1677 assert!(info
1678 .to_string()
1679 .contains("value: IntArray([5, 2, 4, 2, 2, 5, 2], ExponentialHistogram)"));
1680 Ok(())
1681 }
1682
1683 #[fuchsia::test]
1684 fn test_array_out_of_bounds_nop() -> Result<(), Error> {
1685 let mut info = Data::new();
1687 info.apply(&create_array_property!(parent: ROOT_ID, id: 3, name: "value", slots: 3,
1688 type: ValueType::Int))?;
1689 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::IntT(3))).is_ok());
1690 assert!(info.to_string().contains("value: IntArray([0, 3, 0], Default)"));
1691 assert!(info.apply(&array_add!(id: 3, index: 3, value: Value::IntT(3))).is_ok());
1692 assert!(info.apply(&array_add!(id: 3, index: 6, value: Value::IntT(3))).is_ok());
1693 assert!(info.apply(&array_add!(id: 3, index: 12345, value: Value::IntT(3))).is_ok());
1694 assert!(info.to_string().contains("value: IntArray([0, 3, 0], Default)"));
1695 Ok(())
1696 }
1697
1698 #[fuchsia::test]
1699 fn test_basic_uint_ops() -> Result<(), Error> {
1700 let mut info = Data::new();
1701 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 3, name: "value",
1702 value: Value::UintT(42)))?;
1703 assert!(info.apply(&add_number!(id: 3, value: Value::UintT(3))).is_ok());
1704 assert!(info.to_string().contains("value: Uint(45)"));
1705 assert!(info.apply(&add_number!(id: 3, value: Value::IntT(3))).is_err());
1706 assert!(info.apply(&add_number!(id: 3, value: Value::DoubleT(3.0))).is_err());
1707 assert!(info.to_string().contains("value: Uint(45)"));
1708 assert!(info.apply(&subtract_number!(id: 3, value: Value::UintT(5))).is_ok());
1709 assert!(info.to_string().contains("value: Uint(40)"));
1710 assert!(info.apply(&subtract_number!(id: 3, value: Value::IntT(5))).is_err());
1711 assert!(info.apply(&subtract_number!(id: 3, value: Value::DoubleT(5.0))).is_err());
1712 assert!(info.to_string().contains("value: Uint(40)"));
1713 assert!(info.apply(&set_number!(id: 3, value: Value::UintT(22))).is_ok());
1714 assert!(info.to_string().contains("value: Uint(22)"));
1715 assert!(info.apply(&set_number!(id: 3, value: Value::IntT(23))).is_err());
1716 assert!(info.apply(&set_number!(id: 3, value: Value::DoubleT(24.0))).is_err());
1717 assert!(info.to_string().contains("value: Uint(22)"));
1718 Ok(())
1719 }
1720
1721 #[fuchsia::test]
1722 fn test_array_uint_ops() -> Result<(), Error> {
1723 let mut info = Data::new();
1724 info.apply(&create_array_property!(parent: ROOT_ID, id: 3, name: "value", slots: 3,
1725 type: ValueType::Uint))?;
1726 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::UintT(3))).is_ok());
1727 assert!(info.to_string().contains("value: UintArray([0, 3, 0], Default)"));
1728 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::IntT(3))).is_err());
1729 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::DoubleT(3.0))).is_err());
1730 assert!(info.to_string().contains("value: UintArray([0, 3, 0], Default)"));
1731 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::UintT(22))).is_ok());
1732 assert!(info.to_string().contains("value: UintArray([0, 22, 0], Default)"));
1733 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::IntT(23))).is_err());
1734 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::DoubleT(24.0))).is_err());
1735 assert!(info.to_string().contains("value: UintArray([0, 22, 0], Default)"));
1736 assert!(info.apply(&array_subtract!(id: 3, index: 1, value: Value::UintT(5))).is_ok());
1737 assert!(info.to_string().contains("value: UintArray([0, 17, 0], Default)"));
1738 assert!(info.apply(&array_subtract!(id: 3, index: 1, value: Value::IntT(5))).is_err());
1739 assert!(info.apply(&array_subtract!(id: 3, index: 1, value: Value::DoubleT(5.0))).is_err());
1740 assert!(info.to_string().contains("value: UintArray([0, 17, 0], Default)"));
1741 Ok(())
1742 }
1743
1744 #[fuchsia::test]
1745 fn test_linear_uint_ops() -> Result<(), Error> {
1746 let mut info = Data::new();
1747 info.apply(&create_linear_histogram!(parent: ROOT_ID, id: 3, name: "value",
1748 floor: 4, step_size: 2, buckets: 2, type: UintT))?;
1749 assert!(info.to_string().contains("value: UintArray([4, 2, 0, 0, 0, 0], LinearHistogram)"));
1750 assert!(info.apply(&insert!(id: 3, value: Value::UintT(4))).is_ok());
1751 assert!(info.to_string().contains("value: UintArray([4, 2, 0, 1, 0, 0], LinearHistogram)"));
1752 assert!(info.apply(&insert!(id: 3, value: Value::UintT(5))).is_ok());
1753 assert!(info.to_string().contains("value: UintArray([4, 2, 0, 2, 0, 0], LinearHistogram)"));
1754 assert!(info.apply(&insert!(id: 3, value: Value::UintT(6))).is_ok());
1755 assert!(info.to_string().contains("value: UintArray([4, 2, 0, 2, 1, 0], LinearHistogram)"));
1756 assert!(info.apply(&insert!(id: 3, value: Value::UintT(8))).is_ok());
1757 assert!(info.to_string().contains("value: UintArray([4, 2, 0, 2, 1, 1], LinearHistogram)"));
1758 assert!(info.apply(&insert!(id: 3, value: Value::UintT(u64::MAX))).is_ok());
1759 assert!(info.to_string().contains("value: UintArray([4, 2, 0, 2, 1, 2], LinearHistogram)"));
1760 assert!(info.apply(&insert!(id: 3, value: Value::UintT(0))).is_ok());
1761 assert!(info.to_string().contains("value: UintArray([4, 2, 1, 2, 1, 2], LinearHistogram)"));
1762 assert!(info.apply(&insert!(id: 3, value: Value::IntT(0))).is_err());
1763 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(0.0))).is_err());
1764 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::UintT(222))).is_err());
1765 assert!(info.to_string().contains("value: UintArray([4, 2, 1, 2, 1, 2], LinearHistogram)"));
1766 assert!(info.apply(&insert_multiple!(id: 3, value: Value::UintT(7), count: 4)).is_ok());
1767 assert!(info.to_string().contains("value: UintArray([4, 2, 1, 2, 5, 2], LinearHistogram)"));
1768 Ok(())
1769 }
1770
1771 #[fuchsia::test]
1772 fn test_exponential_uint_ops() -> Result<(), Error> {
1773 let mut info = Data::new();
1774 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 3, name: "value",
1776 floor: 5, initial_step: 2,
1777 step_multiplier: 4, buckets: 2, type: UintT))?;
1778 assert!(info
1779 .to_string()
1780 .contains("value: UintArray([5, 2, 4, 0, 0, 0, 0], ExponentialHistogram)"));
1781 assert!(info.apply(&insert!(id: 3, value: Value::UintT(5))).is_ok());
1782 assert!(info
1783 .to_string()
1784 .contains("value: UintArray([5, 2, 4, 0, 1, 0, 0], ExponentialHistogram)"));
1785 assert!(info.apply(&insert!(id: 3, value: Value::UintT(6))).is_ok());
1786 assert!(info
1787 .to_string()
1788 .contains("value: UintArray([5, 2, 4, 0, 2, 0, 0], ExponentialHistogram)"));
1789 assert!(info.apply(&insert!(id: 3, value: Value::UintT(7))).is_ok());
1790 assert!(info
1791 .to_string()
1792 .contains("value: UintArray([5, 2, 4, 0, 2, 1, 0], ExponentialHistogram)"));
1793 assert!(info.apply(&insert!(id: 3, value: Value::UintT(13))).is_ok());
1794 assert!(info
1795 .to_string()
1796 .contains("value: UintArray([5, 2, 4, 0, 2, 1, 1], ExponentialHistogram)"));
1797 assert!(info.apply(&insert!(id: 3, value: Value::UintT(u64::MAX))).is_ok());
1798 assert!(info
1799 .to_string()
1800 .contains("value: UintArray([5, 2, 4, 0, 2, 1, 2], ExponentialHistogram)"));
1801 assert!(info.apply(&insert!(id: 3, value: Value::UintT(0))).is_ok());
1802 assert!(info
1803 .to_string()
1804 .contains("value: UintArray([5, 2, 4, 1, 2, 1, 2], ExponentialHistogram)"));
1805 assert!(info.apply(&insert!(id: 3, value: Value::IntT(0))).is_err());
1806 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(0.0))).is_err());
1807 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::UintT(222))).is_err());
1808 assert!(info
1809 .to_string()
1810 .contains("value: UintArray([5, 2, 4, 1, 2, 1, 2], ExponentialHistogram)"));
1811 assert!(info.apply(&insert_multiple!(id: 3, value: Value::UintT(12), count: 4)).is_ok());
1812 assert!(info
1813 .to_string()
1814 .contains("value: UintArray([5, 2, 4, 1, 2, 5, 2], ExponentialHistogram)"));
1815 Ok(())
1816 }
1817
1818 #[fuchsia::test]
1819 fn test_basic_double_ops() -> Result<(), Error> {
1820 let mut info = Data::new();
1821 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 3, name: "value",
1822 value: Value::DoubleT(42.0)))?;
1823 assert!(info.apply(&add_number!(id: 3, value: Value::DoubleT(3.0))).is_ok());
1824 assert!(info.to_string().contains("value: Double(45.0)"));
1825 assert!(info.apply(&add_number!(id: 3, value: Value::IntT(3))).is_err());
1826 assert!(info.apply(&add_number!(id: 3, value: Value::UintT(3))).is_err());
1827 assert!(info.to_string().contains("value: Double(45.0)"));
1828 assert!(info.apply(&subtract_number!(id: 3, value: Value::DoubleT(5.0))).is_ok());
1829 assert!(info.to_string().contains("value: Double(40.0)"));
1830 assert!(info.apply(&subtract_number!(id: 3, value: Value::UintT(5))).is_err());
1831 assert!(info.apply(&subtract_number!(id: 3, value: Value::UintT(5))).is_err());
1832 assert!(info.to_string().contains("value: Double(40.0)"));
1833 assert!(info.apply(&set_number!(id: 3, value: Value::DoubleT(22.0))).is_ok());
1834 assert!(info.to_string().contains("value: Double(22.0)"));
1835 assert!(info.apply(&set_number!(id: 3, value: Value::UintT(23))).is_err());
1836 assert!(info.apply(&set_number!(id: 3, value: Value::UintT(24))).is_err());
1837 assert!(info.to_string().contains("value: Double(22.0)"));
1838 Ok(())
1839 }
1840
1841 #[fuchsia::test]
1842 fn test_array_double_ops() -> Result<(), Error> {
1843 let mut info = Data::new();
1844 info.apply(&create_array_property!(parent: ROOT_ID, id: 3, name: "value", slots: 3,
1845 type: ValueType::Double))?;
1846 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::DoubleT(3.0))).is_ok());
1847 assert!(info.to_string().contains("value: DoubleArray([0.0, 3.0, 0.0], Default)"));
1848 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::IntT(3))).is_err());
1849 assert!(info.apply(&array_add!(id: 3, index: 1, value: Value::UintT(3))).is_err());
1850 assert!(info.to_string().contains("value: DoubleArray([0.0, 3.0, 0.0], Default)"));
1851 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::DoubleT(5.0))).is_ok());
1852 assert!(info.to_string().contains("value: DoubleArray([0.0, 3.0, -5.0], Default)"));
1853 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::IntT(5))).is_err());
1854 assert!(info.apply(&array_subtract!(id: 3, index: 2, value: Value::UintT(5))).is_err());
1855 assert!(info.to_string().contains("value: DoubleArray([0.0, 3.0, -5.0], Default)"));
1856 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::DoubleT(22.0))).is_ok());
1857 assert!(info.to_string().contains("value: DoubleArray([0.0, 22.0, -5.0], Default)"));
1858 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::IntT(23))).is_err());
1859 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::IntT(24))).is_err());
1860 assert!(info.to_string().contains("value: DoubleArray([0.0, 22.0, -5.0], Default)"));
1861 Ok(())
1862 }
1863
1864 #[fuchsia::test]
1865 fn test_linear_double_ops() -> Result<(), Error> {
1866 let mut info = Data::new();
1867 info.apply(&create_linear_histogram!(parent: ROOT_ID, id: 3, name: "value",
1868 floor: 4.0, step_size: 0.5, buckets: 2, type: DoubleT))?;
1869 assert!(info
1870 .to_string()
1871 .contains("value: DoubleArray([4.0, 0.5, 0.0, 0.0, 0.0, 0.0], LinearHistogram)"));
1872 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(4.0))).is_ok());
1873 assert!(info
1874 .to_string()
1875 .contains("value: DoubleArray([4.0, 0.5, 0.0, 1.0, 0.0, 0.0], LinearHistogram)"));
1876 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(4.25))).is_ok());
1877 assert!(info
1878 .to_string()
1879 .contains("value: DoubleArray([4.0, 0.5, 0.0, 2.0, 0.0, 0.0], LinearHistogram)"));
1880 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(4.75))).is_ok());
1881 assert!(info
1882 .to_string()
1883 .contains("value: DoubleArray([4.0, 0.5, 0.0, 2.0, 1.0, 0.0], LinearHistogram)"));
1884 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(5.1))).is_ok());
1885 assert!(info
1886 .to_string()
1887 .contains("value: DoubleArray([4.0, 0.5, 0.0, 2.0, 1.0, 1.0], LinearHistogram)"));
1888 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(f64::MAX))).is_ok());
1889 assert!(info
1890 .to_string()
1891 .contains("value: DoubleArray([4.0, 0.5, 0.0, 2.0, 1.0, 2.0], LinearHistogram)"));
1892 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(f64::MIN_POSITIVE))).is_ok());
1893 assert!(info
1894 .to_string()
1895 .contains("value: DoubleArray([4.0, 0.5, 1.0, 2.0, 1.0, 2.0], LinearHistogram)"));
1896 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(f64::MIN))).is_ok());
1897 assert!(info
1898 .to_string()
1899 .contains("value: DoubleArray([4.0, 0.5, 2.0, 2.0, 1.0, 2.0], LinearHistogram)"));
1900 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(0.0))).is_ok());
1901 assert!(info
1902 .to_string()
1903 .contains("value: DoubleArray([4.0, 0.5, 3.0, 2.0, 1.0, 2.0], LinearHistogram)"));
1904 assert!(info.apply(&insert!(id: 3, value: Value::IntT(0))).is_err());
1905 assert!(info.apply(&insert!(id: 3, value: Value::UintT(0))).is_err());
1906 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::DoubleT(222.0))).is_err());
1907 assert!(info
1908 .to_string()
1909 .contains("value: DoubleArray([4.0, 0.5, 3.0, 2.0, 1.0, 2.0], LinearHistogram)"));
1910 assert!(info.apply(&insert_multiple!(id: 3, value: Value::DoubleT(4.5), count: 4)).is_ok());
1911 assert!(info
1912 .to_string()
1913 .contains("value: DoubleArray([4.0, 0.5, 3.0, 2.0, 5.0, 2.0], LinearHistogram)"));
1914 Ok(())
1915 }
1916
1917 #[fuchsia::test]
1918 fn test_exponential_double_ops() -> Result<(), Error> {
1919 let mut info = Data::new();
1920 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 3, name: "value",
1922 floor: 5.0, initial_step: 2.0,
1923 step_multiplier: 4.0, buckets: 3, type: DoubleT))?;
1924 assert!(info.to_string().contains(
1925 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0], ExponentialHistogram)"
1926 ));
1927 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(5.0))).is_ok());
1928 assert!(info.to_string().contains(
1929 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 1.0, 0.0, 0.0, 0.0], ExponentialHistogram)"
1930 ));
1931 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(6.9))).is_ok());
1932 assert!(info.to_string().contains(
1933 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 0.0, 0.0, 0.0], ExponentialHistogram)"
1934 ));
1935 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(7.1))).is_ok());
1936 assert!(info.to_string().contains(
1937 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 1.0, 0.0, 0.0], ExponentialHistogram)"
1938 ));
1939 assert!(info
1940 .apply(&insert_multiple!(id: 3, value: Value::DoubleT(12.9), count: 4))
1941 .is_ok());
1942 assert!(info.to_string().contains(
1943 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 5.0, 0.0, 0.0], ExponentialHistogram)"
1944 ));
1945 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(13.1))).is_ok());
1946 assert!(info.to_string().contains(
1947 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 5.0, 1.0, 0.0], ExponentialHistogram)"
1948 ));
1949 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(36.9))).is_ok());
1950 assert!(info.to_string().contains(
1951 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 5.0, 2.0, 0.0], ExponentialHistogram)"
1952 ));
1953 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(37.1))).is_ok());
1954 assert!(info.to_string().contains(
1955 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 5.0, 2.0, 1.0], ExponentialHistogram)"
1956 ));
1957 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(f64::MAX))).is_ok());
1958 assert!(info.to_string().contains(
1959 "value: DoubleArray([5.0, 2.0, 4.0, 0.0, 2.0, 5.0, 2.0, 2.0], ExponentialHistogram)"
1960 ));
1961 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(f64::MIN_POSITIVE))).is_ok());
1962 assert!(info.to_string().contains(
1963 "value: DoubleArray([5.0, 2.0, 4.0, 1.0, 2.0, 5.0, 2.0, 2.0], ExponentialHistogram)"
1964 ));
1965 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(f64::MIN))).is_ok());
1966 assert!(info.to_string().contains(
1967 "value: DoubleArray([5.0, 2.0, 4.0, 2.0, 2.0, 5.0, 2.0, 2.0], ExponentialHistogram)"
1968 ));
1969 assert!(info.apply(&insert!(id: 3, value: Value::DoubleT(0.0))).is_ok());
1970 assert!(info.to_string().contains(
1971 "value: DoubleArray([5.0, 2.0, 4.0, 3.0, 2.0, 5.0, 2.0, 2.0], ExponentialHistogram)"
1972 ));
1973 assert!(info.apply(&insert!(id: 3, value: Value::IntT(0))).is_err());
1974 assert!(info.apply(&insert!(id: 3, value: Value::UintT(0))).is_err());
1975 assert!(info.apply(&array_set!(id: 3, index: 1, value: Value::DoubleT(222.0))).is_err());
1976 assert!(info.to_string().contains(
1977 "value: DoubleArray([5.0, 2.0, 4.0, 3.0, 2.0, 5.0, 2.0, 2.0], ExponentialHistogram)"
1978 ));
1979 Ok(())
1980 }
1981
1982 #[fuchsia::test]
1983 fn test_basic_vector_ops() -> Result<(), Error> {
1984 let mut info = Data::new();
1985 info.apply(&create_string_property!(parent: ROOT_ID, id: 3, name: "value",
1986 value: "foo"))?;
1987 assert!(info.to_string().contains("value: String(\"foo\")"));
1988 assert!(info.apply(&set_string!(id: 3, value: "bar")).is_ok());
1989 assert!(info.to_string().contains("value: String(\"bar\")"));
1990 assert!(info.apply(&set_bytes!(id: 3, value: vec!(3u8))).is_err());
1991 assert!(info.to_string().contains("value: String(\"bar\")"));
1992 info.apply(&create_bytes_property!(parent: ROOT_ID, id: 4, name: "bvalue",
1993 value: vec!(1u8, 2u8)))?;
1994 assert!(info.to_string().contains("bvalue: Bytes([1, 2])"));
1995 assert!(info.apply(&set_bytes!(id: 4, value: vec!(3u8, 4u8))).is_ok());
1996 assert!(info.to_string().contains("bvalue: Bytes([3, 4])"));
1997 assert!(info.apply(&set_string!(id: 4, value: "baz")).is_err());
1998 assert!(info.to_string().contains("bvalue: Bytes([3, 4])"));
1999 Ok(())
2000 }
2001
2002 #[fuchsia::test]
2003 fn test_basic_lazy_node_ops() -> Result<(), Error> {
2004 let mut info = Data::new();
2005 info.apply_lazy(&create_lazy_node!(parent: ROOT_ID, id: 1, name: "child", disposition: validate::LinkDisposition::Child, actions: vec![create_bytes_property!(parent: ROOT_ID, id: 1, name: "child_bytes",value: vec!(3u8, 4u8))]))?;
2006 info.apply_lazy(&create_lazy_node!(parent: ROOT_ID, id: 2, name: "inline", disposition: validate::LinkDisposition::Inline, actions: vec![create_bytes_property!(parent: ROOT_ID, id: 1, name: "inline_bytes",value: vec!(3u8, 4u8))]))?;
2007
2008 assert_eq!(
2010 info.to_string(),
2011 "root ->\n> inline_bytes: Bytes([3, 4])\n> child ->\n> > child_bytes: Bytes([3, 4])"
2012 );
2013
2014 info.apply_lazy(&delete_lazy_node!(id: 1))?;
2015 assert_eq!(info.to_string(), "root ->\n> inline_bytes: Bytes([3, 4])");
2017
2018 Ok(())
2019 }
2020
2021 #[fuchsia::test]
2022 fn test_illegal_node_actions() -> Result<(), Error> {
2023 let mut info = Data::new();
2024 assert!(info.apply(&create_node!(parent: 42, id: 1, name: "child")).is_err());
2026 info = Data::new();
2028 info.apply(&create_node!(parent: ROOT_ID, id: 1, name: "child"))?;
2029 assert!(info.apply(&create_node!(parent: ROOT_ID, id: 1, name: "another_child")).is_err());
2030 info = Data::new();
2032 assert!(info.apply(&delete_node!(id: ROOT_ID)).is_err());
2033 info = Data::new();
2035 assert!(info.apply(&delete_node!(id: 333)).is_err());
2036 Ok(())
2037 }
2038
2039 #[fuchsia::test]
2040 fn test_illegal_property_actions() -> Result<(), Error> {
2041 let mut info = Data::new();
2042 assert!(info
2044 .apply(
2045 &create_numeric_property!(parent: 42, id: 1, name: "answer", value: Value::IntT(42))
2046 )
2047 .is_err());
2048 info = Data::new();
2050 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 1, name: "answer",
2051 value: Value::IntT(42)))?;
2052 assert!(info
2053 .apply(&create_numeric_property!(parent: ROOT_ID, id: 1, name: "another_answer",
2054 value: Value::IntT(7)))
2055 .is_err());
2056 info = Data::new();
2058 assert!(info.apply(&delete_property!(id: 1)).is_err());
2059 info = Data::new();
2061 info.apply(&create_numeric_property!(parent: ROOT_ID, id: 3, name: "value",
2062 value: Value::IntT(42)))?;
2063 info.apply(&create_array_property!(parent: ROOT_ID, id: 4, name: "array", slots: 2,
2064 type: ValueType::Int))?;
2065 info.apply(&create_linear_histogram!(parent: ROOT_ID, id: 5, name: "lin",
2066 floor: 5, step_size: 2,
2067 buckets: 2, type: IntT))?;
2068 info.apply(&create_exponential_histogram!(parent: ROOT_ID, id: 6, name: "exp",
2069 floor: 5, initial_step: 2,
2070 step_multiplier: 2, buckets: 2, type: IntT))?;
2071 assert!(info.apply(&set_number!(id: 3, value: Value::IntT(5))).is_ok());
2072 assert!(info.apply(&array_set!(id: 4, index: 0, value: Value::IntT(5))).is_ok());
2073 assert!(info.apply(&insert!(id: 5, value: Value::IntT(2))).is_ok());
2074 assert!(info.apply(&insert!(id: 6, value: Value::IntT(2))).is_ok());
2075 assert!(info.apply(&insert_multiple!(id: 5, value: Value::IntT(2), count: 3)).is_ok());
2076 assert!(info.apply(&insert_multiple!(id: 6, value: Value::IntT(2), count: 3)).is_ok());
2077 assert!(info.apply(&set_number!(id: 4, value: Value::IntT(5))).is_err());
2078 assert!(info.apply(&set_number!(id: 5, value: Value::IntT(5))).is_err());
2079 assert!(info.apply(&set_number!(id: 6, value: Value::IntT(5))).is_err());
2080 assert!(info.apply(&array_set!(id: 3, index: 0, value: Value::IntT(5))).is_err());
2081 assert!(info.apply(&array_set!(id: 5, index: 0, value: Value::IntT(5))).is_err());
2082 assert!(info.apply(&array_set!(id: 6, index: 0, value: Value::IntT(5))).is_err());
2083 assert!(info.apply(&insert!(id: 3, value: Value::IntT(2))).is_err());
2084 assert!(info.apply(&insert!(id: 4, value: Value::IntT(2))).is_err());
2085 assert!(info.apply(&insert_multiple!(id: 3, value: Value::IntT(2), count: 3)).is_err());
2086 assert!(info.apply(&insert_multiple!(id: 4, value: Value::IntT(2), count: 3)).is_err());
2087 Ok(())
2088 }
2089
2090 #[fuchsia::test]
2091 fn test_enum_values() {
2092 assert_eq!(BlockType::IntValue.to_isize().unwrap(), ArrayType::Int.to_isize().unwrap());
2093 assert_eq!(BlockType::UintValue.to_isize().unwrap(), ArrayType::Uint.to_isize().unwrap());
2094 assert_eq!(
2095 BlockType::DoubleValue.to_isize().unwrap(),
2096 ArrayType::Double.to_isize().unwrap()
2097 );
2098 }
2099
2100 #[fuchsia::test]
2101 fn test_create_node_checks() {
2102 let mut data = Data::new();
2103 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2104 assert!(data.apply(&create_node!(parent: 0, id: 2, name: "second")).is_ok());
2105 assert!(data.apply(&create_node!(parent: 1, id: 3, name: "child")).is_ok());
2106 assert!(data.apply(&create_node!(parent: 0, id: 2, name: "double")).is_err());
2107 let mut data = Data::new();
2108 assert!(data.apply(&create_node!(parent: 1, id: 2, name: "orphan")).is_err());
2109 }
2110
2111 #[fuchsia::test]
2112 fn test_delete_node_checks() {
2113 let mut data = Data::new();
2114 assert!(data.apply(&delete_node!(id: 0)).is_err());
2115 let mut data = Data::new();
2116 data.apply(&create_node!(parent: 0, id: 1, name: "first")).ok();
2117 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2118 assert!(data.apply(&delete_node!(id: 1)).is_err());
2119 }
2120
2121 #[fuchsia::test]
2122 fn test_node_tombstoning() {
2124 let mut data = Data::new();
2126 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2127 assert!(data
2128 .apply(&create_numeric_property!(parent: 1, id: 2,
2129 name: "answer", value: Value::IntT(42)))
2130 .is_ok());
2131 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2132 assert!(data.apply(&delete_property!(id: 2)).is_ok());
2133 assert!(data.apply(&delete_property!(id: 2)).is_err());
2134 let mut data = Data::new();
2136 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2137 assert!(data
2138 .apply(&create_numeric_property!(parent: 1, id: 2,
2139 name: "answer", value: Value::IntT(42)))
2140 .is_ok());
2141 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2142 assert!(data.apply(&delete_property!(id: 2)).is_ok());
2143 assert!(data
2144 .apply(&create_numeric_property!(parent: 0, id: 2,
2145 name: "root_answer", value: Value::IntT(42)))
2146 .is_ok());
2147 let mut data = Data::new();
2149 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2150 assert!(data
2151 .apply(&create_numeric_property!(parent: 1, id: 2,
2152 name: "answer", value: Value::IntT(42)))
2153 .is_ok());
2154 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2155 assert!(data
2156 .apply(&create_numeric_property!(parent: 0, id: 2,
2157 name: "root_answer", value: Value::IntT(42)))
2158 .is_err());
2159 }
2160
2161 #[fuchsia::test]
2162 fn test_property_tombstoning() {
2163 let mut data = Data::new();
2166 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2167 assert!(data.apply(&create_node!(parent: 1, id: 2, name: "second")).is_ok());
2168 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2169 assert!(data.apply(&delete_node!(id: 2)).is_ok());
2170 assert!(data.apply(&delete_node!(id: 2)).is_err());
2171 let mut data = Data::new();
2173 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2174 assert!(data.apply(&create_node!(parent: 1, id: 2, name: "second")).is_ok());
2175 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2176 assert!(data.apply(&delete_node!(id: 2)).is_ok());
2177 assert!(data.apply(&create_node!(parent: 0, id: 2, name: "new_root_second")).is_ok());
2178 let mut data = Data::new();
2180 assert!(data.apply(&create_node!(parent: 0, id: 1, name: "first")).is_ok());
2181 assert!(data.apply(&create_node!(parent: 1, id: 2, name: "second")).is_ok());
2182 assert!(data.apply(&delete_node!(id: 1)).is_ok());
2183 assert!(data.apply(&create_node!(parent: 0, id: 2, name: "new_root_second")).is_err());
2184 }
2185
2186 const DIFF_STRING: &str = r#"-- DIFF --
2187 same: "root ->"
2188 same: "> node ->"
2189local: "> > prop1: String(\"foo\")"
2190other: "> > prop1: String(\"bar\")""#;
2191
2192 const FULL_STRING: &str = r#"-- LOCAL --
2193root ->
2194> node ->
2195> > prop1: String("foo")
2196-- OTHER --
2197root ->
2198> node ->
2199> > prop1: String("bar")"#;
2200
2201 #[fuchsia::test]
2202 fn diff_modes_work() -> Result<(), Error> {
2203 let mut local = Data::new();
2204 let mut remote = Data::new();
2205 local.apply(&create_node!(parent: 0, id: 1, name: "node"))?;
2206 local.apply(&create_string_property!(parent: 1, id: 2, name: "prop1", value: "foo"))?;
2207 remote.apply(&create_node!(parent: 0, id: 1, name: "node"))?;
2208 remote.apply(&create_string_property!(parent: 1, id: 2, name: "prop1", value: "bar"))?;
2209 match local.compare(&remote, DiffType::Diff) {
2210 Err(error) => {
2211 let error_string = format!("{:?}", error);
2212 assert_eq!("Trees differ:\n".to_string() + DIFF_STRING, error_string);
2213 }
2214 _ => return Err(format_err!("Didn't get failure")),
2215 }
2216 match local.compare(&remote, DiffType::Full) {
2217 Err(error) => {
2218 let error_string = format!("{:?}", error);
2219 assert_eq!("Trees differ:\n".to_string() + FULL_STRING, error_string);
2220 }
2221 _ => return Err(format_err!("Didn't get failure")),
2222 }
2223 match local.compare(&remote, DiffType::Both) {
2224 Err(error) => {
2225 let error_string = format!("{:?}", error);
2226 assert_eq!(["Trees differ:", FULL_STRING, DIFF_STRING].join("\n"), error_string);
2227 }
2228 _ => return Err(format_err!("Didn't get failure")),
2229 }
2230 Ok(())
2231 }
2232}