// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. mod common; use crate::common::*; use glean_core::metrics::*; use glean_core::ping::PingMaker; use glean_core::{CommonMetricData, Glean, Lifetime}; fn set_up_basic_ping() -> (Glean, PingMaker, PingType, tempfile::TempDir) { let (tempdir, _) = tempdir(); let (mut glean, t) = new_glean(Some(tempdir)); let ping_maker = PingMaker::new(); let ping_type = PingType::new("store1", true, false, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); // Record something, so the ping will have data let metric = BooleanMetric::new(CommonMetricData { name: "boolean_metric".into(), category: "telemetry".into(), send_in_pings: vec!["store1".into()], disabled: false, lifetime: Lifetime::User, ..Default::default() }); metric.set_sync(&glean, true); (glean, ping_maker, ping_type, t) } #[test] fn ping_info_must_contain_a_nonempty_start_and_end_time() { let (glean, ping_maker, ping_type, _t) = set_up_basic_ping(); let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let ping_info = ping.content["ping_info"].as_object().unwrap(); let start_time_str = ping_info["start_time"].as_str().unwrap(); let start_time_date = iso8601_to_chrono(&iso8601::datetime(start_time_str).unwrap()); let end_time_str = ping_info["end_time"].as_str().unwrap(); let end_time_date = iso8601_to_chrono(&iso8601::datetime(end_time_str).unwrap()); assert!(start_time_date <= end_time_date); } #[test] fn get_ping_info_must_report_all_the_required_fields() { let (glean, ping_maker, ping_type, _t) = set_up_basic_ping(); let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let ping_info = ping.content["ping_info"].as_object().unwrap(); assert!(ping_info.get("start_time").is_some()); assert!(ping_info.get("end_time").is_some()); assert!(ping_info.get("seq").is_some()); } #[test] fn get_client_info_must_report_all_the_available_data() { let (glean, ping_maker, ping_type, _t) = set_up_basic_ping(); let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let client_info = ping.content["client_info"].as_object().unwrap(); client_info["telemetry_sdk_build"].as_str().unwrap(); } #[test] fn test_metrics_must_report_experimentation_id() { let (tempdir, _) = tempdir(); let mut glean = Glean::new(glean_core::InternalConfiguration { data_path: tempdir.path().display().to_string(), application_id: GLOBAL_APPLICATION_ID.into(), language_binding_name: "Rust".into(), upload_enabled: true, max_events: None, delay_ping_lifetime_io: false, app_build: "Unknown".into(), use_core_mps: false, trim_data_to_registered_pings: false, log_level: None, rate_limit: None, enable_event_timestamps: true, experimentation_id: Some("test-experimentation-id".to_string()), enable_internal_pings: true, ping_schedule: Default::default(), ping_lifetime_threshold: 0, ping_lifetime_max_time: 0, }) .unwrap(); let ping_maker = PingMaker::new(); let ping_type = PingType::new("store1", true, false, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); // Record something, so the ping will have data let metric = BooleanMetric::new(CommonMetricData { name: "boolean_metric".into(), category: "telemetry".into(), send_in_pings: vec!["store1".into()], disabled: false, lifetime: Lifetime::User, ..Default::default() }); metric.set_sync(&glean, true); let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let metrics = ping.content["metrics"].as_object().unwrap(); println!("TLDEBUG Metrics:\n{:?}", metrics); let strings = metrics["string"].as_object().unwrap(); assert_eq!( strings["glean.client.annotation.experimentation_id"] .as_str() .unwrap(), "test-experimentation-id", "experimentation ids must match" ); } #[test] fn experimentation_id_is_removed_if_send_if_empty_is_false() { // Initialize Glean with an experimentation id, it should be removed if the ping is empty // and send_if_empty is false. let (tempdir, _) = tempdir(); let mut glean = Glean::new(glean_core::InternalConfiguration { data_path: tempdir.path().display().to_string(), application_id: GLOBAL_APPLICATION_ID.into(), language_binding_name: "Rust".into(), upload_enabled: true, max_events: None, delay_ping_lifetime_io: false, app_build: "Unknown".into(), use_core_mps: false, trim_data_to_registered_pings: false, log_level: None, rate_limit: None, enable_event_timestamps: true, experimentation_id: Some("test-experimentation-id".to_string()), enable_internal_pings: true, ping_schedule: Default::default(), ping_lifetime_threshold: 0, ping_lifetime_max_time: 0, }) .unwrap(); let ping_maker = PingMaker::new(); let unknown_ping_type = PingType::new("unknown", true, false, true, true, true, vec![], vec![]); glean.register_ping_type(&unknown_ping_type); assert!(ping_maker .collect(&glean, &unknown_ping_type, None, "", "") .is_none()); } #[test] fn collect_must_report_none_when_no_data_is_stored() { // NOTE: This is a behavior change from glean-ac which returned an empty // string in this case. As this is an implementation detail and not part of // the public API, it's safe to change this. let (mut glean, ping_maker, ping_type, _t) = set_up_basic_ping(); let unknown_ping_type = PingType::new("unknown", true, false, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); assert!(ping_maker .collect(&glean, &unknown_ping_type, None, "", "") .is_none()); } #[test] fn seq_number_must_be_sequential() { let (glean, ping_maker, _ping_type, _t) = set_up_basic_ping(); let metric = BooleanMetric::new(CommonMetricData { name: "boolean_metric".into(), category: "telemetry".into(), send_in_pings: vec!["store2".into()], disabled: false, lifetime: Lifetime::User, ..Default::default() }); metric.set_sync(&glean, true); for i in 0..=1 { for ping_name in ["store1", "store2"].iter() { let ping_type = PingType::new(*ping_name, true, false, true, true, true, vec![], vec![]); let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let seq_num = ping.content["ping_info"]["seq"].as_i64().unwrap(); // Ensure sequence numbers in different stores are independent of // each other assert_eq!(i, seq_num); } } // Test that ping sequence numbers increase independently. { let ping_type = PingType::new("store1", true, false, true, true, true, vec![], vec![]); // 3rd ping of store1 let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let seq_num = ping.content["ping_info"]["seq"].as_i64().unwrap(); assert_eq!(2, seq_num); // 4th ping of store1 let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let seq_num = ping.content["ping_info"]["seq"].as_i64().unwrap(); assert_eq!(3, seq_num); } { let ping_type = PingType::new("store2", true, false, true, true, true, vec![], vec![]); // 3rd ping of store2 let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let seq_num = ping.content["ping_info"]["seq"].as_i64().unwrap(); assert_eq!(2, seq_num); } { let ping_type = PingType::new("store1", true, false, true, true, true, vec![], vec![]); // 5th ping of store1 let ping = ping_maker .collect(&glean, &ping_type, None, "", "") .unwrap(); let seq_num = ping.content["ping_info"]["seq"].as_i64().unwrap(); assert_eq!(4, seq_num); } } #[test] fn clear_pending_pings() { let (mut glean, _t) = new_glean(None); let ping_maker = PingMaker::new(); let ping_type = PingType::new("store1", true, false, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); // Record something, so the ping will have data let metric = BooleanMetric::new(CommonMetricData { name: "boolean_metric".into(), category: "telemetry".into(), send_in_pings: vec!["store1".into()], disabled: false, lifetime: Lifetime::User, ..Default::default() }); metric.set_sync(&glean, true); assert!(ping_type.submit_sync(&glean, None)); assert_eq!(1, get_queued_pings(glean.get_data_path()).unwrap().len()); assert!(ping_maker .clear_pending_pings(glean.get_data_path()) .is_ok()); assert_eq!(0, get_queued_pings(glean.get_data_path()).unwrap().len()); } #[test] fn no_pings_submitted_if_upload_disabled() { // Regression test, bug 1603571 let (mut glean, _t) = new_glean(None); let ping_type = PingType::new("store1", true, true, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); assert!(ping_type.submit_sync(&glean, None)); assert_eq!(1, get_queued_pings(glean.get_data_path()).unwrap().len()); // Disable upload, then try to sumbit glean.set_upload_enabled(false); // Test again through the direct call assert!(!ping_type.submit_sync(&glean, None)); assert_eq!(0, get_queued_pings(glean.get_data_path()).unwrap().len()); } #[test] fn metadata_is_correctly_added_when_necessary() { let (mut glean, _t) = new_glean(None); glean.set_debug_view_tag("valid-tag"); let ping_type = PingType::new("store1", true, true, true, true, true, vec![], vec![]); glean.register_ping_type(&ping_type); assert!(ping_type.submit_sync(&glean, None)); let (_, _, metadata) = &get_queued_pings(glean.get_data_path()).unwrap()[0]; let headers = metadata.as_ref().unwrap().get("headers").unwrap(); assert_eq!(headers.get("X-Debug-ID").unwrap(), "valid-tag"); }