1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 
5 use inherent::inherent;
6 
7 use uuid::Uuid;
8 
9 use super::{CommonMetricData, MetricId};
10 
11 use crate::ipc::need_ipc;
12 
13 /// A UUID metric.
14 ///
15 /// Stores UUID values.
16 pub enum UuidMetric {
17     Parent(glean::private::UuidMetric),
18     Child(UuidMetricIpc),
19 }
20 
21 #[derive(Debug)]
22 pub struct UuidMetricIpc;
23 
24 impl UuidMetric {
25     /// Create a new UUID metric.
new(_id: MetricId, meta: CommonMetricData) -> Self26     pub fn new(_id: MetricId, meta: CommonMetricData) -> Self {
27         if need_ipc() {
28             UuidMetric::Child(UuidMetricIpc)
29         } else {
30             UuidMetric::Parent(glean::private::UuidMetric::new(meta))
31         }
32     }
33 
34     #[cfg(test)]
child_metric(&self) -> Self35     pub(crate) fn child_metric(&self) -> Self {
36         match self {
37             UuidMetric::Parent(_) => UuidMetric::Child(UuidMetricIpc),
38             UuidMetric::Child(_) => panic!("Can't get a child metric from a child metric"),
39         }
40     }
41 }
42 
43 #[inherent(pub)]
44 impl glean::traits::Uuid for UuidMetric {
45     /// Set to the specified value.
46     ///
47     /// ## Arguments
48     ///
49     /// * `value` - The UUID to set the metric to.
set(&self, value: Uuid)50     fn set(&self, value: Uuid) {
51         match self {
52             UuidMetric::Parent(p) => {
53                 glean::traits::Uuid::set(&*p, value);
54             }
55             UuidMetric::Child(_c) => {
56                 log::error!("Unable to set the uuid metric in non-main process. Ignoring.");
57                 // TODO: Record an error.
58             }
59         };
60     }
61 
62     /// Generate a new random UUID and set the metric to it.
63     ///
64     /// ## Return value
65     ///
66     /// Returns the stored UUID value or `Uuid::nil` if called from
67     /// a non-parent process.
generate_and_set(&self) -> Uuid68     fn generate_and_set(&self) -> Uuid {
69         match self {
70             UuidMetric::Parent(p) => glean::traits::Uuid::generate_and_set(&*p),
71             UuidMetric::Child(_c) => {
72                 log::error!("Unable to set the uuid metric in non-main process. Ignoring.");
73                 // TODO: Record an error.
74                 Uuid::nil()
75             }
76         }
77     }
78 
79     /// **Test-only API.**
80     ///
81     /// Get the stored UUID value.
82     /// This doesn't clear the stored value.
83     ///
84     /// ## Arguments
85     ///
86     /// * `storage_name` - the storage name to look into.
87     ///
88     /// ## Return value
89     ///
90     /// Returns the stored value or `None` if nothing stored.
test_get_value<'a, S: Into<Option<&'a str>>>(&self, storage_name: S) -> Option<Uuid>91     fn test_get_value<'a, S: Into<Option<&'a str>>>(&self, storage_name: S) -> Option<Uuid> {
92         match self {
93             UuidMetric::Parent(p) => p.test_get_value(storage_name),
94             UuidMetric::Child(_c) => panic!("Cannot get test value for in non-parent process!"),
95         }
96     }
97 
98     /// **Exported for test purposes.**
99     ///
100     /// Gets the number of recorded errors for the given metric and error type.
101     ///
102     /// # Arguments
103     ///
104     /// * `error` - The type of error
105     /// * `ping_name` - represents the optional name of the ping to retrieve the
106     ///   metric for. Defaults to the first value in `send_in_pings`.
107     ///
108     /// # Returns
109     ///
110     /// The number of errors reported.
test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>( &self, error: glean::ErrorType, ping_name: S, ) -> i32111     fn test_get_num_recorded_errors<'a, S: Into<Option<&'a str>>>(
112         &self,
113         error: glean::ErrorType,
114         ping_name: S,
115     ) -> i32 {
116         match self {
117             UuidMetric::Parent(p) => p.test_get_num_recorded_errors(error, ping_name),
118             UuidMetric::Child(_c) => {
119                 panic!("Cannot get test value for UuidMetric in non-parent process!")
120             }
121         }
122     }
123 }
124 
125 #[cfg(test)]
126 mod test {
127     use super::*;
128     use crate::{common_test::*, ipc, metrics};
129 
130     #[test]
sets_uuid_value()131     fn sets_uuid_value() {
132         let _lock = lock_test();
133 
134         let metric = &metrics::test_only_ipc::a_uuid;
135         let expected = Uuid::new_v4();
136         metric.set(expected.clone());
137 
138         assert_eq!(expected, metric.test_get_value("store1").unwrap());
139     }
140 
141     #[test]
uuid_ipc()142     fn uuid_ipc() {
143         // UuidMetric doesn't support IPC.
144         let _lock = lock_test();
145 
146         let parent_metric = &metrics::test_only_ipc::a_uuid;
147         let expected = Uuid::new_v4();
148         parent_metric.set(expected.clone());
149 
150         {
151             let child_metric = parent_metric.child_metric();
152 
153             // Instrumentation calls do not panic.
154             child_metric.set(Uuid::new_v4());
155 
156             // (They also shouldn't do anything,
157             // but that's not something we can inspect in this test)
158         }
159 
160         assert!(ipc::replay_from_buf(&ipc::take_buf().unwrap()).is_ok());
161 
162         assert_eq!(
163             expected,
164             parent_metric.test_get_value("store1").unwrap(),
165             "UUID metrics should only work in the parent process"
166         );
167     }
168 }
169