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 mod common;
6 use crate::common::*;
7 
8 use serde_json::json;
9 
10 use glean_core::metrics::*;
11 use glean_core::storage::StorageManager;
12 use glean_core::{test_get_num_recorded_errors, CommonMetricData, ErrorType, Lifetime};
13 
14 #[test]
list_can_store_multiple_items()15 fn list_can_store_multiple_items() {
16     let (glean, _t) = new_glean(None);
17 
18     let list: StringListMetric = StringListMetric::new(CommonMetricData {
19         name: "list".into(),
20         category: "local".into(),
21         send_in_pings: vec!["core".into()],
22         ..Default::default()
23     });
24 
25     list.add(&glean, "first");
26     assert_eq!(list.test_get_value(&glean, "core").unwrap(), vec!["first"]);
27 
28     list.add(&glean, "second");
29     assert_eq!(
30         list.test_get_value(&glean, "core").unwrap(),
31         vec!["first", "second"]
32     );
33 
34     list.set(&glean, vec!["third".into()]);
35     assert_eq!(list.test_get_value(&glean, "core").unwrap(), vec!["third"]);
36 
37     list.add(&glean, "fourth");
38     assert_eq!(
39         list.test_get_value(&glean, "core").unwrap(),
40         vec!["third", "fourth"]
41     );
42 }
43 
44 #[test]
stringlist_serializer_should_correctly_serialize_stringlists()45 fn stringlist_serializer_should_correctly_serialize_stringlists() {
46     let (mut tempdir, _) = tempdir();
47 
48     {
49         // We give tempdir to the `new_glean` function...
50         let (glean, dir) = new_glean(Some(tempdir));
51         // And then we get it back once that function returns.
52         tempdir = dir;
53 
54         let metric = StringListMetric::new(CommonMetricData {
55             name: "string_list_metric".into(),
56             category: "telemetry.test".into(),
57             send_in_pings: vec!["store1".into()],
58             disabled: false,
59             lifetime: Lifetime::User,
60             ..Default::default()
61         });
62         metric.set(&glean, vec!["test_string_1".into(), "test_string_2".into()]);
63     }
64 
65     {
66         let (glean, _) = new_glean(Some(tempdir));
67 
68         let snapshot = StorageManager
69             .snapshot_as_json(glean.storage(), "store1", true)
70             .unwrap();
71         assert_eq!(
72             json!({"string_list": {"telemetry.test.string_list_metric": ["test_string_1", "test_string_2"]}}),
73             snapshot
74         );
75     }
76 }
77 
78 #[test]
set_properly_sets_the_value_in_all_stores()79 fn set_properly_sets_the_value_in_all_stores() {
80     let (glean, _t) = new_glean(None);
81     let store_names: Vec<String> = vec!["store1".into(), "store2".into()];
82 
83     let metric = StringListMetric::new(CommonMetricData {
84         name: "string_list_metric".into(),
85         category: "telemetry.test".into(),
86         send_in_pings: store_names.clone(),
87         disabled: false,
88         lifetime: Lifetime::Ping,
89         ..Default::default()
90     });
91 
92     metric.set(&glean, vec!["test_string_1".into(), "test_string_2".into()]);
93 
94     for store_name in store_names {
95         let snapshot = StorageManager
96             .snapshot_as_json(glean.storage(), &store_name, true)
97             .unwrap();
98 
99         assert_eq!(
100             json!({"string_list": {"telemetry.test.string_list_metric": ["test_string_1", "test_string_2"]}}),
101             snapshot
102         );
103     }
104 }
105 
106 #[test]
long_string_values_are_truncated()107 fn long_string_values_are_truncated() {
108     let (glean, _t) = new_glean(None);
109 
110     let metric = StringListMetric::new(CommonMetricData {
111         name: "string_list_metric".into(),
112         category: "telemetry.test".into(),
113         send_in_pings: vec!["store1".into()],
114         disabled: false,
115         lifetime: Lifetime::Ping,
116         ..Default::default()
117     });
118 
119     let test_string = "0123456789".repeat(20);
120     metric.add(&glean, test_string.clone());
121 
122     // Ensure the string was truncated to the proper length.
123     assert_eq!(
124         vec![test_string[..50].to_string()],
125         metric.test_get_value(&glean, "store1").unwrap()
126     );
127 
128     // Ensure the error has been recorded.
129     assert_eq!(
130         Ok(1),
131         test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidOverflow, None)
132     );
133 
134     metric.set(&glean, vec![test_string.clone()]);
135 
136     // Ensure the string was truncated to the proper length.
137     assert_eq!(
138         vec![test_string[..50].to_string()],
139         metric.test_get_value(&glean, "store1").unwrap()
140     );
141 
142     // Ensure the error has been recorded.
143     assert_eq!(
144         Ok(2),
145         test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidOverflow, None)
146     );
147 }
148 
149 #[test]
disabled_string_lists_dont_record()150 fn disabled_string_lists_dont_record() {
151     let (glean, _t) = new_glean(None);
152 
153     let metric = StringListMetric::new(CommonMetricData {
154         name: "string_list_metric".into(),
155         category: "telemetry.test".into(),
156         send_in_pings: vec!["store1".into()],
157         disabled: true,
158         lifetime: Lifetime::Ping,
159         ..Default::default()
160     });
161 
162     metric.add(&glean, "test_string".repeat(20));
163 
164     // Ensure the string was not added.
165     assert_eq!(None, metric.test_get_value(&glean, "store1"));
166 
167     metric.set(&glean, vec!["test_string_2".repeat(20)]);
168 
169     // Ensure the stringlist was not set.
170     assert_eq!(None, metric.test_get_value(&glean, "store1"));
171 
172     // Ensure no error was recorded.
173     assert!(
174         test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidValue, None).is_err()
175     );
176 }
177 
178 #[test]
string_lists_dont_exceed_max_items()179 fn string_lists_dont_exceed_max_items() {
180     let (glean, _t) = new_glean(None);
181 
182     let metric = StringListMetric::new(CommonMetricData {
183         name: "string_list_metric".into(),
184         category: "telemetry.test".into(),
185         send_in_pings: vec!["store1".into()],
186         disabled: false,
187         lifetime: Lifetime::Ping,
188         ..Default::default()
189     });
190 
191     for _n in 1..21 {
192         metric.add(&glean, "test_string");
193     }
194 
195     let expected: Vec<String> = "test_string "
196         .repeat(20)
197         .split_whitespace()
198         .map(|s| s.to_string())
199         .collect();
200     assert_eq!(expected, metric.test_get_value(&glean, "store1").unwrap());
201 
202     // Ensure the 21st string wasn't added.
203     metric.add(&glean, "test_string");
204     assert_eq!(expected, metric.test_get_value(&glean, "store1").unwrap());
205 
206     // Ensure we recorded the error.
207     assert_eq!(
208         Ok(1),
209         test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidValue, None)
210     );
211 
212     // Try to set it to a list that's too long. Ensure it cuts off at 20 elements.
213     let too_many: Vec<String> = "test_string "
214         .repeat(21)
215         .split_whitespace()
216         .map(|s| s.to_string())
217         .collect();
218     metric.set(&glean, too_many);
219     assert_eq!(expected, metric.test_get_value(&glean, "store1").unwrap());
220 
221     assert_eq!(
222         Ok(2),
223         test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidValue, None)
224     );
225 }
226 
227 #[test]
set_does_not_record_error_when_receiving_empty_list()228 fn set_does_not_record_error_when_receiving_empty_list() {
229     let (glean, _t) = new_glean(None);
230 
231     let metric = StringListMetric::new(CommonMetricData {
232         name: "string_list_metric".into(),
233         category: "telemetry.test".into(),
234         send_in_pings: vec!["store1".into()],
235         disabled: false,
236         lifetime: Lifetime::Ping,
237         ..Default::default()
238     });
239 
240     metric.set(&glean, vec![]);
241 
242     // Ensure the empty list was added
243     assert_eq!(Some(vec![]), metric.test_get_value(&glean, "store1"));
244 
245     // Ensure we didn't record an error.
246     assert!(
247         test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidValue, None).is_err()
248     );
249 }
250