use std::io::Read;
use std::sync::Arc;
use std::time::{Duration, Instant};
use crossbeam_channel::RecvTimeoutError;
use flate2::read::GzDecoder;
use glean_core::{glean_test_get_experimentation_id, DynamicLabelType, LabeledCounter};
use serde_json::Value as JsonValue;
use crate::private::PingType;
use crate::private::{
BooleanMetric, CounterMetric, DualLabeledCounterMetric, EventMetric, StringMetric, TextMetric,
};
use super::*;
use crate::common_test::{lock_test, new_glean, GLOBAL_APPLICATION_ID};
fn new_test_ping(name: &str) -> PingType {
PingType::new(
name,
true,
true,
true,
true,
true,
vec![],
vec![],
true,
vec![],
)
}
#[test]
fn send_a_ping() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<net::PingUploadRequest>(1);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<net::PingUploadRequest>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
const PING_NAME: &str = "test-ping";
let custom_ping = new_test_ping(PING_NAME);
custom_ping.submit(None);
let upload_request = r.recv().unwrap();
assert!(upload_request.body_has_info_sections);
assert_eq!(upload_request.ping_name, PING_NAME);
assert!(upload_request.url.contains(PING_NAME));
}
#[test]
fn send_a_ping_without_info_sections() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<net::PingUploadRequest>(1);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<net::PingUploadRequest>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
const PING_NAME: &str = "noinfo-ping";
let custom_ping = PingType::new(
PING_NAME,
true,
true,
true,
false,
true,
vec![],
vec![],
true,
vec![],
);
custom_ping.submit(None);
let upload_request = r.recv().unwrap();
assert!(!upload_request.body_has_info_sections);
assert_eq!(upload_request.ping_name, PING_NAME);
}
#[test]
fn disabling_upload_disables_metrics_recording() {
let _lock = lock_test();
let _t = new_glean(None, true);
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(Some("store1".into())).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()).unwrap();
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();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
destroy_glean(true, &tmpname);
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());
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
false,
);
assert!(test_is_experiment_active(
"experiment_set_preinit".to_string()
));
assert!(!test_is_experiment_active(
"experiment_preinit_disabled".to_string()
));
}
#[test]
fn test_experimentation_id_recording() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
destroy_glean(true, &tmpname);
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_experimentation_id("alpha-beta-gamma-delta".to_string())
.build(),
ClientInfoMetrics::unknown(),
false,
);
let exp_id = glean_test_get_experimentation_id().expect("Experimentation id must not be None");
assert_eq!(
"alpha-beta-gamma-delta".to_string(),
exp_id,
"Experimentation id must match"
);
}
#[test]
fn sending_of_foreground_background_pings() {
let _lock = lock_test();
let click: EventMetric<traits::NoExtraKeys> = private::EventMetric::new(CommonMetricData {
name: "click".into(),
category: "ui".into(),
send_in_pings: vec!["events".into()],
lifetime: Lifetime::Ping,
disabled: false,
..Default::default()
});
let (s, r) = crossbeam_channel::bounded::<String>(3);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
handle_client_active();
let url = r.recv().unwrap();
assert!(url.contains("baseline"));
click.record(None);
handle_client_inactive();
let mut expected_pings = vec!["baseline", "events"];
for _ in 0..2 {
let url = r.recv().unwrap();
expected_pings.retain(|&name| !url.contains(name));
}
assert_eq!(0, expected_pings.len());
handle_client_active();
let url = r.recv().unwrap();
assert!(url.contains("baseline"));
}
#[test]
fn sending_of_startup_baseline_ping() {
let _lock = lock_test();
let data_dir = new_glean(None, true);
glean_core::glean_set_dirty_flag(true);
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let tmpname = data_dir.path().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build(),
ClientInfoMetrics::unknown(),
false,
);
let url = r.recv().unwrap();
assert!(url.contains("baseline"));
}
#[test]
fn no_dirty_baseline_on_clean_shutdowns() {
let _lock = lock_test();
glean_core::glean_set_test_mode(true);
let data_dir = new_glean(None, true);
glean_core::glean_set_dirty_flag(true);
crate::shutdown();
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let tmpname = data_dir.path().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build(),
ClientInfoMetrics::unknown(),
false,
);
assert_eq!(r.try_recv(), Err(crossbeam_channel::TryRecvError::Empty));
}
#[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();
let file_path = tmpdirname.to_path_buf().join("notadir");
std::fs::write(file_path.clone(), "test").expect("The test Glean dir file must be created");
let cfg = ConfigurationBuilder::new(true, file_path, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build();
test_reset_glean(cfg, ClientInfoMetrics::unknown(), false);
}
#[test]
fn queued_recorded_metrics_correctly_record_during_init() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
destroy_glean(true, &tmpname);
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,
});
for _ in 0..3 {
metric.add(1);
}
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build();
let _t = new_glean(Some(cfg), false);
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().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
true,
);
crate::initialize(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
);
}
#[test]
fn dont_handle_events_when_uninitialized() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
true,
);
let click: EventMetric<traits::NoExtraKeys> = private::EventMetric::new(CommonMetricData {
name: "click".into(),
category: "ui".into(),
send_in_pings: vec!["events".into()],
lifetime: Lifetime::Ping,
disabled: false,
..Default::default()
});
click.record(None);
assert_ne!(None, click.test_get_value(None));
destroy_glean(false, &tmpname);
assert!(!glean_core::glean_submit_ping_by_name_sync(
"events".to_string(),
None
));
}
#[test]
fn the_app_channel_must_be_correctly_set_if_requested() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let app_channel = StringMetric::new(CommonMetricData {
name: "app_channel".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
..Default::default()
});
let client_info = ClientInfoMetrics {
channel: None,
..ClientInfoMetrics::unknown()
};
test_reset_glean(
ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
client_info,
true,
);
assert!(app_channel.test_get_value(None).is_none());
let client_info = ClientInfoMetrics {
channel: Some("testing".into()),
..ClientInfoMetrics::unknown()
};
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
client_info,
true,
);
assert_eq!("testing", app_channel.test_get_value(None).unwrap());
}
#[test]
fn ping_collection_must_happen_after_concurrently_scheduled_metrics_recordings() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded(1);
glean_core::glean_set_test_mode(true);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<(String, JsonValue)>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let net::PingUploadRequest { body, url, .. } =
upload_request.capable(|_| true).unwrap();
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut s = String::with_capacity(body.len());
let data = gzip_decoder
.read_to_string(&mut s)
.ok()
.map(|_| &s[..])
.or_else(|| std::str::from_utf8(&body).ok())
.and_then(|payload| serde_json::from_str(payload).ok())
.unwrap();
self.sender.send((url, data)).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build(),
ClientInfoMetrics::unknown(),
true,
);
let ping_name = "custom_ping_1";
let ping = new_test_ping(ping_name);
let metric = private::StringMetric::new(CommonMetricData {
name: "string_metric".into(),
category: "telemetry".into(),
send_in_pings: vec![ping_name.into()],
lifetime: Lifetime::Ping,
disabled: false,
..Default::default()
});
let test_value = "SomeTestValue";
metric.set(test_value.to_string());
ping.submit(None);
let (url, body) = r.recv().unwrap();
assert!(url.contains(ping_name));
assert_eq!(
test_value,
body["metrics"]["string"]["telemetry.string_metric"]
);
}
#[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!["store1".into()],
lifetime: Lifetime::Ping,
disabled: false,
..Default::default()
});
assert!(metric.test_get_value(None).is_none());
metric.set("TEST VALUE".into());
assert!(metric.test_get_value(None).is_some());
set_upload_enabled(false);
assert!(metric.test_get_value(None).is_none());
metric.set("TEST VALUE".into());
assert!(metric.test_get_value(None).is_none());
set_upload_enabled(true);
assert!(metric.test_get_value(None).is_none());
metric.set("TEST VALUE".into());
assert_eq!("TEST VALUE", metric.test_get_value(None).unwrap());
}
#[test]
fn core_metrics_are_not_cleared_when_disabling_and_enabling_uploading() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
true,
);
let os_version = StringMetric::new(CommonMetricData {
name: "os_version".into(),
category: "".into(),
send_in_pings: vec!["glean_client_info".into()],
lifetime: Lifetime::Application,
disabled: false,
..Default::default()
});
assert!(os_version.test_get_value(None).is_some());
let initial_value = os_version.test_get_value(None).unwrap();
set_upload_enabled(false);
assert_eq!(initial_value, os_version.test_get_value(None).unwrap());
set_upload_enabled(true);
assert_eq!(initial_value, os_version.test_get_value(None).unwrap());
}
#[test]
fn sending_deletion_ping_if_disabled_outside_of_run() {
let _lock = lock_test();
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build();
let _t = new_glean(Some(cfg), true);
test_reset_glean(
ConfigurationBuilder::new(false, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build(),
ClientInfoMetrics::unknown(),
false,
);
let url = r.recv().unwrap();
assert!(url.contains("deletion-request"));
}
#[test]
fn no_sending_of_deletion_ping_if_unchanged_outside_of_run() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<String>(1);
glean_core::glean_set_test_mode(true);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build();
let _t = new_glean(Some(cfg), true);
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build(),
ClientInfoMetrics::unknown(),
false,
);
assert_eq!(0, r.len());
}
#[test]
fn deletion_request_ping_contains_experimentation_id() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<JsonValue>(1);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<JsonValue>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
let body = upload_request.body;
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut body_str = String::with_capacity(body.len());
let data: JsonValue = gzip_decoder
.read_to_string(&mut body_str)
.ok()
.map(|_| &body_str[..])
.or_else(|| std::str::from_utf8(&body).ok())
.and_then(|payload| serde_json::from_str(payload).ok())
.unwrap();
self.sender.send(data).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_experimentation_id("alpha-beta-gamma-delta".to_string())
.build();
let _t = new_glean(Some(cfg), true);
test_reset_glean(
ConfigurationBuilder::new(false, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.with_experimentation_id("alpha-beta-gamma-delta".to_string())
.build(),
ClientInfoMetrics::unknown(),
false,
);
let url = r.recv().unwrap();
let metrics = url.get("metrics").unwrap();
let strings = metrics.get("string").unwrap();
assert_eq!(
"alpha-beta-gamma-delta",
strings
.get("glean.client.annotation.experimentation_id")
.unwrap()
);
}
#[test]
fn test_sending_of_startup_baseline_ping_with_application_lifetime_metric() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded(1);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<(String, JsonValue)>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let net::PingUploadRequest { url, body, .. } =
upload_request.capable(|_| true).unwrap();
let mut gzip_decoder = GzDecoder::new(&body[..]);
let mut s = String::with_capacity(body.len());
let data = gzip_decoder
.read_to_string(&mut s)
.ok()
.map(|_| &s[..])
.or_else(|| std::str::from_utf8(&body).ok())
.and_then(|payload| serde_json::from_str(payload).ok())
.unwrap();
self.sender.send((url, data)).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
test_reset_glean(
ConfigurationBuilder::new(true, tmpname.clone(), GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
true,
);
glean_core::glean_set_dirty_flag(true);
let metric = private::StringMetric::new(CommonMetricData {
name: "app_lifetime".into(),
category: "telemetry".into(),
send_in_pings: vec!["baseline".into()],
lifetime: Lifetime::Application,
disabled: false,
..Default::default()
});
let test_value = "HELLOOOOO!";
metric.set(test_value.into());
assert_eq!(test_value, metric.test_get_value(None).unwrap());
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build(),
ClientInfoMetrics::unknown(),
false,
);
let (url, body) = r.recv().unwrap();
assert!(url.contains("/baseline/"));
assert_eq!("dirty_startup", body["ping_info"]["reason"]);
assert_eq!(
test_value,
body["metrics"]["string"]["telemetry.app_lifetime"]
);
}
#[test]
fn setting_debug_view_tag_before_initialization_should_not_crash() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
destroy_glean(true, &tmpname);
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.headers).unwrap();
net::UploadResult::http_status(200)
}
}
set_debug_view_tag("valid-tag");
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
let _t = new_glean(Some(cfg), true);
submit_ping_by_name("baseline", Some("inactive"));
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();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
destroy_glean(true, &tmpname);
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.headers).unwrap();
net::UploadResult::http_status(200)
}
}
set_source_tags(vec!["valid-tag1".to_string(), "valid-tag2".to_string()]);
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
let _t = new_glean(Some(cfg), true);
submit_ping_by_name("baseline", Some("inactive"));
let headers = r.recv().unwrap();
assert_eq!(
"valid-tag1,valid-tag2",
headers
.iter()
.find(|&kv| kv.0 == "X-Source-Tags")
.unwrap()
.1
);
}
#[test]
fn setting_source_tags_after_initialization_should_not_crash() {
let _lock = lock_test();
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.headers).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
set_source_tags(vec!["valid-tag1".to_string(), "valid-tag2".to_string()]);
submit_ping_by_name("baseline", Some("inactive"));
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() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<String>(1);
glean_core::glean_set_test_mode(true);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
let sample_ping = new_test_ping("sample-ping-1");
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);
set_upload_enabled(false);
metric.set("some-test-value".into());
sample_ping.submit(None);
let url = r.recv().unwrap();
assert!(url.contains("deletion-request"));
}
#[test]
fn registering_pings_before_init_must_work() {
let _lock = lock_test();
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let sample_ping = new_test_ping("pre-register");
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
sample_ping.submit(None);
let url = r.recv().unwrap();
assert!(url.contains("pre-register"));
}
#[test]
fn test_a_ping_before_submission() {
let _lock = lock_test();
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, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
let sample_ping = new_test_ping("custom1");
let metric = CounterMetric::new(CommonMetricData {
name: "counter_metric".into(),
category: "test".into(),
send_in_pings: vec!["custom1".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: None,
});
metric.add(1);
sample_ping.test_before_next_submit(move |reason| {
assert_eq!(None, reason);
assert_eq!(1, metric.test_get_value(None).unwrap());
});
sample_ping.submit(None);
let url = r.recv().unwrap();
assert!(url.contains("custom1"));
}
#[test]
fn test_boolean_get_num_errors() {
let _lock = lock_test();
let _t = new_glean(None, false);
let metric = BooleanMetric::new(CommonMetricData {
name: "counter_metric".into(),
category: "test".into(),
send_in_pings: vec!["custom1".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: Some(DynamicLabelType::Label(str::to_string("asdf"))),
});
let result = metric.test_get_num_recorded_errors(ErrorType::InvalidLabel);
assert_eq!(result, 0);
}
#[test]
fn test_labeled_counter_metric() {
let _lock = lock_test();
let _t = new_glean(None, false);
let metric = LabeledCounter::new(
LabeledMetricData::Common {
cmd: CommonMetricData {
name: "labeled_counter".into(),
category: "telemetry".into(),
send_in_pings: vec!["store1".into()],
disabled: false,
lifetime: Lifetime::Ping,
..Default::default()
},
},
Some(vec!["key1".into()]),
);
metric.get("key1").add(1);
metric.get("key2").add(2);
let value = metric.test_get_value(Some("store1".into())).unwrap();
assert_eq!(value["key1"], 1);
assert_eq!(value["key2"], 2);
let result = metric.test_get_num_recorded_errors(ErrorType::InvalidLabel);
assert_eq!(result, 0);
}
#[test]
fn test_dual_labeled_counter_metric() {
let _lock = lock_test();
let _t = new_glean(None, false);
let metric = DualLabeledCounterMetric::new(
CommonMetricData {
name: "dual_labeled_counter".into(),
category: "telemetry".into(),
send_in_pings: vec!["store1".into()],
disabled: false,
lifetime: Lifetime::Ping,
..Default::default()
},
Some(vec!["key1".into()]),
Some(vec!["category1".into()]),
);
metric.get("key1", "category1").add(1);
let value = metric
.get("key1", "category1")
.test_get_value(Some("store1".into()));
assert_eq!(value.unwrap(), 1);
let result = metric.test_get_num_recorded_errors(ErrorType::InvalidLabel);
assert_eq!(result, 0);
}
#[test]
fn test_text_can_hold_long_string() {
let _lock = lock_test();
let _t = new_glean(None, false);
let metric = TextMetric::new(CommonMetricData {
name: "text_metric".into(),
category: "test".into(),
send_in_pings: vec!["custom1".into()],
lifetime: Lifetime::Application,
disabled: false,
dynamic_label: Some(DynamicLabelType::Label(str::to_string("text"))),
});
metric.set("I've seen things you people wouldn't believe. Attack ships on fire off the shoulder of Orion. I watched C-beams glitter in the dark near the Tannhäuser Gate. All those moments will be lost in time, like tears in rain".into());
let result = metric.test_get_num_recorded_errors(ErrorType::InvalidValue);
assert_eq!(result, 0);
let result = metric.test_get_num_recorded_errors(ErrorType::InvalidOverflow);
assert_eq!(result, 0);
}
#[test]
fn configure_ping_throttling() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<String>(1);
glean_core::glean_set_test_mode(true);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<String>,
done: Arc<std::sync::atomic::AtomicBool>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
if self.done.load(std::sync::atomic::Ordering::SeqCst) {
return net::UploadResult::http_status(200);
}
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let done = Arc::new(std::sync::atomic::AtomicBool::new(false));
let mut cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader {
sender: s,
done: Arc::clone(&done),
})
.build();
let pings_per_interval = 10;
cfg.rate_limit = Some(crate::PingRateLimit {
seconds_per_interval: 1,
pings_per_interval,
});
let _t = new_glean(Some(cfg), true);
const PING_NAME: &str = "test-ping";
let custom_ping = new_test_ping(PING_NAME);
for _ in 0..pings_per_interval {
custom_ping.submit(None);
let url = r.recv().unwrap();
assert!(url.contains(PING_NAME));
}
custom_ping.submit(None);
let now = Instant::now();
assert_eq!(
r.recv_deadline(now + Duration::from_millis(250)),
Err(RecvTimeoutError::Timeout)
);
done.store(true, std::sync::atomic::Ordering::SeqCst);
}
#[test]
fn pings_ride_along_builtin_pings() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<String>(3);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let ping_schedule = HashMap::from([("baseline".to_string(), vec!["ride-along".to_string()])]);
let cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.with_ping_schedule(ping_schedule)
.build();
glean_core::glean_set_test_mode(true);
let _t = new_glean(Some(cfg), true);
let _ride_along_ping = new_test_ping("ride-along");
handle_client_active();
let url = r.recv().unwrap();
assert!(url.contains("baseline"));
let url = r.recv().unwrap();
assert!(url.contains("ride-along"));
}
#[test]
fn pings_get_submitted_on_disabled_ping_schedule() {
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<String>(3);
#[derive(Debug)]
pub struct FakeUploader {
sender: crossbeam_channel::Sender<String>,
}
impl net::PingUploader for FakeUploader {
fn upload(&self, upload_request: net::CapablePingUploadRequest) -> net::UploadResult {
let upload_request = upload_request.capable(|_| true).unwrap();
self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
let ping_schedule = HashMap::from([("baseline".to_string(), vec!["ride-along".to_string()])]);
let cfg = ConfigurationBuilder::new(false, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.with_uploader(FakeUploader { sender: s })
.with_ping_schedule(ping_schedule)
.build();
let _t = new_glean(Some(cfg), true);
let ride_along_ping = PingType::new(
"ride-along",
true,
true,
true,
true,
true,
vec![],
vec![],
false,
vec![],
);
ride_along_ping.set_enabled(true);
handle_client_active();
let url = r.recv_timeout(Duration::from_millis(100)).unwrap();
assert!(url.contains("ride-along"));
}
#[test]
fn test_attribution_and_distribution_updates_before_glean_inits() {
let _lock = lock_test();
let dir = tempfile::tempdir().unwrap();
let tmpname = dir.path().to_path_buf();
destroy_glean(true, &tmpname);
let mut attribution = AttributionMetrics {
source: Some("source".into()),
medium: Some("medium".into()),
campaign: Some("campaign".into()),
term: Some("term".into()),
content: Some("content".into()),
};
let distribution = DistributionMetrics {
name: Some("name".into()),
};
update_attribution(attribution.clone());
update_distribution(distribution);
let attribution_update = AttributionMetrics {
term: Some("new term".into()),
..Default::default()
};
let distribution_update = DistributionMetrics {
name: Some("different name".into()),
};
update_attribution(attribution_update);
update_distribution(distribution_update.clone());
test_reset_glean(
ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(),
ClientInfoMetrics::unknown(),
false,
);
attribution.term = Some("new term".into());
assert_eq!(attribution, test_get_attribution());
assert_eq!(distribution_update, test_get_distribution());
}