diff options
Diffstat (limited to 'third_party/rust/glean/src/test.rs')
-rw-r--r-- | third_party/rust/glean/src/test.rs | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/third_party/rust/glean/src/test.rs b/third_party/rust/glean/src/test.rs new file mode 100644 index 0000000000..789177706a --- /dev/null +++ b/third_party/rust/glean/src/test.rs @@ -0,0 +1,797 @@ +// 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/. + +use crate::private::PingType; +use crate::private::{BooleanMetric, CounterMetric}; +use std::path::PathBuf; + +use super::*; +use crate::common_test::{lock_test, new_glean, GLOBAL_APPLICATION_ID}; + +#[test] +fn send_a_ping() { + let _lock = lock_test(); + + let (s, r) = crossbeam_channel::bounded::<String>(1); + + // Define a fake uploader that reports back the submission URL + // using a crossbeam channel. + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<String>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + url: String, + _body: Vec<u8>, + _headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(url).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }; + + let _t = new_glean(Some(cfg), true); + crate::block_on_dispatcher(); + + // Define a new ping and submit it. + const PING_NAME: &str = "test-ping"; + let custom_ping = private::PingType::new(PING_NAME, true, true, vec![]); + custom_ping.submit(None); + + // Wait for the ping to arrive. + let url = r.recv().unwrap(); + assert_eq!(url.contains(PING_NAME), true); +} + +#[test] +fn disabling_upload_disables_metrics_recording() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + crate::block_on_dispatcher(); + + let metric = BooleanMetric::new(CommonMetricData { + name: "bool_metric".into(), + category: "test".into(), + send_in_pings: vec!["store1".into()], + lifetime: Lifetime::Application, + disabled: false, + dynamic_label: None, + }); + + crate::set_upload_enabled(false); + + assert!(metric.test_get_value("store1").is_none()) +} + +#[test] +fn test_experiments_recording() { + let _lock = lock_test(); + + let _t = new_glean(None, true); + + set_experiment_active("experiment_test".to_string(), "branch_a".to_string(), None); + let mut extra = HashMap::new(); + extra.insert("test_key".to_string(), "value".to_string()); + set_experiment_active( + "experiment_api".to_string(), + "branch_b".to_string(), + Some(extra), + ); + assert!(test_is_experiment_active("experiment_test".to_string())); + assert!(test_is_experiment_active("experiment_api".to_string())); + set_experiment_inactive("experiment_test".to_string()); + assert!(!test_is_experiment_active("experiment_test".to_string())); + assert!(test_is_experiment_active("experiment_api".to_string())); + let stored_data = test_get_experiment_data("experiment_api".to_string()); + assert_eq!("branch_b", stored_data.branch); + assert_eq!("value", stored_data.extra.unwrap()["test_key"]); +} + +#[test] +fn test_experiments_recording_before_glean_inits() { + let _lock = lock_test(); + + // Destroy the existing glean instance from glean-core so that we + // can test the pre-init queueing of the experiment api commands. + // This is doing the exact same thing that `reset_glean` is doing + // but without calling `initialize`. + if was_initialize_called() { + // We need to check if the Glean object (from glean-core) is + // initialized, otherwise this will crash on the first test + // due to bug 1675215 (this check can be removed once that + // bug is fixed). + if global_glean().is_some() { + with_glean_mut(|glean| { + glean.test_clear_all_stores(); + glean.destroy_db(); + }); + } + // Allow us to go through initialization again. + INITIALIZE_CALLED.store(false, Ordering::SeqCst); + // Reset the dispatcher. + dispatcher::reset_dispatcher(); + } + + set_experiment_active( + "experiment_set_preinit".to_string(), + "branch_a".to_string(), + None, + ); + set_experiment_active( + "experiment_preinit_disabled".to_string(), + "branch_a".to_string(), + None, + ); + set_experiment_inactive("experiment_preinit_disabled".to_string()); + + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + test_reset_glean( + Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }, + ClientInfoMetrics::unknown(), + false, + ); + crate::block_on_dispatcher(); + + assert!(test_is_experiment_active( + "experiment_set_preinit".to_string() + )); + assert!(!test_is_experiment_active( + "experiment_preinit_disabled".to_string() + )); +} + +#[test] +#[ignore] // TODO: To be done in bug 1673645. +fn test_sending_of_foreground_background_pings() { + todo!() +} + +#[test] +#[ignore] // TODO: To be done in bug 1672958. +fn test_sending_of_startup_baseline_ping() { + todo!() +} + +#[test] +fn initialize_must_not_crash_if_data_dir_is_messed_up() { + let _lock = lock_test(); + + let dir = tempfile::tempdir().unwrap(); + let tmpdirname = dir.path().display().to_string(); + // Create a file in the temporary dir and use that as the + // name of the Glean data dir. + let file_path = PathBuf::from(tmpdirname).join("notadir"); + std::fs::write(file_path.clone(), "test").expect("The test Glean dir file must be created"); + + let cfg = Configuration { + data_path: file_path.to_string_lossy().to_string(), + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }; + + test_reset_glean(cfg, ClientInfoMetrics::unknown(), false); + // TODO(bug 1675215): ensure initialize runs through dispatcher. + // Glean init is async and, for this test, it bails out early due to + // an caused by not being able to create the data dir: we can do nothing + // but wait. Tests in other bindings use the dispatcher's test mode, which + // runs tasks sequentially on the main thread, so no sleep is required, + // because we're guaranteed that, once we reach this point, the full + // init potentially ran. + std::thread::sleep(std::time::Duration::from_secs(3)); +} + +#[test] +fn queued_recorded_metrics_correctly_record_during_init() { + let _lock = lock_test(); + + destroy_glean(true); + + let metric = CounterMetric::new(CommonMetricData { + name: "counter_metric".into(), + category: "test".into(), + send_in_pings: vec!["store1".into()], + lifetime: Lifetime::Application, + disabled: false, + dynamic_label: None, + }); + + // This will queue 3 tasks that will add to the metric value once Glean is initialized + for _ in 0..3 { + metric.add(1); + } + + // TODO: To be fixed in bug 1677150. + // Ensure that no value has been stored yet since the tasks have only been queued + // and not executed yet + + // Calling `new_glean` here will cause Glean to be initialized and should cause the queued + // tasks recording metrics to execute + let _t = new_glean(None, false); + + // Verify that the callback was executed by testing for the correct value + assert!(metric.test_get_value(None).is_some(), "Value must exist"); + assert_eq!(3, metric.test_get_value(None).unwrap(), "Value must match"); +} + +#[test] +fn initializing_twice_is_a_noop() { + let _lock = lock_test(); + + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + test_reset_glean( + Configuration { + data_path: tmpname.clone(), + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }, + ClientInfoMetrics::unknown(), + true, + ); + + crate::block_on_dispatcher(); + + test_reset_glean( + Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }, + ClientInfoMetrics::unknown(), + false, + ); + + // TODO(bug 1675215): ensure initialize runs through dispatcher. + // Glean init is async and, for this test, it bails out early due to + // being initialized: we can do nothing but wait. Tests in other bindings use + // the dispatcher's test mode, which runs tasks sequentially on the main + // thread, so no sleep is required. Bug 1675215 might fix this, as well. + std::thread::sleep(std::time::Duration::from_secs(3)); +} + +#[test] +#[ignore] // TODO: To be done in bug 1673668. +fn dont_handle_events_when_uninitialized() { + todo!() +} + +#[test] +fn the_app_channel_must_be_correctly_set_if_requested() { + let _lock = lock_test(); + + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + // No appChannel must be set if nothing was provided through the config + // options. + test_reset_glean( + Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: None, + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }, + ClientInfoMetrics::unknown(), + true, + ); + assert!(core_metrics::internal_metrics::app_channel + .test_get_value(None) + .is_none()); + + // The appChannel must be correctly reported if a channel value + // was provided. + let _t = new_glean(None, true); + assert_eq!( + "testing", + core_metrics::internal_metrics::app_channel + .test_get_value(None) + .unwrap() + ); +} + +#[test] +#[ignore] // TODO: To be done in bug 1673672. +fn ping_collection_must_happen_after_concurrently_scheduled_metrics_recordings() { + todo!() +} + +#[test] +fn basic_metrics_should_be_cleared_when_disabling_uploading() { + let _lock = lock_test(); + + let _t = new_glean(None, false); + + let metric = private::StringMetric::new(CommonMetricData { + name: "string_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["default".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }); + + assert!(metric.test_get_value(None).is_none()); + + metric.set("TEST VALUE"); + assert!(metric.test_get_value(None).is_some()); + + set_upload_enabled(false); + assert!(metric.test_get_value(None).is_none()); + metric.set("TEST VALUE"); + assert!(metric.test_get_value(None).is_none()); + + set_upload_enabled(true); + assert!(metric.test_get_value(None).is_none()); + metric.set("TEST VALUE"); + assert_eq!("TEST VALUE", metric.test_get_value(None).unwrap()); +} + +#[test] +fn core_metrics_should_be_cleared_and_restored_when_disabling_and_enabling_uploading() { + let _lock = lock_test(); + + let _t = new_glean(None, false); + + assert!(core_metrics::internal_metrics::os_version + .test_get_value(None) + .is_some()); + + set_upload_enabled(false); + assert!(core_metrics::internal_metrics::os_version + .test_get_value(None) + .is_none()); + + set_upload_enabled(true); + assert!(core_metrics::internal_metrics::os_version + .test_get_value(None) + .is_some()); +} + +#[test] +#[ignore] // TODO: To be done in bug 1686736. +fn overflowing_the_task_queue_records_telemetry() { + todo!() +} + +#[test] +fn sending_deletion_ping_if_disabled_outside_of_run() { + let _lock = lock_test(); + + let (s, r) = crossbeam_channel::bounded::<String>(1); + + // Define a fake uploader that reports back the submission URL + // using a crossbeam channel. + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<String>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + url: String, + _body: Vec<u8>, + _headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(url).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname.clone(), + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }; + + let _t = new_glean(Some(cfg), true); + + crate::block_on_dispatcher(); + + // Now reset Glean and disable upload: it should still send a deletion request + // ping even though we're just starting. + test_reset_glean( + Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: false, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }, + ClientInfoMetrics::unknown(), + false, + ); + + // Wait for the ping to arrive. + let url = r.recv().unwrap(); + assert_eq!(url.contains("deletion-request"), true); +} + +#[test] +fn no_sending_of_deletion_ping_if_unchanged_outside_of_run() { + let _lock = lock_test(); + + let (s, r) = crossbeam_channel::bounded::<String>(1); + + // Define a fake uploader that reports back the submission URL + // using a crossbeam channel. + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<String>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + url: String, + _body: Vec<u8>, + _headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(url).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname.clone(), + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }; + + let _t = new_glean(Some(cfg), true); + + crate::block_on_dispatcher(); + + // Now reset Glean and keep upload enabled: no deletion-request + // should be sent. + test_reset_glean( + Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }, + ClientInfoMetrics::unknown(), + false, + ); + + crate::block_on_dispatcher(); + + assert_eq!(0, r.len()); +} + +#[test] +#[ignore] // TODO: To be done in bug 1672956. +fn test_sending_of_startup_baseline_ping_with_application_lifetime_metric() { + todo!() +} + +#[test] +#[ignore] // TODO: To be done in bug 1672956. +fn test_dirty_flag_is_reset_to_false() { + todo!() +} + +#[test] +fn setting_debug_view_tag_before_initialization_should_not_crash() { + let _lock = lock_test(); + + destroy_glean(true); + assert!(!was_initialize_called()); + + // Define a fake uploader that reports back the submission headers + // using a crossbeam channel. + let (s, r) = crossbeam_channel::bounded::<Vec<(String, String)>>(1); + + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<Vec<(String, String)>>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + _url: String, + _body: Vec<u8>, + headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(headers).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Attempt to set a debug view tag before Glean is initialized. + set_debug_view_tag("valid-tag"); + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }; + + let _t = new_glean(Some(cfg), true); + crate::block_on_dispatcher(); + + // Submit a baseline ping. + submit_ping_by_name("baseline", Some("background")); + + // Wait for the ping to arrive. + let headers = r.recv().unwrap(); + assert_eq!( + "valid-tag", + headers.iter().find(|&kv| kv.0 == "X-Debug-ID").unwrap().1 + ); +} + +#[test] +fn setting_source_tags_before_initialization_should_not_crash() { + let _lock = lock_test(); + + destroy_glean(true); + assert!(!was_initialize_called()); + + // Define a fake uploader that reports back the submission headers + // using a crossbeam channel. + let (s, r) = crossbeam_channel::bounded::<Vec<(String, String)>>(1); + + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<Vec<(String, String)>>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + _url: String, + _body: Vec<u8>, + headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(headers).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Attempt to set source tags before Glean is initialized. + set_source_tags(vec!["valid-tag1".to_string(), "valid-tag2".to_string()]); + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }; + + let _t = new_glean(Some(cfg), true); + crate::block_on_dispatcher(); + + // Submit a baseline ping. + submit_ping_by_name("baseline", Some("background")); + + // Wait for the ping to arrive. + let headers = r.recv().unwrap(); + assert_eq!( + "valid-tag1,valid-tag2", + headers + .iter() + .find(|&kv| kv.0 == "X-Source-Tags") + .unwrap() + .1 + ); +} + +#[test] +fn flipping_upload_enabled_respects_order_of_events() { + // NOTES(janerik): + // I'm reasonably sure this test is excercising the right code paths + // and from the log output it does the right thing: + // + // * It fully initializes with the assumption uploadEnabled=true + // * It then disables upload + // * Then it submits the custom ping, which rightfully is ignored because uploadEnabled=false. + // + // The test passes. + let _lock = lock_test(); + + let (s, r) = crossbeam_channel::bounded::<String>(1); + + // Define a fake uploader that reports back the submission URL + // using a crossbeam channel. + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<String>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + url: String, + _body: Vec<u8>, + _headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(url).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }; + + // We create a ping and a metric before we initialize Glean + let sample_ping = PingType::new("sample-ping-1", true, false, vec![]); + let metric = private::StringMetric::new(CommonMetricData { + name: "string_metric".into(), + category: "telemetry".into(), + send_in_pings: vec!["sample-ping-1".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }); + + let _t = new_glean(Some(cfg), true); + + // Glean might still be initializing. Disable upload. + set_upload_enabled(false); + + // Set data and try to submit a custom ping. + metric.set("some-test-value"); + sample_ping.submit(None); + + // Wait for the ping to arrive. + let url = r.recv().unwrap(); + assert_eq!(url.contains("deletion-request"), true); +} + +#[test] +fn registering_pings_before_init_must_work() { + let _lock = lock_test(); + + destroy_glean(true); + assert!(!was_initialize_called()); + + // Define a fake uploader that reports back the submission headers + // using a crossbeam channel. + let (s, r) = crossbeam_channel::bounded::<String>(1); + + #[derive(Debug)] + pub struct FakeUploader { + sender: crossbeam_channel::Sender<String>, + } + impl net::PingUploader for FakeUploader { + fn upload( + &self, + url: String, + _body: Vec<u8>, + _headers: Vec<(String, String)>, + ) -> net::UploadResult { + self.sender.send(url).unwrap(); + net::UploadResult::HttpStatus(200) + } + } + + // Create a custom ping and attempt its registration. + let sample_ping = PingType::new("pre-register", true, true, vec![]); + + // Create a custom configuration to use a fake uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: GLOBAL_APPLICATION_ID.into(), + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: Some(Box::new(FakeUploader { sender: s })), + }; + + let _t = new_glean(Some(cfg), true); + crate::block_on_dispatcher(); + + // Submit a baseline ping. + sample_ping.submit(None); + + // Wait for the ping to arrive. + let url = r.recv().unwrap(); + assert!(url.contains("pre-register")); +} |