1#![allow(clippy::cast_precision_loss)]
4
5use std::borrow::Cow;
6use std::cmp::Ordering;
7use std::fmt::Display;
8use std::hash::Hash;
9use std::ops::{Add, AddAssign, Div, Mul, Sub};
10use std::str::FromStr;
11
12use anyhow::{anyhow, Context, Result};
13use indexmap::map::Iter;
14use indexmap::{indexmap, IndexMap, IndexSet};
15#[cfg(feature = "schema")]
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19use super::summary::Diffs;
20use crate::api::{self, CachegrindMetric, DhatMetric, ErrorMetric, EventKind};
21use crate::util::{to_string_unsigned_short, EitherOrBoth};
22
23#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
37#[cfg_attr(feature = "schema", derive(JsonSchema))]
38pub enum Metric {
39 Int(u64),
41 Float(f64),
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47#[cfg_attr(feature = "schema", derive(JsonSchema))]
48pub enum MetricKind {
49 None,
51 Callgrind(EventKind),
53 Cachegrind(CachegrindMetric),
55 Dhat(DhatMetric),
57 Memcheck(ErrorMetric),
59 Helgrind(ErrorMetric),
61 DRD(ErrorMetric),
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69#[cfg_attr(feature = "schema", derive(JsonSchema))]
70pub struct Metrics<K: Hash + Eq>(pub IndexMap<K, Metric>);
71
72#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
78#[cfg_attr(feature = "schema", derive(JsonSchema))]
79pub struct MetricsDiff {
80 pub diffs: Option<Diffs>,
82 pub metrics: EitherOrBoth<Metric>,
84}
85
86#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
88#[cfg_attr(feature = "schema", derive(JsonSchema))]
89pub struct MetricsSummary<K: Hash + Eq = EventKind>(IndexMap<K, MetricsDiff>);
90
91pub trait Summarize: Hash + Eq + Clone {
93 fn summarize(_: &mut Cow<Metrics<Self>>) {}
95}
96
97pub trait TypeChecker {
99 fn is_float(&self) -> bool;
101 fn is_int(&self) -> bool;
103 fn verify_metric(&self, metric: Metric) -> bool {
105 (self.is_int() && metric.is_int()) || (self.is_float() && metric.is_float())
106 }
107}
108
109impl Metric {
110 #[must_use]
115 pub fn div0(self, rhs: Self) -> Self {
116 match (self, rhs) {
117 (_, Self::Int(0) | Self::Float(0.0f64)) => Self::Float(0.0f64),
118 (a, b) => a / b,
119 }
120 }
121
122 pub fn is_int(&self) -> bool {
124 match self {
125 Self::Int(_) => true,
126 Self::Float(_) => false,
127 }
128 }
129
130 pub fn is_float(&self) -> bool {
132 match self {
133 Self::Int(_) => false,
134 Self::Float(_) => true,
135 }
136 }
137
138 pub fn try_convert<T: Display + TypeChecker>(&self, metric_kind: T) -> Option<(T, Self)> {
145 if metric_kind.verify_metric(*self) {
146 Some((metric_kind, *self))
147 } else if let Self::Int(a) = self {
148 Some((metric_kind, Self::Float(*a as f64)))
149 } else {
150 None
151 }
152 }
153}
154
155impl Add for Metric {
156 type Output = Self;
157
158 fn add(self, rhs: Self) -> Self::Output {
159 match (self, rhs) {
160 (Self::Int(a), Self::Int(b)) => Self::Int(a.saturating_add(b)),
161 (Self::Int(a), Self::Float(b)) => Self::Float((a as f64) + b),
162 (Self::Float(a), Self::Int(b)) => Self::Float((b as f64) + a),
163 (Self::Float(a), Self::Float(b)) => Self::Float(a + b),
164 }
165 }
166}
167
168impl AddAssign for Metric {
169 fn add_assign(&mut self, rhs: Self) {
170 *self = *self + rhs;
171 }
172}
173
174impl Display for Metric {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 match self {
177 Self::Int(a) => f.pad(&format!("{a}")),
178 Self::Float(a) => f.pad(&to_string_unsigned_short(*a)),
179 }
180 }
181}
182
183impl Div for Metric {
184 type Output = Self;
185
186 fn div(self, rhs: Self) -> Self::Output {
187 match (self, rhs) {
188 (Self::Int(a), Self::Int(b)) => Self::Float((a as f64) / (b as f64)),
189 (Self::Int(a), Self::Float(b)) => Self::Float((a as f64) / b),
190 (Self::Float(a), Self::Int(b)) => Self::Float(a / (b as f64)),
191 (Self::Float(a), Self::Float(b)) => Self::Float(a / b),
192 }
193 }
194}
195
196impl Eq for Metric {}
197
198impl From<u64> for Metric {
199 fn from(value: u64) -> Self {
200 Self::Int(value)
201 }
202}
203
204impl From<f64> for Metric {
205 fn from(value: f64) -> Self {
206 Self::Float(value)
207 }
208}
209
210impl From<api::Limit> for Metric {
211 fn from(value: api::Limit) -> Self {
212 match value {
213 api::Limit::Int(a) => Self::Int(a),
214 api::Limit::Float(f) => Self::Float(f),
215 }
216 }
217}
218
219impl FromStr for Metric {
220 type Err = anyhow::Error;
221
222 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
223 match s.parse::<u64>() {
224 Ok(a) => Ok(Self::Int(a)),
225 Err(_) => match s.parse::<f64>() {
226 Ok(a) => Ok(Self::Float(a)),
227 Err(error) => Err(anyhow!("Invalid metric: {error}")),
228 },
229 }
230 }
231}
232
233impl Mul<u64> for Metric {
234 type Output = Self;
235
236 fn mul(self, rhs: u64) -> Self::Output {
237 match self {
238 Self::Int(a) => Self::Int(a.saturating_mul(rhs)),
239 Self::Float(a) => Self::Float(a * (rhs as f64)),
240 }
241 }
242}
243
244impl Ord for Metric {
245 fn cmp(&self, other: &Self) -> Ordering {
246 match (self, other) {
247 (Self::Int(a), Self::Int(b)) => a.cmp(b),
248 (Self::Int(a), Self::Float(b)) => (*a as f64).total_cmp(b),
249 (Self::Float(a), Self::Int(b)) => a.total_cmp(&(*b as f64)),
250 (Self::Float(a), Self::Float(b)) => a.total_cmp(b),
251 }
252 }
253}
254
255impl PartialEq for Metric {
256 fn eq(&self, other: &Self) -> bool {
257 match (self, other) {
258 (Self::Int(a), Self::Int(b)) => a == b,
259 (Self::Int(a), Self::Float(b)) => (*a as f64).total_cmp(b) == Ordering::Equal,
260 (Self::Float(a), Self::Int(b)) => a.total_cmp(&(*b as f64)) == Ordering::Equal,
261 (Self::Float(a), Self::Float(b)) => a.total_cmp(b) == Ordering::Equal,
262 }
263 }
264}
265
266impl PartialOrd for Metric {
267 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
268 Some(self.cmp(other))
269 }
270}
271
272impl Sub for Metric {
273 type Output = Self;
274
275 fn sub(self, rhs: Self) -> Self::Output {
276 match (self, rhs) {
277 (Self::Int(a), Self::Int(b)) => Self::Int(a.saturating_sub(b)),
278 (Self::Int(a), Self::Float(b)) => Self::Float((a as f64) - b),
279 (Self::Float(a), Self::Int(b)) => Self::Float(a - (b as f64)),
280 (Self::Float(a), Self::Float(b)) => Self::Float(a - b),
281 }
282 }
283}
284
285impl Display for MetricKind {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 match self {
288 Self::None => Ok(()),
289 Self::Callgrind(metric) => f.write_fmt(format_args!("Callgrind: {metric}")),
290 Self::Cachegrind(metric) => f.write_fmt(format_args!("Cachegrind: {metric}")),
291 Self::Dhat(metric) => f.write_fmt(format_args!("DHAT: {metric}")),
292 Self::Memcheck(metric) => f.write_fmt(format_args!("Memcheck: {metric}")),
293 Self::Helgrind(metric) => f.write_fmt(format_args!("Helgrind: {metric}")),
294 Self::DRD(metric) => f.write_fmt(format_args!("DRD: {metric}")),
295 }
296 }
297}
298
299impl<K: Hash + Eq + Display + Clone> Metrics<K> {
300 pub fn empty() -> Self {
302 Self(IndexMap::new())
303 }
304
305 pub fn with_metric_kinds<I, T>(kinds: T) -> Self
307 where
308 I: Into<Metric>,
309 T: IntoIterator<Item = (K, I)>,
310 {
311 Self(kinds.into_iter().map(|(k, n)| (k, n.into())).collect())
312 }
313
314 pub fn add_iter_str<I, T>(&mut self, iter: T) -> Result<()>
327 where
328 I: AsRef<str>,
329 T: IntoIterator<Item = I>,
330 {
331 for (this, other) in self.0.values_mut().zip(iter.into_iter()) {
332 *this += other
333 .as_ref()
334 .parse::<Metric>()
335 .context("A metric must be a valid number")?;
336 }
337
338 Ok(())
339 }
340
341 pub fn add(&mut self, other: &Self) {
345 for (this, other) in self.0.values_mut().zip(other.0.values()) {
346 *this += *other;
347 }
348 }
349
350 pub fn metric_by_index(&self, index: usize) -> Option<Metric> {
354 self.0.get_index(index).map(|(_, c)| *c)
355 }
356
357 pub fn metric_by_kind(&self, kind: &K) -> Option<Metric> {
361 self.0.get_key_value(kind).map(|(_, c)| *c)
362 }
363
364 pub fn try_metric_by_kind(&self, kind: &K) -> Result<Metric> {
370 self.metric_by_kind(kind)
371 .with_context(|| format!("Missing event type '{kind}"))
372 }
373
374 pub fn metric_kinds(&self) -> Vec<K> {
376 self.0.iter().map(|(k, _)| k.clone()).collect()
377 }
378
379 pub fn metric_kinds_union<'a>(&'a self, other: &'a Self) -> IndexSet<&'a K> {
384 let set = self.0.keys().collect::<IndexSet<_>>();
385 let other_set = other.0.keys().collect::<IndexSet<_>>();
386 set.union(&other_set).copied().collect()
387 }
388
389 pub fn iter(&self) -> Iter<'_, K, Metric> {
391 self.0.iter()
392 }
393
394 pub fn is_empty(&self) -> bool {
396 self.0.is_empty()
397 }
398
399 pub fn insert(&mut self, key: K, value: Metric) -> Option<Metric> {
408 self.0.insert(key, value)
409 }
410
411 pub fn insert_all(&mut self, entries: &[(K, Metric)]) {
415 for (key, value) in entries {
416 self.insert(key.clone(), *value);
417 }
418 }
419}
420
421impl<'a, K: Hash + Eq + Display + Clone> IntoIterator for &'a Metrics<K> {
422 type Item = (&'a K, &'a Metric);
423
424 type IntoIter = Iter<'a, K, Metric>;
425
426 fn into_iter(self) -> Self::IntoIter {
427 self.iter()
428 }
429}
430
431impl<I, K: Hash + Eq + From<I>> FromIterator<I> for Metrics<K> {
432 fn from_iter<T>(iter: T) -> Self
433 where
434 T: IntoIterator<Item = I>,
435 {
436 Self(
437 iter.into_iter()
438 .map(|s| (K::from(s), Metric::Int(0)))
439 .collect::<IndexMap<_, _>>(),
440 )
441 }
442}
443
444impl MetricsDiff {
445 pub fn new(metrics: EitherOrBoth<Metric>) -> Self {
447 if let EitherOrBoth::Both(new, old) = metrics {
448 Self {
449 metrics,
450 diffs: Some(Diffs::new(new, old)),
451 }
452 } else {
453 Self {
454 metrics,
455 diffs: None,
456 }
457 }
458 }
459
460 #[must_use]
462 pub fn add(&self, other: &Self) -> Self {
463 match (&self.metrics, &other.metrics) {
464 (EitherOrBoth::Left(new), EitherOrBoth::Left(other_new)) => {
465 Self::new(EitherOrBoth::Left(*new + *other_new))
466 }
467 (EitherOrBoth::Right(old), EitherOrBoth::Left(new))
468 | (EitherOrBoth::Left(new), EitherOrBoth::Right(old)) => {
469 Self::new(EitherOrBoth::Both(*new, *old))
470 }
471 (EitherOrBoth::Right(old), EitherOrBoth::Right(other_old)) => {
472 Self::new(EitherOrBoth::Right(*old + *other_old))
473 }
474 (EitherOrBoth::Both(new, old), EitherOrBoth::Left(other_new))
475 | (EitherOrBoth::Left(new), EitherOrBoth::Both(other_new, old)) => {
476 Self::new(EitherOrBoth::Both(*new + *other_new, *old))
477 }
478 (EitherOrBoth::Both(new, old), EitherOrBoth::Right(other_old))
479 | (EitherOrBoth::Right(old), EitherOrBoth::Both(new, other_old)) => {
480 Self::new(EitherOrBoth::Both(*new, *old + *other_old))
481 }
482 (EitherOrBoth::Both(new, old), EitherOrBoth::Both(other_new, other_old)) => {
483 Self::new(EitherOrBoth::Both(*new + *other_new, *old + *other_old))
484 }
485 }
486 }
487}
488
489impl<K> MetricsSummary<K>
490where
491 K: Hash + Eq + Summarize + Display + Clone,
492{
493 pub fn new(metrics: EitherOrBoth<Metrics<K>>) -> Self {
500 match metrics {
501 EitherOrBoth::Left(new) => {
502 assert!(!new.is_empty());
503
504 let mut new = Cow::Owned(new);
505 K::summarize(&mut new);
506
507 Self(
508 new.iter()
509 .map(|(metric_kind, metric)| {
510 (
511 metric_kind.clone(),
512 MetricsDiff::new(EitherOrBoth::Left(*metric)),
513 )
514 })
515 .collect::<IndexMap<_, _>>(),
516 )
517 }
518 EitherOrBoth::Right(old) => {
519 assert!(!old.is_empty());
520
521 let mut old = Cow::Owned(old);
522 K::summarize(&mut old);
523
524 Self(
525 old.iter()
526 .map(|(metric_kind, metric)| {
527 (
528 metric_kind.clone(),
529 MetricsDiff::new(EitherOrBoth::Right(*metric)),
530 )
531 })
532 .collect::<IndexMap<_, _>>(),
533 )
534 }
535 EitherOrBoth::Both(new, old) => {
536 assert!(!new.is_empty());
537 assert!(!old.is_empty());
538
539 let mut new = Cow::Owned(new);
540 K::summarize(&mut new);
541 let mut old = Cow::Owned(old);
542 K::summarize(&mut old);
543
544 let mut map = indexmap! {};
545 for metric_kind in new.metric_kinds_union(&old) {
546 let diff = match (
547 new.metric_by_kind(metric_kind),
548 old.metric_by_kind(metric_kind),
549 ) {
550 (Some(metric), None) => MetricsDiff::new(EitherOrBoth::Left(metric)),
551 (None, Some(metric)) => MetricsDiff::new(EitherOrBoth::Right(metric)),
552 (Some(new), Some(old)) => MetricsDiff::new(EitherOrBoth::Both(new, old)),
553 (None, None) => {
554 unreachable!(
555 "The union contains the event kinds either from new or old or \
556 from both"
557 )
558 }
559 };
560 map.insert(metric_kind.clone(), diff);
561 }
562 Self(map)
563 }
564 }
565 }
566
567 pub fn diff_by_kind(&self, metric_kind: &K) -> Option<&MetricsDiff> {
569 self.0.get(metric_kind)
570 }
571
572 pub fn all_diffs(&self) -> impl Iterator<Item = (&K, &MetricsDiff)> {
574 self.0.iter()
575 }
576
577 pub fn extract_costs(&self) -> EitherOrBoth<Metrics<K>> {
581 let mut new_metrics: Metrics<K> = Metrics::empty();
582 let mut old_metrics: Metrics<K> = Metrics::empty();
583
584 for (metric_kind, diff) in self.all_diffs() {
586 match diff.metrics {
587 EitherOrBoth::Left(new) => {
588 new_metrics.insert(metric_kind.clone(), new);
589 }
590 EitherOrBoth::Right(old) => {
591 old_metrics.insert(metric_kind.clone(), old);
592 }
593 EitherOrBoth::Both(new, old) => {
594 new_metrics.insert(metric_kind.clone(), new);
595 old_metrics.insert(metric_kind.clone(), old);
596 }
597 }
598 }
599
600 match (new_metrics.is_empty(), old_metrics.is_empty()) {
601 (false, false) => EitherOrBoth::Both(new_metrics, old_metrics),
602 (false, true) => EitherOrBoth::Left(new_metrics),
603 (true, false) => EitherOrBoth::Right(old_metrics),
604 (true, true) => unreachable!("A costs diff contains new or old values or both."),
605 }
606 }
607
608 pub fn add(&mut self, other: &Self) {
613 let other_keys = other.0.keys().cloned().collect::<IndexSet<_>>();
614 let keys = self.0.keys().cloned().collect::<IndexSet<_>>();
615 let union = keys.union(&other_keys);
616
617 for key in union {
618 match (self.diff_by_kind(key), other.diff_by_kind(key)) {
619 (None, None) => unreachable!("One key of the union set must be present"),
620 (None, Some(other_diff)) => {
621 self.0.insert(key.clone(), other_diff.clone());
622 }
623 (Some(_), None) => {
624 }
626 (Some(this_diff), Some(other_diff)) => {
627 let new_diff = this_diff.add(other_diff);
628 self.0.insert(key.clone(), new_diff);
629 }
630 }
631 }
632 }
633}
634
635impl<K> Default for MetricsSummary<K>
636where
637 K: Hash + Eq,
638{
639 fn default() -> Self {
640 Self(IndexMap::default())
641 }
642}
643
644impl From<Metric> for f64 {
645 fn from(value: Metric) -> Self {
646 match value {
647 Metric::Int(a) => a as Self,
648 Metric::Float(a) => a,
649 }
650 }
651}
652
653impl Mul<Metric> for u64 {
654 type Output = Metric;
655
656 fn mul(self, rhs: Metric) -> Self::Output {
657 match rhs {
658 Metric::Int(b) => Metric::Int(self.saturating_mul(b)),
659 Metric::Float(b) => Metric::Float((self as f64) * b),
660 }
661 }
662}
663
664#[cfg(test)]
665mod tests {
666 use std::cmp::Ordering;
667 use std::{f64, iter};
668
669 use pretty_assertions::assert_eq;
670 use rstest::rstest;
671
672 use super::*;
673 use crate::api::EventKind::{self, *};
674 use crate::runner::summary::Diffs;
675 use crate::util::EitherOrBoth;
676
677 fn expected_metrics<I, T>(events: T) -> Metrics<EventKind>
678 where
679 I: Into<Metric>,
680 T: IntoIterator<Item = (EventKind, I)>,
681 {
682 Metrics(
683 events
684 .into_iter()
685 .map(|(k, n)| (k, n.into()))
686 .collect::<IndexMap<_, _>>(),
687 )
688 }
689
690 fn expected_metrics_diff<D>(metrics: EitherOrBoth<Metric>, diffs: D) -> MetricsDiff
691 where
692 D: Into<Option<(f64, f64)>>,
693 {
694 MetricsDiff {
695 metrics,
696 diffs: diffs
697 .into()
698 .map(|(diff_pct, factor)| Diffs { diff_pct, factor }),
699 }
700 }
701
702 fn metrics_fixture(metrics: &[u64]) -> Metrics<EventKind> {
703 let event_kinds = [
705 Ir,
706 Dr,
707 Dw,
708 I1mr,
709 D1mr,
710 D1mw,
711 ILmr,
712 DLmr,
713 DLmw,
714 L1hits,
715 LLhits,
716 RamHits,
717 TotalRW,
718 EstimatedCycles,
719 I1MissRate,
720 D1MissRate,
721 LLiMissRate,
722 LLdMissRate,
723 LLMissRate,
724 L1HitRate,
725 LLHitRate,
726 RamHitRate,
727 ];
728
729 Metrics::with_metric_kinds(
730 event_kinds
731 .iter()
732 .zip(metrics.iter())
733 .map(|(e, v)| (*e, *v)),
734 )
735 }
736
737 fn metrics_summary_fixture<T, U>(kinds: U) -> MetricsSummary<EventKind>
738 where
739 T: Into<Option<(f64, f64)>> + Clone,
740 U: IntoIterator<Item = (EitherOrBoth<Metric>, T)>,
741 {
742 let event_kinds = [
744 Ir,
745 Dr,
746 Dw,
747 I1mr,
748 D1mr,
749 D1mw,
750 ILmr,
751 DLmr,
752 DLmw,
753 L1hits,
754 LLhits,
755 RamHits,
756 TotalRW,
757 EstimatedCycles,
758 I1MissRate,
759 D1MissRate,
760 LLiMissRate,
761 LLdMissRate,
762 LLMissRate,
763 L1HitRate,
764 LLHitRate,
765 RamHitRate,
766 ];
767
768 let map: IndexMap<EventKind, MetricsDiff> = event_kinds
769 .iter()
770 .zip(kinds)
771 .map(|(e, (m, d))| (*e, expected_metrics_diff(m, d)))
772 .collect();
773
774 MetricsSummary(map)
775 }
776
777 #[rstest]
778 #[case::single_zero(&[Ir], &["0"], expected_metrics([(Ir, 0)]))]
779 #[case::single_one(&[Ir], &["1"], expected_metrics([(Ir, 1)]))]
780 #[case::single_float(&[Ir], &["1.0"], expected_metrics([(Ir, 1.0f64)]))]
781 #[case::single_u64_max(&[Ir], &[u64::MAX.to_string()], expected_metrics([(Ir, u64::MAX)]))]
782 #[case::one_more_than_max_u64(&[Ir], &["18446744073709551616"],
783 expected_metrics([(Ir, 18_446_744_073_709_552_000_f64)])
785 )]
786 #[case::more_values_than_kinds(&[Ir], &["1", "2"], expected_metrics([(Ir, 1)]))]
787 #[case::more_kinds_than_values(&[Ir, I1mr], &["1"], expected_metrics([(Ir, 1), (I1mr, 0)]))]
788 fn test_metrics_add_iter_str<I>(
789 #[case] event_kinds: &[EventKind],
790 #[case] to_add: &[I],
791 #[case] expected_metrics: Metrics<EventKind>,
792 ) where
793 I: AsRef<str>,
794 {
795 let mut metrics =
796 Metrics::with_metric_kinds(event_kinds.iter().copied().zip(iter::repeat(0)));
797 metrics.add_iter_str(to_add).unwrap();
798
799 assert_eq!(metrics, expected_metrics);
800 }
801
802 #[rstest]
803 #[case::word(&[Ir], &["abc"])]
804 #[case::empty(&[Ir], &[""])]
805 fn test_metrics_add_iter_str_when_error<I>(
806 #[case] event_kinds: &[EventKind],
807 #[case] to_add: &[I],
808 ) where
809 I: AsRef<str>,
810 {
811 let mut metrics =
812 Metrics::with_metric_kinds(event_kinds.iter().copied().zip(iter::repeat(0)));
813 assert!(metrics.add_iter_str(to_add).is_err());
814 }
815
816 #[rstest]
817 #[case::all_zero_int(0, 0, 0.0f64)]
818 #[case::lhs_zero_int_one(0, 1, 0.0f64)]
819 #[case::lhs_zero_int_two(0, 2, 0.0f64)]
820 #[case::one_rhs_zero_int(1, 0, 0.0f64)]
821 #[case::two_rhs_zero_int(2, 0, 0.0f64)]
822 #[case::all_zero_float(0.0f64, 0.0f64, 0.0f64)]
823 #[case::lhs_zero_float_one(0.0f64, 1.0f64, 0.0f64)]
824 #[case::lhs_zero_float_two(0.0f64, 2.0f64, 0.0f64)]
825 #[case::lhs_zero_float_neg_two(0.0f64, -2.0f64, -0.0f64)]
826 #[case::one_rhs_zero_float(1.0f64, 0.0f64, 0.0f64)]
827 #[case::two_rhs_zero_float(2.0f64, 0.0f64, 0.0f64)]
828 #[case::one_neg_rhs_zero_float(1.0f64, -0.0f64, 0.0f64)]
829 #[case::one_one_int(1, 1, 1.0f64)]
830 #[case::two_one_int(2, 1, 2.0f64)]
831 #[case::one_two_int(1, 2, 0.5f64)]
832 #[case::one_float_one(1, 1.0f64, 1.0f64)]
833 #[case::float_one_int_one(1.0f64, 1, 1.0f64)]
834 #[case::float_one(1.0f64, 1.0f64, 1.0f64)]
835 #[case::one_float_two(1, 2.0f64, 0.5f64)]
836 #[case::float_one_int_two(1.0f64, 2, 0.5f64)]
837 #[case::float_one_two(1.0f64, 2.0f64, 0.5f64)]
838 fn test_metric_safe_div<L, R, E>(#[case] lhs: L, #[case] rhs: R, #[case] expected: E)
839 where
840 L: Into<Metric>,
841 R: Into<Metric>,
842 E: Into<Metric>,
843 {
844 let expected = expected.into();
845
846 let lhs = lhs.into();
847 let rhs = rhs.into();
848
849 assert_eq!(lhs.div0(rhs), expected);
850 }
851
852 #[rstest]
853 #[case::zero(0, 0, 0)]
854 #[case::one_zero(1, 0, 1)]
855 #[case::zero_one(0, 1, 1)]
856 #[case::u64_max(0, u64::MAX, u64::MAX)]
857 #[case::one_u64_max_saturates(1, u64::MAX, u64::MAX)]
858 #[case::one(1, 1, 2)]
859 #[case::two_one(2, 1, 3)]
860 #[case::one_two(1, 2, 3)]
861 #[case::float_one_int_zero(1.0f64, 0, 1.0f64)]
862 #[case::int_zero_float_one(0, 1.0f64, 1.0f64)]
863 #[case::float_zero(0.0f64, 0.0f64, 0.0f64)]
864 #[case::float_one(1.0f64, 1.0f64, 2.0f64)]
865 #[case::float_one_two(1.0f64, 2.0f64, 3.0f64)]
866 #[case::float_two_one(2.0f64, 1.0f64, 3.0f64)]
867 fn test_metric_add_and_add_assign<L, R, E>(#[case] lhs: L, #[case] rhs: R, #[case] expected: E)
868 where
869 L: Into<Metric>,
870 R: Into<Metric>,
871 E: Into<Metric>,
872 {
873 let expected = expected.into();
874
875 let mut lhs = lhs.into();
876 let rhs = rhs.into();
877
878 assert_eq!(lhs + rhs, expected);
879
880 lhs += rhs;
881 assert_eq!(lhs, expected);
882 }
883
884 #[rstest]
885 #[case::zero("0", 0)]
886 #[case::one("1", 1)]
887 #[case::u64_max(&format!("{}", u64::MAX), u64::MAX)]
888 #[case::one_below_u64_max(&format!("{}", u64::MAX - 1), u64::MAX - 1)]
889 #[case::zero_float("0.0", 0.0f64)]
890 #[case::one_float("1.0", 1.0f64)]
891 #[case::one_point("1.", 1.0f64)]
892 #[case::point_one(".1", 0.1f64)]
893 #[case::two_float("2.0", 2.0f64)]
894 #[case::neg_one_float("-1.0", -1.0f64)]
895 #[case::neg_two_float("-2.0", -2.0f64)]
896 #[case::inf("inf", f64::INFINITY)]
897 fn test_metric_from_str<E>(#[case] input: &str, #[case] expected: E)
898 where
899 E: Into<Metric>,
900 {
901 let expected = expected.into();
902 assert_eq!(input.parse::<Metric>().unwrap(), expected);
903 }
904
905 #[test]
906 fn test_metric_from_str_when_invalid_then_error() {
907 let err = "abc".parse::<Metric>().unwrap_err();
908 assert_eq!(
909 "Invalid metric: invalid float literal".to_owned(),
910 err.to_string()
911 );
912 }
913
914 #[rstest]
915 #[case::zero(0, 0, 0)]
916 #[case::zero_one(0, 1, 0)]
917 #[case::one(1, 1, 1)]
918 #[case::one_two(1, 2, 2)]
919 #[case::u64_max_one(u64::MAX, 1, u64::MAX)]
920 #[case::u64_max_two_saturates(u64::MAX, 2, u64::MAX)]
921 #[case::zero_float(0, 0.0f64, 0.0f64)]
922 #[case::zero_one_float(0, 1.0f64, 0.0f64)]
923 #[case::one_float(1, 1.0f64, 1.0f64)]
924 #[case::one_two_float(1, 2.0f64, 2.0f64)]
925 #[case::u64_max_two_float(u64::MAX, 2.0f64, 2.0f64 * (u64::MAX as f64))]
926 fn test_metric_mul_u64<B, E>(#[case] a: u64, #[case] b: B, #[case] expected: E)
927 where
928 B: Into<Metric>,
929 E: Into<Metric>,
930 {
931 let expected = expected.into();
932 let b = b.into();
933
934 assert_eq!(a * b, expected);
935 assert_eq!(b * a, expected);
936 }
937
938 #[rstest]
939 #[case::zero(0, 0, 0)]
940 #[case::one_zero(1, 0, 1)]
941 #[case::zero_one_saturates(0, 1, 0)]
942 #[case::u64_max_saturates(0, u64::MAX, 0)]
943 #[case::one_u64_max_saturates(1, u64::MAX, 0)]
944 #[case::u64_max_one(u64::MAX, 1, u64::MAX - 1)]
945 #[case::one(1, 1, 0)]
946 #[case::two_one(2, 1, 1)]
947 #[case::one_two(1, 2, 0)]
948 #[case::float_one_int_zero(1.0f64, 0, 1.0f64)]
949 #[case::int_zero_float_one(0, 1.0f64, -1.0f64)]
950 #[case::float_zero(0.0f64, 0.0f64, 0.0f64)]
951 #[case::float_one(1.0f64, 1.0f64, 0.0f64)]
952 #[case::float_one_two(1.0f64, 2.0f64, -1.0f64)]
953 #[case::float_two_one(2.0f64, 1.0f64, 1.0f64)]
954 fn test_metric_sub<L, R, E>(#[case] lhs: L, #[case] rhs: R, #[case] expected: E)
955 where
956 L: Into<Metric>,
957 R: Into<Metric>,
958 E: Into<Metric>,
959 {
960 let expected = expected.into();
961
962 let lhs = lhs.into();
963 let rhs = rhs.into();
964
965 assert_eq!(lhs - rhs, expected);
966 }
967
968 #[rstest]
969 #[case::zero(0, 0, Ordering::Equal)]
970 #[case::one_zero(1, 0, Ordering::Greater)]
971 #[case::zero_float(0.0f64, 0.0f64, Ordering::Equal)]
972 #[case::one_zero_float(1.0f64, 0.0f64, Ordering::Greater)]
973 #[case::one_int_zero_float(1, 0.0f64, Ordering::Greater)]
974 #[case::one_float_zero_int(1.0f64, 0, Ordering::Greater)]
975 #[case::some_number(220, 220.0f64, Ordering::Equal)]
976 fn test_metric_ordering<L, R>(#[case] lhs: L, #[case] rhs: R, #[case] expected: Ordering)
977 where
978 L: Into<Metric>,
979 R: Into<Metric>,
980 {
981 let lhs: Metric = lhs.into();
982 let rhs = rhs.into();
983
984 assert_eq!(lhs.cmp(&rhs), expected);
985 assert_eq!(rhs.cmp(&lhs), expected.reverse());
986 }
987
988 #[rstest]
989 #[case::new_zero(EitherOrBoth::Left(0), None)]
990 #[case::new_one(EitherOrBoth::Left(1), None)]
991 #[case::new_u64_max(EitherOrBoth::Left(u64::MAX), None)]
992 #[case::old_zero(EitherOrBoth::Right(0), None)]
993 #[case::old_one(EitherOrBoth::Right(1), None)]
994 #[case::old_u64_max(EitherOrBoth::Right(u64::MAX), None)]
995 #[case::both_zero(
996 EitherOrBoth::Both(0, 0),
997 (0f64, 1f64)
998 )]
999 #[case::both_one(
1000 EitherOrBoth::Both(1, 1),
1001 (0f64, 1f64)
1002 )]
1003 #[case::both_u64_max(
1004 EitherOrBoth::Both(u64::MAX, u64::MAX),
1005 (0f64, 1f64)
1006 )]
1007 #[case::new_one_old_zero(
1008 EitherOrBoth::Both(1, 0),
1009 (f64::INFINITY, f64::INFINITY)
1010 )]
1011 #[case::new_one_old_two(
1012 EitherOrBoth::Both(1, 2),
1013 (-50f64, -2f64)
1014 )]
1015 #[case::new_zero_old_one(
1016 EitherOrBoth::Both(0, 1),
1017 (-100f64, f64::NEG_INFINITY)
1018 )]
1019 #[case::new_two_old_one(
1020 EitherOrBoth::Both(2, 1),
1021 (100f64, 2f64)
1022 )]
1023 fn test_metrics_diff_new<T>(#[case] metrics: EitherOrBoth<u64>, #[case] expected_diffs: T)
1024 where
1025 T: Into<Option<(f64, f64)>>,
1026 {
1027 let expected = expected_metrics_diff(metrics.clone().map(Metric::Int), expected_diffs);
1028 let actual = MetricsDiff::new(metrics.map(Metric::Int));
1029
1030 assert_eq!(actual, expected);
1031 }
1032
1033 #[rstest]
1034 #[case::new_new(EitherOrBoth::Left(1), EitherOrBoth::Left(2), EitherOrBoth::Left(3))]
1035 #[case::new_old(
1036 EitherOrBoth::Left(1),
1037 EitherOrBoth::Right(2),
1038 EitherOrBoth::Both(1, 2)
1039 )]
1040 #[case::new_both(
1041 EitherOrBoth::Left(1),
1042 EitherOrBoth::Both(2, 5),
1043 EitherOrBoth::Both(3, 5)
1044 )]
1045 #[case::old_old(EitherOrBoth::Right(1), EitherOrBoth::Right(2), EitherOrBoth::Right(3))]
1046 #[case::old_new(
1047 EitherOrBoth::Right(1),
1048 EitherOrBoth::Left(2),
1049 EitherOrBoth::Both(2, 1)
1050 )]
1051 #[case::old_both(
1052 EitherOrBoth::Right(1),
1053 EitherOrBoth::Both(2, 5),
1054 EitherOrBoth::Both(2, 6)
1055 )]
1056 #[case::both_new(
1057 EitherOrBoth::Both(2, 5),
1058 EitherOrBoth::Left(1),
1059 EitherOrBoth::Both(3, 5)
1060 )]
1061 #[case::both_old(
1062 EitherOrBoth::Both(2, 5),
1063 EitherOrBoth::Right(1),
1064 EitherOrBoth::Both(2, 6)
1065 )]
1066 #[case::both_both(
1067 EitherOrBoth::Both(2, 5),
1068 EitherOrBoth::Both(1, 3),
1069 EitherOrBoth::Both(3, 8)
1070 )]
1071 #[case::saturating_new(
1072 EitherOrBoth::Left(u64::MAX),
1073 EitherOrBoth::Left(1),
1074 EitherOrBoth::Left(u64::MAX)
1075 )]
1076 #[case::saturating_new_other(
1077 EitherOrBoth::Left(1),
1078 EitherOrBoth::Left(u64::MAX),
1079 EitherOrBoth::Left(u64::MAX)
1080 )]
1081 #[case::saturating_old(
1082 EitherOrBoth::Right(u64::MAX),
1083 EitherOrBoth::Right(1),
1084 EitherOrBoth::Right(u64::MAX)
1085 )]
1086 #[case::saturating_old_other(
1087 EitherOrBoth::Right(1),
1088 EitherOrBoth::Right(u64::MAX),
1089 EitherOrBoth::Right(u64::MAX)
1090 )]
1091 #[case::saturating_both(
1092 EitherOrBoth::Both(u64::MAX, u64::MAX),
1093 EitherOrBoth::Both(1, 1),
1094 EitherOrBoth::Both(u64::MAX, u64::MAX)
1095 )]
1096 #[case::saturating_both_other(
1097 EitherOrBoth::Both(1, 1),
1098 EitherOrBoth::Both(u64::MAX, u64::MAX),
1099 EitherOrBoth::Both(u64::MAX, u64::MAX)
1100 )]
1101 fn test_metrics_diff_add(
1102 #[case] metric: EitherOrBoth<u64>,
1103 #[case] other_metric: EitherOrBoth<u64>,
1104 #[case] expected: EitherOrBoth<u64>,
1105 ) {
1106 let new_diff = MetricsDiff::new(metric.map(Metric::Int));
1107 let old_diff = MetricsDiff::new(other_metric.map(Metric::Int));
1108 let expected = MetricsDiff::new(expected.map(Metric::Int));
1109
1110 assert_eq!(new_diff.add(&old_diff), expected);
1111 assert_eq!(old_diff.add(&new_diff), expected);
1112 }
1113
1114 #[rstest]
1115 #[case::new_ir(&[0], &[], &[(EitherOrBoth::Left(Metric::Int(0)), None)])]
1116 #[case::new_is_summarized(&[10, 20, 30, 1, 2, 3, 4, 2, 0], &[],
1117 &[
1118 (EitherOrBoth::Left(Metric::Int(10)), None),
1119 (EitherOrBoth::Left(Metric::Int(20)), None),
1120 (EitherOrBoth::Left(Metric::Int(30)), None),
1121 (EitherOrBoth::Left(Metric::Int(1)), None),
1122 (EitherOrBoth::Left(Metric::Int(2)), None),
1123 (EitherOrBoth::Left(Metric::Int(3)), None),
1124 (EitherOrBoth::Left(Metric::Int(4)), None),
1125 (EitherOrBoth::Left(Metric::Int(2)), None),
1126 (EitherOrBoth::Left(Metric::Int(0)), None),
1127 (EitherOrBoth::Left(Metric::Int(54)), None),
1128 (EitherOrBoth::Left(Metric::Int(0)), None),
1129 (EitherOrBoth::Left(Metric::Int(6)), None),
1130 (EitherOrBoth::Left(Metric::Int(60)), None),
1131 (EitherOrBoth::Left(Metric::Int(264)), None),
1132 (EitherOrBoth::Left(Metric::Float(10f64)), None),
1133 (EitherOrBoth::Left(Metric::Float(10f64)), None),
1134 (EitherOrBoth::Left(Metric::Float(40f64)), None),
1135 (EitherOrBoth::Left(Metric::Float(4f64)), None),
1136 (EitherOrBoth::Left(Metric::Float(10f64)), None),
1137 (EitherOrBoth::Left(Metric::Float(90f64)), None),
1138 (EitherOrBoth::Left(Metric::Float(0f64)), None),
1139 (EitherOrBoth::Left(Metric::Float(10f64)), None),
1140 ]
1141 )]
1142 #[case::old_ir(&[], &[0], &[(EitherOrBoth::Right(Metric::Int(0)), None)])]
1143 #[case::old_is_summarized(&[], &[5, 10, 15, 1, 2, 3, 4, 1, 0],
1144 &[
1145 (EitherOrBoth::Right(Metric::Int(5)), None),
1146 (EitherOrBoth::Right(Metric::Int(10)), None),
1147 (EitherOrBoth::Right(Metric::Int(15)), None),
1148 (EitherOrBoth::Right(Metric::Int(1)), None),
1149 (EitherOrBoth::Right(Metric::Int(2)), None),
1150 (EitherOrBoth::Right(Metric::Int(3)), None),
1151 (EitherOrBoth::Right(Metric::Int(4)), None),
1152 (EitherOrBoth::Right(Metric::Int(1)), None),
1153 (EitherOrBoth::Right(Metric::Int(0)), None),
1154 (EitherOrBoth::Right(Metric::Int(24)), None),
1155 (EitherOrBoth::Right(Metric::Int(1)), None),
1156 (EitherOrBoth::Right(Metric::Int(5)), None),
1157 (EitherOrBoth::Right(Metric::Int(30)), None),
1158 (EitherOrBoth::Right(Metric::Int(204)), None),
1159 (EitherOrBoth::Right(Metric::Float(20f64)), None),
1160 (EitherOrBoth::Right(Metric::Float(20f64)), None),
1161 (EitherOrBoth::Right(Metric::Float(80f64)), None),
1162 (EitherOrBoth::Right(Metric::Float(4f64)), None),
1163 (EitherOrBoth::Right(Metric::Float(16.666_666_666_666_664_f64)), None),
1164 (EitherOrBoth::Right(Metric::Float(80f64)), None),
1165 (EitherOrBoth::Right(Metric::Float(3.333_333_333_333_333_5_f64)), None),
1166 (EitherOrBoth::Right(Metric::Float(16.666_666_666_666_664_f64)), None),
1167 ]
1168 )]
1169 #[case::new_and_old_ir_zero(&[0], &[0], &[
1170 (EitherOrBoth::Both(Metric::Int(0), Metric::Int(0)), (0f64, 1f64))
1171 ])]
1172 #[case::new_and_old_summarized_when_equal(
1173 &[10, 20, 30, 1, 2, 3, 4, 2, 0],
1174 &[10, 20, 30, 1, 2, 3, 4, 2, 0],
1175 &[
1176 (EitherOrBoth::Both(Metric::Int(10), Metric::Int(10)), (0f64, 1f64)),
1177 (EitherOrBoth::Both(Metric::Int(20), Metric::Int(20)), (0f64, 1f64)),
1178 (EitherOrBoth::Both(Metric::Int(30), Metric::Int(30)), (0f64, 1f64)),
1179 (EitherOrBoth::Both(Metric::Int(1), Metric::Int(1)), (0f64, 1f64)),
1180 (EitherOrBoth::Both(Metric::Int(2), Metric::Int(2)), (0f64, 1f64)),
1181 (EitherOrBoth::Both(Metric::Int(3), Metric::Int(3)), (0f64, 1f64)),
1182 (EitherOrBoth::Both(Metric::Int(4), Metric::Int(4)), (0f64, 1f64)),
1183 (EitherOrBoth::Both(Metric::Int(2), Metric::Int(2)), (0f64, 1f64)),
1184 (EitherOrBoth::Both(Metric::Int(0), Metric::Int(0)), (0f64, 1f64)),
1185 (EitherOrBoth::Both(Metric::Int(54), Metric::Int(54)), (0f64, 1f64)),
1186 (EitherOrBoth::Both(Metric::Int(0), Metric::Int(0)), (0f64, 1f64)),
1187 (EitherOrBoth::Both(Metric::Int(6), Metric::Int(6)), (0f64, 1f64)),
1188 (EitherOrBoth::Both(Metric::Int(60), Metric::Int(60)), (0f64, 1f64)),
1189 (EitherOrBoth::Both(Metric::Int(264), Metric::Int(264)), (0f64, 1f64)),
1190 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(10f64)), (0f64, 1f64)),
1191 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(10f64)), (0f64, 1f64)),
1192 (EitherOrBoth::Both(Metric::Float(40f64), Metric::Float(40f64)), (0f64, 1f64)),
1193 (EitherOrBoth::Both(Metric::Float(4f64), Metric::Float(4f64)), (0f64, 1f64)),
1194 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(10f64)), (0f64, 1f64)),
1195 (EitherOrBoth::Both(Metric::Float(90f64), Metric::Float(90f64)), (0f64, 1f64)),
1196 (EitherOrBoth::Both(Metric::Float(0f64), Metric::Float(0f64)), (0f64, 1f64)),
1197 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(10f64)), (0f64, 1f64)),
1198 ]
1199 )]
1200 #[case::new_and_old_summarized_when_not_equal(
1201 &[10, 20, 30, 1, 2, 3, 4, 2, 0],
1202 &[5, 10, 15, 1, 2, 3, 4, 1, 0],
1203 &[
1204 (EitherOrBoth::Both(Metric::Int(10), Metric::Int(5)), (100f64, 2f64)),
1205 (EitherOrBoth::Both(Metric::Int(20), Metric::Int(10)), (100f64, 2f64)),
1206 (EitherOrBoth::Both(Metric::Int(30), Metric::Int(15)), (100f64, 2f64)),
1207 (EitherOrBoth::Both(Metric::Int(1), Metric::Int(1)), (0f64, 1f64)),
1208 (EitherOrBoth::Both(Metric::Int(2), Metric::Int(2)), (0f64, 1f64)),
1209 (EitherOrBoth::Both(Metric::Int(3), Metric::Int(3)), (0f64, 1f64)),
1210 (EitherOrBoth::Both(Metric::Int(4), Metric::Int(4)), (0f64, 1f64)),
1211 (EitherOrBoth::Both(Metric::Int(2), Metric::Int(1)), (100f64, 2f64)),
1212 (EitherOrBoth::Both(Metric::Int(0), Metric::Int(0)), (0f64, 1f64)),
1213 (EitherOrBoth::Both(Metric::Int(54), Metric::Int(24)), (125f64, 2.25f64)),
1214 (EitherOrBoth::Both(Metric::Int(0), Metric::Int(1)), (-100f64, f64::NEG_INFINITY)),
1215 (EitherOrBoth::Both(Metric::Int(6), Metric::Int(5)), (20f64, 1.2f64)),
1216 (EitherOrBoth::Both(Metric::Int(60), Metric::Int(30)), (100f64, 2f64)),
1217 (EitherOrBoth::Both(Metric::Int(264), Metric::Int(204)),
1218 (29.411_764_705_882_355_f64, 1.294_117_647_058_823_6_f64)
1219 ),
1220 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(20f64)), (-50f64, -2f64)),
1221 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(20f64)), (-50f64, -2f64)),
1222 (EitherOrBoth::Both(Metric::Float(40f64), Metric::Float(80f64)), (-50f64, -2f64)),
1223 (EitherOrBoth::Both(Metric::Float(4f64), Metric::Float(4f64)), (0f64, 1f64)),
1224 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(16.666_666_666_666_664_f64)),
1225 (-39.999_999_999_999_99_f64, -1.666_666_666_666_666_5_f64)
1226 ),
1227 (EitherOrBoth::Both(Metric::Float(90f64), Metric::Float(80f64)), (12.5f64, 1.125f64)),
1228 (EitherOrBoth::Both(Metric::Float(0f64), Metric::Float(3.333_333_333_333_333_5_f64)),
1229 (-100f64, f64::NEG_INFINITY)
1230 ),
1231 (EitherOrBoth::Both(Metric::Float(10f64), Metric::Float(16.666_666_666_666_664_f64)),
1232 (-39.999_999_999_999_99_f64, -1.666_666_666_666_666_5_f64)
1233 ),
1234 ]
1235 )]
1236 fn test_metrics_summary_new<V>(
1237 #[case] new_metrics: &[u64],
1238 #[case] old_metrics: &[u64],
1239 #[case] expected: &[(EitherOrBoth<Metric>, V)],
1240 ) where
1241 V: Into<Option<(f64, f64)>> + Clone,
1242 {
1243 use crate::util::EitherOrBoth;
1244
1245 let expected_metrics_summary =
1246 metrics_summary_fixture(expected.iter().map(|(e, v)| (e.clone(), v.clone())));
1247 let actual = match (
1248 (!new_metrics.is_empty()).then_some(new_metrics),
1249 (!old_metrics.is_empty()).then_some(old_metrics),
1250 ) {
1251 (None, None) => unreachable!(),
1252 (Some(new), None) => MetricsSummary::new(EitherOrBoth::Left(metrics_fixture(new))),
1253 (None, Some(old)) => MetricsSummary::new(EitherOrBoth::Right(metrics_fixture(old))),
1254 (Some(new), Some(old)) => MetricsSummary::new(EitherOrBoth::Both(
1255 metrics_fixture(new),
1256 metrics_fixture(old),
1257 )),
1258 };
1259
1260 assert_eq!(actual, expected_metrics_summary);
1261 }
1262}