// 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 glean_core::{Glean, PingType, Result};
use std::fs::{read_dir, File};
use std::io::{BufRead, BufReader};
use std::path::Path;
use chrono::offset::TimeZone;
use iso8601::Date::YMD;
use serde_json::Value
as JsonValue;
use ctor::ctor;
/// Initialize the logger for all tests without individual tests requiring to call the init code.
/// Log output can be controlled via the environment variable `RUST_LOG` for the `glean_core` crate,
/// e.g.:
///
/// ```
/// export RUST_LOG=glean_core=debug
/// ```
#[ctor]
fn enable_test_logging() {
// When testing we want all logs to go to stdout/stderr by default,
// without requiring each individual test to activate it.
// This only applies to glean-core tests, users of the main library still need to call
// `glean_enable_logging` of the FFI component (automatically done by the platform wrappers).
let _ = env_logger::builder().is_test(
true).try_init();
}
pub fn tempdir() -> (tempfile::TempDir, String) {
let t = tempfile::tempdir().unwrap();
let name = t.path().display().to_string();
(t, name)
}
pub const GLOBAL_APPLICATION_ID: &str =
"org.mozilla.glean.test.app";
// Creates 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.
pub fn new_glean(tempdir: Option<tempfile::TempDir>) -> (Glean, tempfile::TempDir) {
let dir =
match tempdir {
Some(tempdir) => tempdir,
None => tempfile::tempdir().unwrap(),
};
let cfg = glean_core::InternalConfiguration {
data_path: dir.path().display().to_string(),
application_id: GLOBAL_APPLICATION_ID.into(),
language_binding_name:
"Rust".into(),
upload_enabled:
true,
max_events: None,
delay_ping_lifetime_io:
false,
app_build:
"Unknown".into(),
use_core_mps:
false,
trim_data_to_registered_pings:
false,
log_level: None,
rate_limit: None,
enable_event_timestamps:
false,
experimentation_id: None,
enable_internal_pings:
true,
ping_schedule: Default::default(),
ping_lifetime_threshold:
0,
ping_lifetime_max_time:
0,
};
let mut glean = Glean::new(cfg).unwrap();
// store{1,2} is used throughout tests
_ = new_test_ping(&
mut glean,
"store1");
_ = new_test_ping(&
mut glean,
"store2");
(glean, dir)
}
pub fn new_test_ping(glean: &
mut Glean, name: &str) -> PingType {
let ping = PingType::new(name,
true,
false,
true,
true,
true, vec![], vec![],
true);
glean.register_ping_type(&ping);
ping
}
/// Converts an iso8601::DateTime to a chrono::DateTime<FixedOffset>
pub fn iso8601_to_chrono(datetime: &iso8601::DateTime) -> chrono::DateTime<chrono::FixedOffset> {
if let YMD { year, month, day } = datetime.date {
return chrono::FixedOffset::east(datetime.time.tz_offset_hours *
3600)
.ymd(year, month, day)
.and_hms_milli(
datetime.time.hour,
datetime.time.minute,
datetime.time.second,
datetime.time.millisecond,
);
};
panic!(
"Unsupported datetime format");
}
/// Gets a vector of the currently queued pings.
///
/// # Arguments
///
/// * `data_path` - Glean's data path, as returned from Glean::get_data_path()
///
/// # Returns
///
/// A vector of all queued pings.
///
/// Each entry is a pair `(url, json_data, metadata)`,
/// where `url` is the endpoint the ping will go to, `json_data` is the JSON payload
/// and metadata is optional persisted data related to the ping.
pub fn get_queued_pings(data_path: &Path) -> Result<Vec<(String, JsonValue, Option<JsonValue>
)>> {
get_pings(&data_path.join("pending_pings"))
}
/// Gets a vector of the currently queued `deletion-request` pings.
///
/// # Arguments
///
/// * `data_path` - Glean's data path, as returned from Glean::get_data_path()
///
/// # Returns
///
/// A vector of all queued pings.
///
/// Each entry is a pair `(url, json_data, metadata)`,
/// where `url` is the endpoint the ping will go to, `json_data` is the JSON payload
/// and metadata is optional persisted data related to the ping.
pub fn get_deletion_pings(data_path: &Path) -> Result<Vec<(String, JsonValue, Option<JsonValue>)>> {
get_pings(&data_path.join("deletion_request"))
}
fn get_pings(pings_dir: &Path) -> Result<Vec<(String, JsonValue, Option<JsonValue>)>> {
let entries = read_dir(pings_dir)?;
Ok(entries
.filter_map(|entry| entry.ok())
.filter(|entry| match entry.file_type() {
Ok(file_type) => file_type.is_file(),
Err(_) => false,
})
.filter_map(|entry| File::open(entry.path()).ok())
.filter_map(|file| {
let mut lines = BufReader::new(file).lines();
if let (Some(Ok(url)), Some(Ok(body)), Ok(metadata)) =
(lines.next(), lines.next(), lines.next().transpose())
{
let parsed_metadata = metadata.map(|m| {
serde_json::from_str::<JsonValue>(&m).expect("metadata should be valid JSON")
});
if let Ok(parsed_body) = serde_json::from_str::<JsonValue>(&body) {
Some((url, parsed_body, parsed_metadata))
} else {
None
}
} else {
None
}
})
.collect())
}