summaryrefslogtreecommitdiffstats
path: root/third_party/rust/glean/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/glean/tests
parentInitial commit. (diff)
downloadfirefox-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.rs50
-rw-r--r--third_party/rust/glean/tests/init_fails.rs84
-rw-r--r--third_party/rust/glean/tests/never_init.rs66
-rw-r--r--third_party/rust/glean/tests/no_time_to_init.rs81
-rw-r--r--third_party/rust/glean/tests/schema.rs121
-rw-r--r--third_party/rust/glean/tests/simple.rs84
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();
+}