// 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 std::sync::{Arc, Barrier, Mutex}; use std::thread::{self, ThreadId}; use std::time::{Duration, Instant};
use crossbeam_channel::RecvTimeoutError; use flate2::read::GzDecoder; use glean_core::glean_test_get_experimentation_id; use serde_json::Value as JsonValue;
#[test] fn send_a_ping() { let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded::<net::PingUploadRequest>(1);
// Define a fake uploader that reports back the ping upload request. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<net::PingUploadRequest>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. 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 _t = new_glean(Some(cfg), true);
// Define a new ping and submit it. const PING_NAME: &str = "test-ping"; let custom_ping = new_test_ping(PING_NAME);
custom_ping.submit(None);
// Wait for the ping to arrive. 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);
// Define a fake uploader that reports back the ping upload request. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<net::PingUploadRequest>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. 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 _t = new_glean(Some(cfg), true);
// Define a new ping and submit it. const PING_NAME: &str = "noinfo-ping"; let custom_ping = PingType::new(
PING_NAME, true, true, true, /* include_info_sections */ false, true,
vec![],
vec![], true,
);
custom_ping.submit(None);
// Wait for the ping to arrive. 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();
#[test] fn test_experiments_recording_before_glean_inits() { let _lock = lock_test(); let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
#[test] fn test_experimentation_id_recording() { let _lock = lock_test(); let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
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();
// Wait for the pings to arrive. letmut expected_pings = vec!["baseline", "events"]; for _ in0..2 { let url = r.recv().unwrap(); // If the url contains the expected reason, remove it from the list.
expected_pings.retain(|&name| !url.contains(name));
} // We received all the expected pings.
assert_eq!(0, expected_pings.len());
// Simulate becoming active again.
handle_client_active();
// We expect a baseline ping to be generated here (reason: 'active'). let url = r.recv().unwrap();
assert!(url.contains("baseline"));
}
#[test] fn sending_of_startup_baseline_ping() { let _lock = lock_test();
// Create an instance of Glean and then flip the dirty // bit to true. let data_dir = new_glean(None, true);
glean_core::glean_set_dirty_flag(true);
// Restart glean and wait for a baseline ping to be generated. let (s, r) = crossbeam_channel::bounded::<String>(1);
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<String>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. let tmpname = data_dir.path().to_path_buf();
// Now reset Glean: it should still send a baseline ping with reason // dirty_startup when starting, because of the dirty bit being set.
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,
);
// Wait for the ping to arrive. let url = r.recv().unwrap();
assert!(url.contains("baseline"));
}
#[test] fn no_dirty_baseline_on_clean_shutdowns() { let _lock = lock_test();
// Create an instance of Glean, wait for init and then flip the dirty // bit to true. let data_dir = new_glean(None, true);
glean_core::glean_set_dirty_flag(true);
crate::shutdown();
// Restart glean and wait for a baseline ping to be generated. let (s, r) = crossbeam_channel::bounded::<String>(1);
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<String>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. let tmpname = data_dir.path().to_path_buf();
// Now reset Glean: it should not send a baseline ping, because // we cleared the dirty bit.
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,
);
// We don't expect a startup ping.
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(); // Create a file in the temporary dir and use that as the // name of the Glean data dir. 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();
// We don't need to sleep here. // The `test_reset_glean` already waited on the initialize task.
}
#[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();
// This will queue 3 tasks that will add to the metric value once Glean is initialized for _ in0..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 cfg = ConfigurationBuilder::new(true, tmpname, GLOBAL_APPLICATION_ID)
.with_server_endpoint("invalid-test-host")
.build(); let _t = new_glean(Some(cfg), 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().to_path_buf();
// Glean was initialized and it waited for a full initialization to finish. // We now just want to try to initialize again. // This will bail out early.
// We don't need to sleep here. // The `test_reset_glean` already waited on the initialize task, // and the 2nd initialize will bail out early. // // All we tested is that this didn't crash.
}
#[test] fn dont_handle_events_when_uninitialized() { let _lock = lock_test(); let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
// Ensure there's at least one event recorded, // otherwise the ping is not sent. 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); // Wait for the dispatcher.
assert_ne!(None, click.test_get_value(None));
// Now destroy Glean. We test submission when not initialized.
destroy_glean(false, &tmpname);
// We reach into `glean_core` to test this, // only there we can synchronously submit and get a return value.
assert!(!glean_core::glean_submit_ping_by_name_sync( "events".to_string(),
None
));
}
// TODO: Should probably move into glean-core. #[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();
// Internal metric, replicated here for testing. 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()
});
#[test] fn ping_collection_must_happen_after_concurrently_scheduled_metrics_recordings() { // Given the following block of code: // // Metric.A.set("SomeTestValue") // Glean.submitPings(listOf("custom-ping-1")) // // This test ensures that "custom-ping-1" contains "metric.a" with a value of "SomeTestValue" // when the ping is collected.
let _lock = lock_test();
let (s, r) = crossbeam_channel::bounded(1);
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<(String, JsonValue)>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { let net::PingUploadRequest { body, url, .. } = upload_request; // Decode the gzipped body. letmut gzip_decoder = GzDecoder::new(&body[..]); letmut 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);
// Wait for the ping to arrive. let (url, body) = r.recv().unwrap();
assert!(url.contains(ping_name));
// TODO: Should probably move into glean-core. #[test] fn core_metrics_should_be_cleared_and_restored_when_disabling_and_enabling_uploading() { let _lock = lock_test();
let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
#[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)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<String>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. 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);
// Now reset Glean and disable upload: it should still send a deletion request // ping even though we're just starting.
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,
);
// Wait for the ping to arrive. 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);
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<String>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. 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);
// Now reset Glean and keep upload enabled: no deletion-request // should be sent.
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);
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<JsonValue>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { let body = upload_request.body; letmut gzip_decoder = GzDecoder::new(&body[..]); letmut 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)
}
}
// Create a custom configuration to use a fake uploader. 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);
// Now reset Glean and disable upload: it should still send a deletion request // ping even though we're just starting.
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,
);
// Wait for the ping to arrive and check the experimentation id matches 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);
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<(String, JsonValue)>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { let net::PingUploadRequest { url, body, .. } = upload_request; // Decode the gzipped body. letmut gzip_decoder = GzDecoder::new(&body[..]); letmut 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,
);
// Reaching into the core.
glean_core::glean_set_dirty_flag(true);
// Restart glean and don't clear the stores.
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/"));
// We set the dirty bit above.
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);
// Define a fake uploader that reports back the submission headers // using a crossbeam channel. let (s, r) = crossbeam_channel::bounded::<Vec<(String, String)>>(1);
// 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 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 a baseline ping.
submit_ping_by_name("baseline", Some("inactive"));
// 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(); let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
// Define a fake uploader that reports back the submission headers // using a crossbeam channel. let (s, r) = crossbeam_channel::bounded::<Vec<(String, String)>>(1);
// 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 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 a baseline ping.
submit_ping_by_name("baseline", Some("inactive"));
// 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 setting_source_tags_after_initialization_should_not_crash() { let _lock = lock_test();
// Define a fake uploader that reports back the submission headers // using a crossbeam channel. let (s, r) = crossbeam_channel::bounded::<Vec<(String, String)>>(1);
// Create a custom configuration to use a fake uploader. 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 _t = new_glean(Some(cfg), true);
// Attempt to set source tags after `Glean.initialize` is called, // but before Glean is fully initialized. //assert!(was_initialize_called());
set_source_tags(vec!["valid-tag1".to_string(), "valid-tag2".to_string()]);
// Submit a baseline ping.
submit_ping_by_name("baseline", Some("inactive"));
// 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)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<String>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. 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();
// We create a ping and a metric before we initialize Glean 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);
// 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".into());
sample_ping.submit(None);
// Wait for the ping to arrive. let url = r.recv().unwrap();
assert!(url.contains("deletion-request"));
}
#[test] fn registering_pings_before_init_must_work() { let _lock = lock_test();
// Define a fake uploader that reports back the submission headers // using a crossbeam channel. let (s, r) = crossbeam_channel::bounded::<String>(1);
// 216 characters, which would overflow StringMetric
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 signaling_done() { let _lock = lock_test();
// Define a fake uploader that reports back the submission URL // using a crossbeam channel. #[derive(Debug)] pubstruct FakeUploader {
barrier: Arc<Barrier>,
counter: Arc<Mutex<HashMap<ThreadId, u32>>>,
} impl net::PingUploader for FakeUploader { fn upload(&self, _upload_request: net::PingUploadRequest) -> net::UploadResult { letmut map = self.counter.lock().unwrap();
*map.entry(thread::current().id()).or_insert(0) += 1;
// Wait for the sync. self.barrier.wait();
// Signal that this uploader thread is done.
net::UploadResult::done()
}
}
// Create a custom configuration to use a fake uploader. let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
// We use a barrier to sync this test thread with the uploader thread. let barrier = Arc::new(Barrier::new(2)); // We count how many times `upload` was invoked per thread. let call_count = Arc::new(Mutex::default());
// Define a new ping and submit it. const PING_NAME: &str = "test-ping"; let custom_ping = new_test_ping(PING_NAME);
custom_ping.submit(None);
custom_ping.submit(None);
// Sync up with the upload thread.
barrier.wait();
// Submit another ping and wait for it to do work.
custom_ping.submit(None);
// Sync up with the upload thread again. // This will not be the same thread as the one before (hopefully).
barrier.wait();
// No one's ever gonna wait for the uploader thread (the RLB doesn't store the handle to it), // so all we can do is hope it finishes within time.
std::thread::sleep(std::time::Duration::from_millis(100));
let map = call_count.lock().unwrap();
assert_eq!(2, map.len(), "should have launched 2 uploader threads"); for &count in map.values() {
assert_eq!(1, count, "each thread should call upload only once");
}
}
#[test] fn configure_ping_throttling() { 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)] pubstruct FakeUploader {
sender: crossbeam_channel::Sender<String>,
done: Arc<std::sync::atomic::AtomicBool>,
} impl net::PingUploader for FakeUploader { fn upload(&self, upload_request: net::PingUploadRequest) -> net::UploadResult { ifself.done.load(std::sync::atomic::Ordering::SeqCst) { // If we've outlived the test, just lie. return net::UploadResult::http_status(200);
} self.sender.send(upload_request.url).unwrap();
net::UploadResult::http_status(200)
}
}
// Create a custom configuration to use a fake uploader. let dir = tempfile::tempdir().unwrap(); let tmpname = dir.path().to_path_buf();
// Define a new ping. const PING_NAME: &str = "test-ping"; let custom_ping = new_test_ping(PING_NAME);
// Submit and receive it `pings_per_interval` times. for _ in0..pings_per_interval {
custom_ping.submit(None);
// Wait for the ping to arrive. let url = r.recv().unwrap();
assert!(url.contains(PING_NAME));
}
// Submit one ping more than the rate limit permits.
custom_ping.submit(None);
// We'd expect it to be received within 250ms if it weren't throttled. let now = Instant::now();
assert_eq!(
r.recv_deadline(now + Duration::from_millis(250)),
Err(RecvTimeoutError::Timeout)
);
// We still have to deal with that eleventh ping. // When it eventually processes after the throttle interval, this'll tell // it that it's done.
done.store(true, std::sync::atomic::Ordering::SeqCst); // Unfortunately, we'll still be stuck waiting the full // `seconds_per_interval` before running the next test, since shutting down // will wait for the queue to clear.
}
#[test] fn pings_ride_along_builtin_pings() { let _lock = lock_test();
// Define a fake uploader that reports back the submission headers // using a crossbeam channel. let (s, r) = crossbeam_channel::bounded::<String>(3);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.