diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/glean/tests | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/glean/tests')
-rw-r--r-- | third_party/rust/glean/tests/common/mod.rs | 50 | ||||
-rw-r--r-- | third_party/rust/glean/tests/init_fails.rs | 84 | ||||
-rw-r--r-- | third_party/rust/glean/tests/never_init.rs | 66 | ||||
-rw-r--r-- | third_party/rust/glean/tests/no_time_to_init.rs | 81 | ||||
-rw-r--r-- | third_party/rust/glean/tests/schema.rs | 121 | ||||
-rw-r--r-- | third_party/rust/glean/tests/simple.rs | 84 |
6 files changed, 486 insertions, 0 deletions
diff --git a/third_party/rust/glean/tests/common/mod.rs b/third_party/rust/glean/tests/common/mod.rs new file mode 100644 index 0000000000..6e6f15eb80 --- /dev/null +++ b/third_party/rust/glean/tests/common/mod.rs @@ -0,0 +1,50 @@ +// 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/. + +// #[allow(dead_code)] is required on this module as a workaround for +// https://github.com/rust-lang/rust/issues/46379 +#![allow(dead_code)] + +use std::{panic, process}; + +use glean::{ClientInfoMetrics, Configuration}; + +/// Initialize the env logger for a test environment. +/// +/// When testing we want all logs to go to stdout/stderr by default. +pub fn enable_test_logging() { + let _ = env_logger::builder().is_test(true).try_init(); +} + +/// Install a panic handler that exits the whole process when a panic occurs. +/// +/// This causes the process to exit even if a thread panics. +/// This is similar to the `panic=abort` configuration, but works in the default configuration +/// (as used by `cargo test`). +fn install_panic_handler() { + let orig_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + // invoke the default handler and exit the process + orig_hook(panic_info); + process::exit(1); + })); +} + +/// Create a new instance of Glean. +pub fn initialize(cfg: Configuration) { + // Ensure panics in threads, such as the init thread or the dispatcher, cause the process to + // exit. + // + // Otherwise in case of a panic in a thread the integration test will just hang. + // CI will terminate it after a timeout, but why stick around if we know nothing is happening? + install_panic_handler(); + + // Use some default values to make our life easier a bit. + let client_info = ClientInfoMetrics { + app_build: "1.0.0".to_string(), + app_display_version: "1.0.0".to_string(), + }; + + glean::initialize(cfg, client_info); +} diff --git a/third_party/rust/glean/tests/init_fails.rs b/third_party/rust/glean/tests/init_fails.rs new file mode 100644 index 0000000000..ccd4c2e3a8 --- /dev/null +++ b/third_party/rust/glean/tests/init_fails.rs @@ -0,0 +1,84 @@ +// 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/. + +//! This integration test should model how the RLB is used when embedded in another Rust application +//! (e.g. FOG/Firefox Desktop). +//! +//! We write a single test scenario per file to avoid any state keeping across runs +//! (different files run as different processes). + +mod common; + +use std::{thread, time::Duration}; + +use glean::Configuration; + +/// Some user metrics. +mod metrics { + use glean::private::*; + use glean::{Lifetime, TimeUnit}; + use glean_core::CommonMetricData; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| { + TimespanMetric::new( + CommonMetricData { + name: "initialization".into(), + category: "sample".into(), + send_in_pings: vec!["validation".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }, + TimeUnit::Nanosecond, + ) + }); +} + +mod pings { + use glean::private::PingType; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static validation: Lazy<PingType> = + Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![])); +} + +/// Test scenario: Glean initialization fails. +/// +/// App tries to initialize Glean, but that somehow fails. +#[test] +fn init_fails() { + common::enable_test_logging(); + + metrics::initialization.start(); + + // Create a custom configuration to use a validating uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: "".into(), // An empty application ID is invalid. + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }; + common::initialize(cfg); + + metrics::initialization.stop(); + + pings::validation.submit(None); + + // We don't test for data here, as that would block on the dispatcher. + + // Give it a short amount of time to actually finish initialization. + thread::sleep(Duration::from_millis(500)); + + glean::shutdown(); +} diff --git a/third_party/rust/glean/tests/never_init.rs b/third_party/rust/glean/tests/never_init.rs new file mode 100644 index 0000000000..321662b327 --- /dev/null +++ b/third_party/rust/glean/tests/never_init.rs @@ -0,0 +1,66 @@ +// 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/. + +//! This integration test should model how the RLB is used when embedded in another Rust application +//! (e.g. FOG/Firefox Desktop). +//! +//! We write a single test scenario per file to avoid any state keeping across runs +//! (different files run as different processes). + +mod common; + +/// Some user metrics. +mod metrics { + use glean::private::*; + use glean::{Lifetime, TimeUnit}; + use glean_core::CommonMetricData; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| { + TimespanMetric::new( + CommonMetricData { + name: "initialization".into(), + category: "sample".into(), + send_in_pings: vec!["validation".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }, + TimeUnit::Nanosecond, + ) + }); +} + +mod pings { + use glean::private::PingType; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static validation: Lazy<PingType> = + Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![])); +} + +/// Test scenario: Glean is never initialized. +/// +/// Glean is never initialized. +/// Some data is recorded early on. +/// And later the whole process is shutdown. +#[test] +fn never_initialize() { + common::enable_test_logging(); + + metrics::initialization.start(); + + // NOT calling `initialize` here. + // In apps this might happen for several reasons: + // 1. Process doesn't run long enough for Glean to be initialized. + // 2. Getting some early data used for initialize fails + + pings::validation.submit(None); + + // We can't test for data either, as that would panic because init was never called. + + glean::shutdown(); +} diff --git a/third_party/rust/glean/tests/no_time_to_init.rs b/third_party/rust/glean/tests/no_time_to_init.rs new file mode 100644 index 0000000000..3edebca3d7 --- /dev/null +++ b/third_party/rust/glean/tests/no_time_to_init.rs @@ -0,0 +1,81 @@ +// 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/. + +//! This integration test should model how the RLB is used when embedded in another Rust application +//! (e.g. FOG/Firefox Desktop). +//! +//! We write a single test scenario per file to avoid any state keeping across runs +//! (different files run as different processes). + +mod common; + +use glean::Configuration; + +/// Some user metrics. +mod metrics { + use glean::private::*; + use glean::{Lifetime, TimeUnit}; + use glean_core::CommonMetricData; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| { + TimespanMetric::new( + CommonMetricData { + name: "initialization".into(), + category: "sample".into(), + send_in_pings: vec!["validation".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }, + TimeUnit::Nanosecond, + ) + }); +} + +mod pings { + use glean::private::PingType; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static validation: Lazy<PingType> = + Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![])); +} + +/// Test scenario: Glean initialization fails. +/// +/// The app tries to initializate Glean, but that somehow fails. +#[test] +fn init_fails() { + common::enable_test_logging(); + + metrics::initialization.start(); + + // Create a custom configuration to use a validating uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: "firefox-desktop".into(), // An empty application ID is invalid. + upload_enabled: true, + max_events: None, + delay_ping_lifetime_io: false, + channel: Some("testing".into()), + server_endpoint: Some("invalid-test-host".into()), + uploader: None, + }; + common::initialize(cfg); + + metrics::initialization.stop(); + + pings::validation.submit(None); + + // We don't test for data here, as that would block on the dispatcher. + + // Shut it down immediately; this might not be enough time to initialize. + + glean::shutdown(); +} diff --git a/third_party/rust/glean/tests/schema.rs b/third_party/rust/glean/tests/schema.rs new file mode 100644 index 0000000000..54acc157f0 --- /dev/null +++ b/third_party/rust/glean/tests/schema.rs @@ -0,0 +1,121 @@ +// 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 std::io::Read; + +use flate2::read::GzDecoder; +use jsonschema_valid::{self, schemas::Draft}; +use serde_json::Value; + +use glean::{ClientInfoMetrics, Configuration}; + +const SCHEMA_JSON: &str = include_str!("../../../glean.1.schema.json"); + +fn load_schema() -> Value { + serde_json::from_str(SCHEMA_JSON).unwrap() +} + +const GLOBAL_APPLICATION_ID: &str = "org.mozilla.glean.test.app"; + +// Create a new instance of Glean with a temporary directory. +// We need to keep the `TempDir` alive, so that it's not deleted before we stop using it. +fn new_glean(configuration: Option<Configuration>) -> tempfile::TempDir { + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = match configuration { + Some(c) => c, + None => 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, + }, + }; + + let client_info = ClientInfoMetrics { + app_build: env!("CARGO_PKG_VERSION").to_string(), + app_display_version: env!("CARGO_PKG_VERSION").to_string(), + }; + + glean::initialize(cfg, client_info); + + dir +} + +#[test] +fn validate_against_schema() { + let schema = load_schema(); + + let (s, r) = crossbeam_channel::bounded::<Vec<u8>>(1); + + // Define a fake uploader that reports back the submitted payload + // using a crossbeam channel. + #[derive(Debug)] + pub struct ValidatingUploader { + sender: crossbeam_channel::Sender<Vec<u8>>, + } + impl glean::net::PingUploader for ValidatingUploader { + fn upload( + &self, + _url: String, + body: Vec<u8>, + _headers: Vec<(String, String)>, + ) -> glean::net::UploadResult { + self.sender.send(body).unwrap(); + glean::net::UploadResult::HttpStatus(200) + } + } + + // Create a custom configuration to use a validating 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(ValidatingUploader { sender: s })), + }; + let _ = new_glean(Some(cfg)); + + // Define a new ping and submit it. + const PING_NAME: &str = "test-ping"; + let custom_ping = glean::private::PingType::new(PING_NAME, true, true, vec![]); + custom_ping.submit(None); + + // Wait for the ping to arrive. + let raw_body = r.recv().unwrap(); + + // Decode the gzipped body. + let mut gzip_decoder = GzDecoder::new(&raw_body[..]); + let mut s = String::with_capacity(raw_body.len()); + + let data = gzip_decoder + .read_to_string(&mut s) + .ok() + .map(|_| &s[..]) + .or_else(|| std::str::from_utf8(&raw_body).ok()) + .and_then(|payload| serde_json::from_str(payload).ok()) + .unwrap(); + + // Now validate against the vendored schema + let cfg = jsonschema_valid::Config::from_schema(&schema, Some(Draft::Draft6)).unwrap(); + let validation = cfg.validate(&data); + match validation { + Ok(()) => {} + Err(e) => { + let errors = e.map(|e| format!("{}", e)).collect::<Vec<_>>(); + panic!("Data: {:#?}\nErrors: {:#?}", data, errors); + } + } +} diff --git a/third_party/rust/glean/tests/simple.rs b/third_party/rust/glean/tests/simple.rs new file mode 100644 index 0000000000..7af54eddaa --- /dev/null +++ b/third_party/rust/glean/tests/simple.rs @@ -0,0 +1,84 @@ +// 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/. + +//! This integration test should model how the RLB is used when embedded in another Rust application +//! (e.g. FOG/Firefox Desktop). +//! +//! We write a single test scenario per file to avoid any state keeping across runs +//! (different files run as different processes). + +mod common; + +use glean::Configuration; + +/// Some user metrics. +mod metrics { + use glean::private::*; + use glean::{Lifetime, TimeUnit}; + use glean_core::CommonMetricData; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static initialization: Lazy<TimespanMetric> = Lazy::new(|| { + TimespanMetric::new( + CommonMetricData { + name: "initialization".into(), + category: "sample".into(), + send_in_pings: vec!["validation".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }, + TimeUnit::Nanosecond, + ) + }); +} + +mod pings { + use glean::private::PingType; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + pub static validation: Lazy<PingType> = + Lazy::new(|| glean::private::PingType::new("validation", true, true, vec![])); +} + +/// Test scenario: A clean run +/// +/// The app is initialized, in turn Glean gets initialized without problems. +/// Some data is recorded (before and after initialization). +/// And later the whole process is shutdown. +#[test] +fn simple_lifecycle() { + common::enable_test_logging(); + + metrics::initialization.start(); + + // Create a custom configuration to use a validating uploader. + let dir = tempfile::tempdir().unwrap(); + let tmpname = dir.path().display().to_string(); + + let cfg = Configuration { + data_path: tmpname, + application_id: "firefox-desktop".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, + }; + common::initialize(cfg); + + metrics::initialization.stop(); + + // This would never be called outside of tests, + // but it's the only way we can really test it's working right now. + assert!(metrics::initialization.test_get_value(None).is_some()); + + pings::validation.submit(None); + assert!(metrics::initialization.test_get_value(None).is_none()); + + glean::shutdown(); +} |