summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean')
-rw-r--r--toolkit/components/glean/Cargo.toml6
-rw-r--r--toolkit/components/glean/api/Cargo.toml3
-rw-r--r--toolkit/components/glean/api/src/ffi/custom_distribution.rs2
-rw-r--r--toolkit/components/glean/api/src/ffi/memory_distribution.rs2
-rw-r--r--toolkit/components/glean/api/src/ffi/mod.rs1
-rw-r--r--toolkit/components/glean/api/src/ffi/object.rs68
-rw-r--r--toolkit/components/glean/api/src/ffi/timing_distribution.rs2
-rw-r--r--toolkit/components/glean/api/src/pings.rs2
-rw-r--r--toolkit/components/glean/api/src/private/custom_distribution.rs4
-rw-r--r--toolkit/components/glean/api/src/private/mod.rs2
-rw-r--r--toolkit/components/glean/api/src/private/object.rs83
-rw-r--r--toolkit/components/glean/api/src/private/ping.rs4
-rw-r--r--toolkit/components/glean/api/src/private/timing_distribution.rs4
-rw-r--r--toolkit/components/glean/bindings/GleanMetric.h1
-rw-r--r--toolkit/components/glean/bindings/MetricTypes.h1
-rw-r--r--toolkit/components/glean/bindings/jog/src/lib.rs6
-rw-r--r--toolkit/components/glean/bindings/private/CustomDistribution.cpp8
-rw-r--r--toolkit/components/glean/bindings/private/DistributionData.h6
-rw-r--r--toolkit/components/glean/bindings/private/MemoryDistribution.cpp8
-rw-r--r--toolkit/components/glean/bindings/private/Object.cpp80
-rw-r--r--toolkit/components/glean/bindings/private/Object.h94
-rw-r--r--toolkit/components/glean/bindings/private/Timespan.cpp1
-rw-r--r--toolkit/components/glean/bindings/private/TimingDistribution.cpp29
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/jog.py1
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py27
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/rust.py37
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja22
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja215
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/ohttp.jinja213
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2106
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja21
-rw-r--r--toolkit/components/glean/build_scripts/mach_commands.py17
-rw-r--r--toolkit/components/glean/metrics_index.py3
-rw-r--r--toolkit/components/glean/moz.build13
-rw-r--r--toolkit/components/glean/pings.yaml5
-rw-r--r--toolkit/components/glean/src/init/viaduct_uploader.rs205
-rw-r--r--toolkit/components/glean/src/lib.rs10
-rw-r--r--toolkit/components/glean/src/ohttp_pings.rs13
-rw-r--r--toolkit/components/glean/tags.yaml20
-rw-r--r--toolkit/components/glean/tests/gtest/TestFog.cpp6
-rw-r--r--toolkit/components/glean/tests/pytest/gifft_output_Event4
-rw-r--r--toolkit/components/glean/tests/pytest/gifft_output_Scalar6
-rw-r--r--toolkit/components/glean/tests/pytest/jogfile_output21
-rw-r--r--toolkit/components/glean/tests/pytest/metrics_test.yaml20
-rw-r--r--toolkit/components/glean/tests/pytest/metrics_test_output138
-rw-r--r--toolkit/components/glean/tests/pytest/metrics_test_output_cpp19
-rw-r--r--toolkit/components/glean/tests/pytest/metrics_test_output_js_cpp104
-rw-r--r--toolkit/components/glean/tests/pytest/metrics_test_output_js_h2
-rw-r--r--toolkit/components/glean/tests/pytest/pings_test.yaml18
-rw-r--r--toolkit/components/glean/tests/pytest/pings_test_output19
-rw-r--r--toolkit/components/glean/tests/pytest/pings_test_output_cpp7
-rw-r--r--toolkit/components/glean/tests/pytest/pings_test_output_js_cpp10
-rw-r--r--toolkit/components/glean/tests/pytest/pings_test_output_js_h2
-rw-r--r--toolkit/components/glean/tests/test_metrics.yaml19
-rw-r--r--toolkit/components/glean/tests/test_pings.yaml19
-rw-r--r--toolkit/components/glean/tests/xpcshell/head.js152
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_GIFFT.js10
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_GIFFTIPC.js7
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_Glean.js118
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_GleanIPC.js5
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_JOG.js27
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_JOGIPC.js5
-rw-r--r--toolkit/components/glean/tests/xpcshell/test_OHTTP.js32
-rw-r--r--toolkit/components/glean/tests/xpcshell/xpcshell.toml3
-rw-r--r--toolkit/components/glean/xpcom/FOG.cpp7
-rw-r--r--toolkit/components/glean/xpcom/nsIFOG.idl3
66 files changed, 1470 insertions, 218 deletions
diff --git a/toolkit/components/glean/Cargo.toml b/toolkit/components/glean/Cargo.toml
index 45423a183b..98b8d8a95b 100644
--- a/toolkit/components/glean/Cargo.toml
+++ b/toolkit/components/glean/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
license = "MPL-2.0"
[dependencies]
-glean = "57.0.0"
+glean = "58.1.0"
log = "0.4"
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
@@ -18,6 +18,10 @@ cstr = "0.2"
viaduct = "0.1"
url = "2.1"
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
+ohttp = { version = "0.3", default-features = false, features = ["gecko", "nss", "client"] }
+bhttp = "0.3"
+thiserror = "1.0"
+mozbuild = "0.1"
[features]
# Leave data collection enabled, but disable upload.
diff --git a/toolkit/components/glean/api/Cargo.toml b/toolkit/components/glean/api/Cargo.toml
index 403168fb90..dc688dc41e 100644
--- a/toolkit/components/glean/api/Cargo.toml
+++ b/toolkit/components/glean/api/Cargo.toml
@@ -9,7 +9,7 @@ license = "MPL-2.0"
[dependencies]
bincode = "1.0"
chrono = "0.4.10"
-glean = "57.0.0"
+glean = "58.1.0"
inherent = "1.0.0"
log = "0.4"
nsstring = { path = "../../../../xpcom/rust/nsstring", optional = true }
@@ -19,6 +19,7 @@ uuid = { version = "1.0", features = ["v4"] }
xpcom = { path = "../../../../xpcom/rust/xpcom", optional = true }
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
mozbuild = "0.1"
+serde_json = "1"
[dev-dependencies]
tempfile = "3.1.0"
diff --git a/toolkit/components/glean/api/src/ffi/custom_distribution.rs b/toolkit/components/glean/api/src/ffi/custom_distribution.rs
index 853a6e9845..643ebfbff5 100644
--- a/toolkit/components/glean/api/src/ffi/custom_distribution.rs
+++ b/toolkit/components/glean/api/src/ffi/custom_distribution.rs
@@ -22,6 +22,7 @@ pub extern "C" fn fog_custom_distribution_test_get_value(
id: u32,
ping_name: &nsACString,
sum: &mut u64,
+ count: &mut u64,
buckets: &mut ThinVec<u64>,
counts: &mut ThinVec<u64>,
) {
@@ -33,6 +34,7 @@ pub extern "C" fn fog_custom_distribution_test_get_value(
);
// FIXME(bug 1771885): Glean should use `u64` where it can.
*sum = val.sum as _;
+ *count = val.count as _;
for (&bucket, &count) in val.values.iter() {
buckets.push(bucket as _);
counts.push(count as _);
diff --git a/toolkit/components/glean/api/src/ffi/memory_distribution.rs b/toolkit/components/glean/api/src/ffi/memory_distribution.rs
index cf09d3f8de..35c8326c4d 100644
--- a/toolkit/components/glean/api/src/ffi/memory_distribution.rs
+++ b/toolkit/components/glean/api/src/ffi/memory_distribution.rs
@@ -22,6 +22,7 @@ pub extern "C" fn fog_memory_distribution_test_get_value(
id: u32,
ping_name: &nsACString,
sum: &mut u64,
+ count: &mut u64,
buckets: &mut ThinVec<u64>,
counts: &mut ThinVec<u64>,
) {
@@ -32,6 +33,7 @@ pub extern "C" fn fog_memory_distribution_test_get_value(
test_get!(metric, ping_name)
);
*sum = val.sum as _;
+ *count = val.count as _;
for (&bucket, &count) in val.values.iter() {
buckets.push(bucket as _);
counts.push(count as _);
diff --git a/toolkit/components/glean/api/src/ffi/mod.rs b/toolkit/components/glean/api/src/ffi/mod.rs
index 23235fc2f1..4eb614aefc 100644
--- a/toolkit/components/glean/api/src/ffi/mod.rs
+++ b/toolkit/components/glean/api/src/ffi/mod.rs
@@ -16,6 +16,7 @@ mod event;
mod labeled;
mod memory_distribution;
mod numerator;
+mod object;
mod ping;
mod quantity;
mod rate;
diff --git a/toolkit/components/glean/api/src/ffi/object.rs b/toolkit/components/glean/api/src/ffi/object.rs
new file mode 100644
index 0000000000..85f5269da9
--- /dev/null
+++ b/toolkit/components/glean/api/src/ffi/object.rs
@@ -0,0 +1,68 @@
+// 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/.
+
+#![cfg(feature = "with_gecko")]
+
+use nsstring::nsACString;
+
+use crate::metrics::__glean_metric_maps as metric_maps;
+
+#[no_mangle]
+pub extern "C" fn fog_object_set_string(id: u32, value: &nsACString) {
+ if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ panic!("No dynamic metric for objects");
+ }
+
+ let value = value.to_utf8().to_string();
+ if metric_maps::set_object_by_id(id, value).is_err() {
+ panic!("No object for id {}", id);
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fog_object_test_has_value(id: u32, ping_name: &nsACString) -> bool {
+ let storage = if ping_name.is_empty() {
+ None
+ } else {
+ Some(ping_name.to_utf8().into_owned())
+ };
+ if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ panic!("No dynamic metric for objects");
+ } else {
+ metric_maps::object_test_get_value(id, storage).is_some()
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn fog_object_test_get_value(
+ id: u32,
+ ping_name: &nsACString,
+ value: &mut nsACString,
+) {
+ let storage = if ping_name.is_empty() {
+ None
+ } else {
+ Some(ping_name.to_utf8().into_owned())
+ };
+
+ let object = if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ panic!("No dynamic metric for objects");
+ } else {
+ match metric_maps::object_test_get_value(id, storage) {
+ Some(object) => object,
+ None => return,
+ }
+ };
+ value.assign(&object);
+}
+
+#[no_mangle]
+pub extern "C" fn fog_object_test_get_error(id: u32, error_str: &mut nsACString) -> bool {
+ let err = if id & (1 << crate::factory::DYNAMIC_METRIC_BIT) > 0 {
+ panic!("No dynamic metric for objects");
+ } else {
+ metric_maps::object_test_get_error(id)
+ };
+ err.map(|err_str| error_str.assign(&err_str)).is_some()
+}
diff --git a/toolkit/components/glean/api/src/ffi/timing_distribution.rs b/toolkit/components/glean/api/src/ffi/timing_distribution.rs
index 4ac5d03986..4391985efa 100644
--- a/toolkit/components/glean/api/src/ffi/timing_distribution.rs
+++ b/toolkit/components/glean/api/src/ffi/timing_distribution.rs
@@ -58,6 +58,7 @@ pub extern "C" fn fog_timing_distribution_test_get_value(
id: u32,
ping_name: &nsACString,
sum: &mut u64,
+ count: &mut u64,
buckets: &mut ThinVec<u64>,
counts: &mut ThinVec<u64>,
) {
@@ -68,6 +69,7 @@ pub extern "C" fn fog_timing_distribution_test_get_value(
test_get!(metric, ping_name)
);
*sum = val.sum as _;
+ *count = val.count as _;
for (&bucket, &count) in val.values.iter() {
buckets.push(bucket as _);
counts.push(count as _);
diff --git a/toolkit/components/glean/api/src/pings.rs b/toolkit/components/glean/api/src/pings.rs
index f1d0332695..21eb3855ee 100644
--- a/toolkit/components/glean/api/src/pings.rs
+++ b/toolkit/components/glean/api/src/pings.rs
@@ -6,7 +6,7 @@
//!
//! The contents of this module are generated by
//! `toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py`, from
-//! 'toolkit/components/glean/pings.yaml`.
+//! ping definitions files identified by `toolkit/components/glean/metrics_index.py`.
include!(mozbuild::objdir_path!(
"toolkit/components/glean/api/src/pings.rs"
diff --git a/toolkit/components/glean/api/src/private/custom_distribution.rs b/toolkit/components/glean/api/src/private/custom_distribution.rs
index 2114430898..aeaf9b58c2 100644
--- a/toolkit/components/glean/api/src/private/custom_distribution.rs
+++ b/toolkit/components/glean/api/src/private/custom_distribution.rs
@@ -92,6 +92,10 @@ impl CustomDistribution for CustomDistributionMetric {
}
}
+ pub fn accumulate_single_sample_signed(&self, _sample: i64) {
+ unimplemented!("bug 1884183: expose this to FOG")
+ }
+
pub fn test_get_value<'a, S: Into<Option<&'a str>>>(
&self,
ping_name: S,
diff --git a/toolkit/components/glean/api/src/private/mod.rs b/toolkit/components/glean/api/src/private/mod.rs
index b0b1e11393..e86e121d72 100644
--- a/toolkit/components/glean/api/src/private/mod.rs
+++ b/toolkit/components/glean/api/src/private/mod.rs
@@ -24,6 +24,7 @@ mod labeled;
mod labeled_counter;
mod memory_distribution;
mod numerator;
+mod object;
mod ping;
mod quantity;
mod rate;
@@ -46,6 +47,7 @@ pub use self::labeled::LabeledMetric;
pub use self::labeled_counter::LabeledCounterMetric;
pub use self::memory_distribution::MemoryDistributionMetric;
pub use self::numerator::NumeratorMetric;
+pub use self::object::ObjectMetric;
pub use self::ping::Ping;
pub use self::quantity::QuantityMetric;
pub use self::rate::RateMetric;
diff --git a/toolkit/components/glean/api/src/private/object.rs b/toolkit/components/glean/api/src/private/object.rs
new file mode 100644
index 0000000000..5199cfad31
--- /dev/null
+++ b/toolkit/components/glean/api/src/private/object.rs
@@ -0,0 +1,83 @@
+// 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 super::{CommonMetricData, MetricId};
+
+use crate::ipc::need_ipc;
+
+use glean::traits::ObjectSerialize;
+
+/// An object metric.
+pub enum ObjectMetric<K> {
+ Parent {
+ id: MetricId,
+ inner: glean::private::ObjectMetric<K>,
+ },
+ Child,
+}
+
+impl<K: ObjectSerialize> ObjectMetric<K> {
+ /// Create a new object metric.
+ pub fn new(id: MetricId, meta: CommonMetricData) -> Self {
+ if need_ipc() {
+ ObjectMetric::Child
+ } else {
+ let inner = glean::private::ObjectMetric::new(meta);
+ ObjectMetric::Parent { id, inner }
+ }
+ }
+
+ pub fn set(&self, value: K) {
+ match self {
+ ObjectMetric::Parent { inner, .. } => {
+ inner.set(value);
+ }
+ ObjectMetric::Child => {
+ log::error!("Unable to set object metric in non-main process. This operation will be ignored.");
+ // TODO: Record an error.
+ }
+ };
+ }
+
+ pub fn set_string(&self, value: String) {
+ match self {
+ ObjectMetric::Parent { inner, .. } => {
+ inner.set_string(value);
+ }
+ ObjectMetric::Child => {
+ log::error!("Unable to set object metric in non-main process. This operation will be ignored.");
+ // TODO: Record an error.
+ }
+ };
+ }
+
+ pub fn test_get_value<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<serde_json::Value> {
+ match self {
+ ObjectMetric::Parent { inner, .. } => inner.test_get_value(ping_name),
+ ObjectMetric::Child => {
+ panic!("Cannot get test value for object metric in non-parent process!",)
+ }
+ }
+ }
+
+ pub fn test_get_value_as_str<'a, S: Into<Option<&'a str>>>(
+ &self,
+ ping_name: S,
+ ) -> Option<String> {
+ self.test_get_value(ping_name)
+ .map(|val| serde_json::to_string(&val).unwrap())
+ }
+
+ pub fn test_get_num_recorded_errors(&self, error: glean::ErrorType) -> i32 {
+ match self {
+ ObjectMetric::Parent { inner, .. } => inner.test_get_num_recorded_errors(error),
+ ObjectMetric::Child => {
+ panic!("Cannot get the number of recorded errors in non-parent process!")
+ }
+ }
+ }
+}
diff --git a/toolkit/components/glean/api/src/private/ping.rs b/toolkit/components/glean/api/src/private/ping.rs
index cc9585eea1..7e03c1ff00 100644
--- a/toolkit/components/glean/api/src/private/ping.rs
+++ b/toolkit/components/glean/api/src/private/ping.rs
@@ -30,6 +30,7 @@ impl Ping {
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Vec<String>,
) -> Self {
if need_ipc() {
@@ -40,6 +41,7 @@ impl Ping {
include_client_id,
send_if_empty,
precise_timestamps,
+ include_info_sections,
reason_codes,
))
}
@@ -103,7 +105,7 @@ mod test {
// Smoke test for what should be the generated code.
static PROTOTYPE_PING: Lazy<Ping> =
- Lazy::new(|| Ping::new("prototype", false, true, true, vec![]));
+ Lazy::new(|| Ping::new("prototype", false, true, true, true, vec![]));
#[test]
fn smoke_test_custom_ping() {
diff --git a/toolkit/components/glean/api/src/private/timing_distribution.rs b/toolkit/components/glean/api/src/private/timing_distribution.rs
index 0ab25cc900..6707560e41 100644
--- a/toolkit/components/glean/api/src/private/timing_distribution.rs
+++ b/toolkit/components/glean/api/src/private/timing_distribution.rs
@@ -374,6 +374,10 @@ impl TimingDistribution for TimingDistributionMetric {
}
}
+ pub fn accumulate_single_sample(&self, _sample: i64) {
+ unimplemented!("bug 1884183: expose this to FOG")
+ }
+
/// **Exported for test purposes.**
///
/// Gets the currently stored value of the metric.
diff --git a/toolkit/components/glean/bindings/GleanMetric.h b/toolkit/components/glean/bindings/GleanMetric.h
index 65ac75191d..c6bf6b4066 100644
--- a/toolkit/components/glean/bindings/GleanMetric.h
+++ b/toolkit/components/glean/bindings/GleanMetric.h
@@ -11,6 +11,7 @@
#include "nsIGlobalObject.h"
#include "nsWrapperCache.h"
#include "nsClassHashtable.h"
+#include "nsGlobalWindowInner.h"
#include "nsTHashMap.h"
#include "mozilla/DataMutex.h"
diff --git a/toolkit/components/glean/bindings/MetricTypes.h b/toolkit/components/glean/bindings/MetricTypes.h
index a7ae09fe19..6d855c2bf4 100644
--- a/toolkit/components/glean/bindings/MetricTypes.h
+++ b/toolkit/components/glean/bindings/MetricTypes.h
@@ -14,6 +14,7 @@
#include "mozilla/glean/bindings/Labeled.h"
#include "mozilla/glean/bindings/MemoryDistribution.h"
#include "mozilla/glean/bindings/Numerator.h"
+#include "mozilla/glean/bindings/Object.h"
#include "mozilla/glean/bindings/Quantity.h"
#include "mozilla/glean/bindings/Rate.h"
#include "mozilla/glean/bindings/String.h"
diff --git a/toolkit/components/glean/bindings/jog/src/lib.rs b/toolkit/components/glean/bindings/jog/src/lib.rs
index 4f2d439d80..b62e54f6e8 100644
--- a/toolkit/components/glean/bindings/jog/src/lib.rs
+++ b/toolkit/components/glean/bindings/jog/src/lib.rs
@@ -138,6 +138,7 @@ pub extern "C" fn jog_test_register_ping(
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: &ThinVec<nsCString>,
) -> u32 {
let ping_name = name.to_string();
@@ -150,6 +151,7 @@ pub extern "C" fn jog_test_register_ping(
include_client_id,
send_if_empty,
precise_timestamps,
+ include_info_sections,
reason_codes,
)
.expect("Creation or registration of ping failed.") // permitted to panic in test-only method.
@@ -160,6 +162,7 @@ fn create_and_register_ping(
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Vec<String>,
) -> Result<u32, Box<dyn std::error::Error>> {
let ns_name = nsCString::from(&ping_name);
@@ -168,6 +171,7 @@ fn create_and_register_ping(
include_client_id,
send_if_empty,
precise_timestamps,
+ include_info_sections,
reason_codes,
);
extern "C" {
@@ -214,6 +218,7 @@ struct PingDefinitionData {
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Option<Vec<String>>,
}
@@ -260,6 +265,7 @@ pub extern "C" fn jog_load_jogfile(jogfile_path: &nsAString) -> bool {
ping.include_client_id,
ping.send_if_empty,
ping.precise_timestamps,
+ ping.include_info_sections,
ping.reason_codes.unwrap_or_else(Vec::new),
);
}
diff --git a/toolkit/components/glean/bindings/private/CustomDistribution.cpp b/toolkit/components/glean/bindings/private/CustomDistribution.cpp
index 2f0226cb58..a5a821a558 100644
--- a/toolkit/components/glean/bindings/private/CustomDistribution.cpp
+++ b/toolkit/components/glean/bindings/private/CustomDistribution.cpp
@@ -59,9 +59,10 @@ CustomDistributionMetric::TestGetValue(const nsACString& aPingName) const {
nsTArray<uint64_t> buckets;
nsTArray<uint64_t> counts;
uint64_t sum;
- fog_custom_distribution_test_get_value(mId, &aPingName, &sum, &buckets,
- &counts);
- return Some(DistributionData(buckets, counts, sum));
+ uint64_t count;
+ fog_custom_distribution_test_get_value(mId, &aPingName, &sum, &count,
+ &buckets, &counts);
+ return Some(DistributionData(buckets, counts, sum, count));
}
} // namespace impl
@@ -92,6 +93,7 @@ void GleanCustomDistribution::TestGetValue(
dom::GleanDistributionData ret;
ret.mSum = optresult.ref().sum;
+ ret.mCount = optresult.ref().count;
auto& data = optresult.ref().values;
for (const auto& entry : data) {
dom::binding_detail::RecordEntry<nsCString, uint64_t> bucket;
diff --git a/toolkit/components/glean/bindings/private/DistributionData.h b/toolkit/components/glean/bindings/private/DistributionData.h
index 6ff995f222..fb9bba720e 100644
--- a/toolkit/components/glean/bindings/private/DistributionData.h
+++ b/toolkit/components/glean/bindings/private/DistributionData.h
@@ -12,6 +12,7 @@ namespace mozilla::glean {
struct DistributionData final {
uint64_t sum;
+ uint64_t count;
nsTHashMap<nsUint64HashKey, uint64_t> values;
/**
@@ -19,8 +20,9 @@ struct DistributionData final {
* as returned by `fog_*_distribution_test_get_value`.
*/
DistributionData(const nsTArray<uint64_t>& aBuckets,
- const nsTArray<uint64_t>& aCounts, uint64_t aSum)
- : sum(aSum) {
+ const nsTArray<uint64_t>& aCounts, uint64_t aSum,
+ uint64_t aCount)
+ : sum(aSum), count(aCount) {
for (size_t i = 0; i < aBuckets.Length(); ++i) {
this->values.InsertOrUpdate(aBuckets[i], aCounts[i]);
}
diff --git a/toolkit/components/glean/bindings/private/MemoryDistribution.cpp b/toolkit/components/glean/bindings/private/MemoryDistribution.cpp
index a580c5df3c..64f3bf241c 100644
--- a/toolkit/components/glean/bindings/private/MemoryDistribution.cpp
+++ b/toolkit/components/glean/bindings/private/MemoryDistribution.cpp
@@ -44,9 +44,10 @@ MemoryDistributionMetric::TestGetValue(const nsACString& aPingName) const {
nsTArray<uint64_t> buckets;
nsTArray<uint64_t> counts;
uint64_t sum;
- fog_memory_distribution_test_get_value(mId, &aPingName, &sum, &buckets,
- &counts);
- return Some(DistributionData(buckets, counts, sum));
+ uint64_t count;
+ fog_memory_distribution_test_get_value(mId, &aPingName, &sum, &count,
+ &buckets, &counts);
+ return Some(DistributionData(buckets, counts, sum, count));
}
} // namespace impl
@@ -76,6 +77,7 @@ void GleanMemoryDistribution::TestGetValue(
dom::GleanDistributionData ret;
ret.mSum = optresult.ref().sum;
+ ret.mCount = optresult.ref().count;
auto& data = optresult.ref().values;
for (const auto& entry : data) {
dom::binding_detail::RecordEntry<nsCString, uint64_t> bucket;
diff --git a/toolkit/components/glean/bindings/private/Object.cpp b/toolkit/components/glean/bindings/private/Object.cpp
new file mode 100644
index 0000000000..817b14dc0f
--- /dev/null
+++ b/toolkit/components/glean/bindings/private/Object.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/glean/bindings/Object.h"
+
+#include "Common.h"
+#include "mozilla/dom/GleanMetricsBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/Logging.h"
+#include "jsapi.h"
+#include "js/JSON.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla::dom;
+
+namespace mozilla::glean {
+
+/* virtual */
+JSObject* GleanObject::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return dom::GleanObject_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void GleanObject::Set(JSContext* aCx, JS::Handle<JSObject*> aObj) {
+ // We take in an `object`. Cannot be `null`!
+ // But at this point the type system doesn't know that.
+ JS::Rooted<JS::Value> value(aCx);
+ value.setObjectOrNull(aObj);
+
+ nsAutoString serializedValue;
+ bool res = nsContentUtils::StringifyJSON(aCx, value, serializedValue,
+ UndefinedIsNullStringLiteral);
+ if (!res) {
+ // JS_Stringify throws an exception, e.g. on cyclic objects.
+ // We don't want this rethrown.
+ JS_ClearPendingException(aCx);
+
+ LogToBrowserConsole(nsIScriptError::warningFlag,
+ u"passed in object cannot be serialized"_ns);
+ return;
+ }
+
+ NS_ConvertUTF16toUTF8 payload(serializedValue);
+ mObject.SetStr(payload);
+}
+
+void GleanObject::TestGetValue(JSContext* aCx, const nsACString& aPingName,
+ JS::MutableHandle<JSObject*> aResult,
+ ErrorResult& aRv) {
+ aResult.set(nullptr);
+
+ auto result = mObject.TestGetValue(aPingName);
+ if (result.isErr()) {
+ aRv.ThrowDataError(result.unwrapErr());
+ return;
+ }
+ auto optresult = result.unwrap();
+ if (optresult.isNothing()) {
+ return;
+ }
+
+ const NS_ConvertUTF8toUTF16 str(optresult.ref());
+ JS::Rooted<JS::Value> json(aCx);
+ bool res = JS_ParseJSON(aCx, str.get(), str.Length(), &json);
+ if (!res) {
+ aRv.ThrowDataError("couldn't parse stored object");
+ return;
+ }
+ if (!json.isObject()) {
+ aRv.ThrowDataError("stored data does not represent a valid object");
+ return;
+ }
+
+ aResult.set(&json.toObject());
+}
+
+} // namespace mozilla::glean
diff --git a/toolkit/components/glean/bindings/private/Object.h b/toolkit/components/glean/bindings/private/Object.h
new file mode 100644
index 0000000000..4c81f8b096
--- /dev/null
+++ b/toolkit/components/glean/bindings/private/Object.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_glean_GleanObject_h
+#define mozilla_glean_GleanObject_h
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/glean/bindings/GleanMetric.h"
+#include "mozilla/glean/fog_ffi_generated.h"
+#include "mozilla/ResultVariant.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla::glean {
+
+// forward declaration
+class GleanObject;
+
+namespace impl {
+
+template <class T>
+class ObjectMetric {
+ friend class mozilla::glean::GleanObject;
+
+ public:
+ constexpr explicit ObjectMetric(uint32_t id) : mId(id) {}
+
+ private:
+ const uint32_t mId;
+
+ /* TODO(bug 1881023): Turn this into the public C++ API */
+ /**
+ * **Test-only API**
+ *
+ * Gets the currently stored object as a JSON-encoded string.
+ *
+ * This function will attempt to await the last parent-process task (if any)
+ * writing to the the metric's storage engine before returning a value.
+ * This function will not wait for data from child processes.
+ *
+ * This doesn't clear the stored value.
+ * Parent process only. Panics in child processes.
+ *
+ * @param aPingName The (optional) name of the ping to retrieve the metric
+ * for. Defaults to the first value in `send_in_pings`.
+ *
+ * @return value of the stored metric, or Nothing() if there is no value.
+ */
+ Result<Maybe<nsCString>, nsCString> TestGetValue(
+ const nsACString& aPingName) const {
+ nsCString err;
+ if (fog_object_test_get_error(mId, &err)) {
+ return Err(err);
+ }
+ if (!fog_object_test_has_value(mId, &aPingName)) {
+ return Maybe<nsCString>();
+ }
+ nsCString ret;
+ fog_object_test_get_value(mId, &aPingName, &ret);
+ return Some(ret);
+ }
+
+ void SetStr(const nsACString& aValue) const {
+ fog_object_set_string(mId, &aValue);
+ }
+};
+
+} // namespace impl
+
+class GleanObject final : public GleanMetric {
+ public:
+ explicit GleanObject(uint32_t aId, nsISupports* aParent)
+ : GleanMetric(aParent), mObject(aId) {}
+
+ virtual JSObject* WrapObject(
+ JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
+
+ void Set(JSContext* aCx, JS::Handle<JSObject*> aObj);
+
+ void TestGetValue(JSContext* aCx, const nsACString& aPingName,
+ JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv);
+
+ virtual ~GleanObject() = default;
+
+ private:
+ const impl::ObjectMetric<void> mObject;
+};
+
+} // namespace mozilla::glean
+
+#endif /* mozilla_glean_GleanObject.h */
diff --git a/toolkit/components/glean/bindings/private/Timespan.cpp b/toolkit/components/glean/bindings/private/Timespan.cpp
index 13e57317fa..2ab1f0dbba 100644
--- a/toolkit/components/glean/bindings/private/Timespan.cpp
+++ b/toolkit/components/glean/bindings/private/Timespan.cpp
@@ -36,6 +36,7 @@ class ScalarIDHashKey : public PLDHashEntryHdr {
return static_cast<std::underlying_type<ScalarID>::type>(*aKey);
}
enum { ALLOW_MEMMOVE = true };
+ static_assert(std::is_trivially_copyable_v<ScalarID>);
private:
const ScalarID mValue;
diff --git a/toolkit/components/glean/bindings/private/TimingDistribution.cpp b/toolkit/components/glean/bindings/private/TimingDistribution.cpp
index f7a78165ae..036db5f9db 100644
--- a/toolkit/components/glean/bindings/private/TimingDistribution.cpp
+++ b/toolkit/components/glean/bindings/private/TimingDistribution.cpp
@@ -21,7 +21,10 @@
namespace mozilla::glean {
using MetricId = uint32_t; // Same type as in api/src/private/mod.rs
-using MetricTimerTuple = std::tuple<MetricId, TimerId>;
+struct MetricTimerTuple {
+ MetricId mMetricId;
+ TimerId mTimerId;
+};
class MetricTimerTupleHashKey : public PLDHashEntryHdr {
public:
using KeyType = const MetricTimerTuple&;
@@ -34,16 +37,17 @@ class MetricTimerTupleHashKey : public PLDHashEntryHdr {
KeyType GetKey() const { return mValue; }
bool KeyEquals(KeyTypePointer aKey) const {
- return std::get<0>(*aKey) == std::get<0>(mValue) &&
- std::get<1>(*aKey) == std::get<1>(mValue);
+ return aKey->mMetricId == mValue.mMetricId &&
+ aKey->mTimerId == mValue.mTimerId;
}
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey) {
// Chosen because this is how nsIntegralHashKey does it.
- return HashGeneric(std::get<0>(*aKey), std::get<1>(*aKey));
+ return HashGeneric(aKey->mMetricId, aKey->mTimerId);
}
enum { ALLOW_MEMMOVE = true };
+ static_assert(std::is_trivially_copyable_v<MetricTimerTuple>);
private:
const MetricTimerTuple mValue;
@@ -103,7 +107,7 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionStart(
auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
if (mirrorId) {
mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
- auto tuple = std::make_tuple(aMetricId, aTimerId);
+ auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
// It should be all but impossible for anyone to have already inserted
// this timer for this metric given the monotonicity of timer ids.
(void)NS_WARN_IF(lock.ref()->Remove(tuple));
@@ -118,7 +122,8 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionStopAndAccumulate(
auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
if (mirrorId) {
mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
- auto optStart = lock.ref()->Extract(std::make_tuple(aMetricId, aTimerId));
+ auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
+ auto optStart = lock.ref()->Extract(tuple);
// The timer might not be in the map to be removed if it's already been
// cancelled or stop_and_accumulate'd.
if (!NS_WARN_IF(!optStart)) {
@@ -145,8 +150,8 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionCancel(
mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
// The timer might not be in the map to be removed if it's already been
// cancelled or stop_and_accumulate'd.
- (void)NS_WARN_IF(
- !lock.ref()->Remove(std::make_tuple(aMetricId, aTimerId)));
+ auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
+ (void)NS_WARN_IF(!lock.ref()->Remove(tuple));
});
}
}
@@ -187,9 +192,10 @@ TimingDistributionMetric::TestGetValue(const nsACString& aPingName) const {
nsTArray<uint64_t> buckets;
nsTArray<uint64_t> counts;
uint64_t sum;
- fog_timing_distribution_test_get_value(mId, &aPingName, &sum, &buckets,
- &counts);
- return Some(DistributionData(buckets, counts, sum));
+ uint64_t count;
+ fog_timing_distribution_test_get_value(mId, &aPingName, &sum, &count,
+ &buckets, &counts);
+ return Some(DistributionData(buckets, counts, sum, count));
}
} // namespace impl
@@ -225,6 +231,7 @@ void GleanTimingDistribution::TestGetValue(
dom::GleanDistributionData ret;
ret.mSum = optresult.ref().sum;
+ ret.mCount = optresult.ref().count;
auto& data = optresult.ref().values;
for (const auto& entry : data) {
dom::binding_detail::RecordEntry<nsCString, uint64_t> bucket;
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/jog.py b/toolkit/components/glean/build_scripts/glean_parser_ext/jog.py
index 81d92b0f5d..b42827989e 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/jog.py
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/jog.py
@@ -55,6 +55,7 @@ known_ping_args = [
"include_client_id",
"send_if_empty",
"precise_timestamps",
+ "include_info_sections",
"reason_codes",
]
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py b/toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py
index 1d7d97cf73..bc9f09f0d3 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py
@@ -230,5 +230,32 @@ def jog_file(output_fd, *args):
return get_deps()
+def ohttp_pings(output_fd, *args):
+ all_objs, options = parse(args)
+ ohttp_pings = []
+ for ping in all_objs["pings"].values():
+ if ping.metadata.get("use_ohttp", False):
+ if ping.include_info_sections:
+ raise ParserError(
+ "Cannot send pings with OHTTP that contain {client|ping}_info sections. Specify `metadata: include_info_sections: false`"
+ )
+ ohttp_pings.append(ping.name)
+
+ env = jinja2.Environment(
+ loader=jinja2.PackageLoader("run_glean_parser", "templates"),
+ trim_blocks=True,
+ lstrip_blocks=True,
+ )
+ env.filters["quote_and_join"] = lambda l: "\n| ".join(f'"{x}"' for x in l)
+ template = env.get_template("ohttp.jinja2")
+ output_fd.write(
+ template.render(
+ ohttp_pings=ohttp_pings,
+ )
+ )
+ output_fd.write("\n")
+ return get_deps()
+
+
if __name__ == "__main__":
main(sys.stdout, *sys.argv[1:])
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/rust.py b/toolkit/components/glean/build_scripts/glean_parser_ext/rust.py
index 615784b481..4b2b35886b 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/rust.py
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/rust.py
@@ -68,9 +68,10 @@ def rust_datatypes_filter(value):
# CowString is also a 'str' but is a special case.
# Ensure its case is handled before str's (below).
elif isinstance(value, CowString):
- yield f'::std::borrow::Cow::from("{value.inner}")'
+ value = json.dumps(value)
+ yield f"::std::borrow::Cow::from({value})"
elif isinstance(value, str):
- yield '"' + value + '".into()'
+ yield f"{json.dumps(value)}.into()"
elif isinstance(value, Rate):
yield "CommonMetricData {"
for arg_name in common_metric_data_args:
@@ -118,6 +119,10 @@ def type_name(obj):
return "{}<{}>".format(
class_name(obj.type), util.Camelize(obj.name) + suffix
)
+ generate_structure = getattr(obj, "_generate_structure", [])
+ if len(generate_structure):
+ generic = util.Camelize(obj.name) + "Object"
+ return "{}<{}>".format(class_name(obj.type), generic)
return class_name(obj.type)
@@ -136,6 +141,21 @@ def extra_type_name(typ: str) -> str:
return "UNSUPPORTED"
+def structure_type_name(typ: str) -> str:
+ """
+ Returns the corresponding Rust type for structure items.
+ """
+
+ if typ == "boolean":
+ return "bool"
+ elif typ == "string":
+ return "String"
+ elif typ == "number":
+ return "i64"
+ else:
+ return "UNSUPPORTED"
+
+
def class_name(obj_type):
"""
Returns the Rust class name for a given metric or ping type.
@@ -208,6 +228,14 @@ def output_rust(objs, output_fd, ping_names_by_app_id, options={}):
# 17 -> "test_only::an_event"
events_by_id = {}
+ # Map from a metric ID to the fully qualified path of the object metric in Rust.
+ # Required for the special handling of object lookups.
+ #
+ # Example:
+ #
+ # 18 -> "test_only::an_object"
+ objects_by_id = {}
+
# Map from a labeled type (e.g. "counter") to a map from metric ID to the
# fully qualified path of the labeled metric object in Rust paired with
# whether the labeled metric has an enum.
@@ -238,6 +266,9 @@ def output_rust(objs, output_fd, ping_names_by_app_id, options={}):
if metric.type == "event":
events_by_id[get_metric_id(metric)] = full_path
continue
+ if metric.type == "object":
+ objects_by_id[get_metric_id(metric)] = full_path
+ continue
if getattr(metric, "labeled", False):
labeled_type = metric.type[8:]
@@ -261,6 +292,7 @@ def output_rust(objs, output_fd, ping_names_by_app_id, options={}):
("snake_case", util.snake_case),
("type_name", type_name),
("extra_type_name", extra_type_name),
+ ("structure_type_name", structure_type_name),
("ctor", ctor),
("extra_keys", extra_keys),
("metric_id", get_metric_id),
@@ -275,6 +307,7 @@ def output_rust(objs, output_fd, ping_names_by_app_id, options={}):
metric_by_type=objs_by_type,
extra_args=util.extra_args,
events_by_id=events_by_id,
+ objects_by_id=objects_by_id,
labeleds_by_id_by_type=labeleds_by_id_by_type,
submetric_bit=ID_BITS - ID_SIGNAL_BITS,
ping_names_by_app_id=ping_names_by_app_id,
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2
index 2a4e40d6ac..3e9d573232 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2
@@ -78,6 +78,7 @@ enum class DynamicLabel: uint16_t { };
{% for category_name, objs in all_objs.items() %}
namespace {{ category_name|snake_case }} {
{% for obj in objs.values() %}
+ {% if obj.type != "object" %}{# TODO(bug 1881023): Add C++ support #}
/**
* generated from {{ category_name }}.{{ obj.name }}
*/
@@ -91,6 +92,7 @@ namespace {{ category_name|snake_case }} {
* {{ obj.description|wordwrap() | replace('\n', '\n * ') }}
*/
constexpr impl::{{ obj|type_name }} {{obj.name|snake_case }}({{obj|metric_id}});
+ {% endif %}
{% endfor %}
}
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2
index 4b7838a47d..a31bdbabf0 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2
@@ -23,7 +23,9 @@ use crate::private::{
Ping,
LabeledMetric,
{% for metric_type_name in metric_types.keys() if not metric_type_name.startswith('labeled_') %}
+ {% if metric_type_name != "object" %}{# TODO(bug 1883857): Add JOG support #}
{{ metric_type_name|Camelize }}Metric,
+ {% endif %}
{% endfor %}};
use crate::private::traits::HistogramType;
@@ -50,7 +52,7 @@ pub(crate) mod __jog_metric_maps {
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
-{% for metric_type_name in metric_types.keys() if metric_type_name != "event" and not metric_type_name.startswith('labeled_') %}
+{% for metric_type_name in metric_types.keys() if metric_type_name != "event" and not metric_type_name.startswith('labeled_') and metric_type_name != "object" %}
pub static {{ metric_type_name.upper() }}_MAP: Lazy<Arc<RwLock<HashMap<MetricId, {{ metric_type_name|Camelize }}Metric>>>> =
Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
@@ -67,6 +69,10 @@ pub(crate) mod __jog_metric_maps {
{# Event metrics are special because they're EventMetric<K> #}
pub static EVENT_MAP: Lazy<Arc<RwLock<HashMap<MetricId, EventMetric<NoExtraKeys>>>>> =
Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
+{# Object metrics are special because they're ObjectMetric<K> #}
+ #[allow(dead_code)]
+ pub static OBJECT_MAP: Lazy<Arc<RwLock<HashMap<MetricId, ObjectMetric<()>>>>> =
+ Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
}
#[derive(Debug)]
@@ -105,6 +111,9 @@ map of argument name to argument type. I may regret this if I need it again. #}
let metric32 = match metric_type {
{% for metric_type_name, metric_type in metric_types.items() %}
"{{ metric_type_name }}" => {
+ {% if metric_type_name == "object" %}{# TODO(bug 1883857): Add JOG support #}
+ return Err(Box::new(MetricTypeNotFoundError(metric_type.to_string())));
+ {% else %}
let metric = {{ metric_type_name|Camelize if not metric_type_name.startswith('labeled_') else "Labeled"}}Metric::{% if metric_type_name == 'event' %}with_runtime_extra_keys{% else %}new{% endif %}(metric_id.into(), CommonMetricData {
{% for arg_name in common_metric_data_args if arg_name in metric_type.args %}
{{ arg_name }},
@@ -124,6 +133,7 @@ map of argument name to argument type. I may regret this if I need it again. #}
"We should never insert a runtime metric with an already-used id."
);
metric32
+ {% endif %}
}
{% endfor %}
_ => return Err(Box::new(MetricTypeNotFoundError(metric_type.to_string())))
@@ -137,10 +147,11 @@ pub fn create_and_register_ping(
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
+ include_info_sections: bool,
reason_codes: Vec<String>,
) -> Result<u32, Box<dyn std::error::Error>> {
let ping_id = NEXT_PING_ID.fetch_add(1, Ordering::SeqCst);
- let ping = Ping::new(ping_name, include_client_id, send_if_empty, precise_timestamps, reason_codes);
+ let ping = Ping::new(ping_name, include_client_id, send_if_empty, precise_timestamps, include_info_sections, reason_codes);
assert!(
__jog_metric_maps::PING_MAP.write()?.insert(ping_id.into(), ping).is_none(),
"We should never insert a runtime ping with an already-used id."
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/ohttp.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/ohttp.jinja2
new file mode 100644
index 0000000000..d6e1248021
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/ohttp.jinja2
@@ -0,0 +1,13 @@
+// -*- mode: Rust -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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 http://mozilla.org/MPL/2.0/. */
+
+pub fn uses_ohttp(ping_name: &str) -> bool {
+ matches!(ping_name, {{ ohttp_pings|quote_and_join }})
+}
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2
index cc29805099..5723ff5d58 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2
@@ -8,6 +8,41 @@ Jinja2 template is not. Please file bugs! #}
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+{% macro generate_structure(name, struct) %}
+{% if struct.type == "array" %}
+ pub type {{ name }} = Vec<{{ name }}Item>;
+
+ {{ generate_structure(name ~ "Item", struct["items"]) -}}
+
+{% elif struct.type == "object" %}
+ #[derive(Debug, Hash, Eq, PartialEq, ::glean::traits::__serde::Serialize, ::glean::traits::__serde::Deserialize)]
+ #[serde(deny_unknown_fields)]
+ pub struct {{ name }} {
+ {% for itemname, val in struct.properties.items() %}
+ {% if val.type == "object" %}
+ pub {{itemname|snake_case}}: {{ name ~ "Item" ~ itemname|Camelize ~ "Object" }},
+ {% elif val.type == "array" %}
+ pub {{itemname|snake_case}}: {{ name ~ "Item" ~ itemname|Camelize }},
+ {% else %}
+ pub {{itemname|snake_case}}: Option<{{val.type|structure_type_name}}>,
+ {% endif %}
+ {% endfor %}
+ }
+
+ {% for itemname, val in struct.properties.items() %}
+ {% if val.type == "array" %}
+ {% set nested_name = name ~ "Item" ~ itemname|Camelize %}
+ {{ generate_structure(nested_name, val) -}}
+ {% elif val.type == "object" %}
+ {% set nested_name = name ~ "Item" ~ itemname|Camelize ~ "Object" %}
+ {{ generate_structure(nested_name, val) -}}
+ {% endif %}
+ {% endfor %}
+{% else %}
+pub type {{ name }} = {{ struct.type|structure_type_name }};
+{% endif %}
+{% endmacro %}
+
{% macro generate_extra_keys(obj) -%}
{% for name, _ in obj["_generate_enums"] %}
{# we always use the `extra` suffix, because we only expose the new event API #}
@@ -81,6 +116,9 @@ pub mod {{ category_name|snake_case }} {
use glean::HistogramType;
use once_cell::sync::Lazy;
+ #[allow(unused_imports)]
+ use std::convert::TryFrom;
+
{% for obj in objs.values() %}
{% if obj|attr("_generate_enums") %}
{{ generate_extra_keys(obj) }}
@@ -88,6 +126,9 @@ pub mod {{ category_name|snake_case }} {
{% if obj.labeled and obj.labels and obj.labels|length %}
{{ generate_label_enum(obj)|indent }}
{% endif %}
+ {% if obj|attr("_generate_structure") %}
+ {{ generate_structure(obj.name|Camelize ~ "Object", obj._generate_structure) -}}
+ {% endif %}
#[allow(non_upper_case_globals)]
/// generated from {{ category_name }}.{{ obj.name }}
///
@@ -148,6 +189,71 @@ pub(crate) mod __glean_metric_maps {
{% endfor %}
+ pub(crate) fn set_object_by_id(metric_id: u32, value: String) -> Result<(), ()> {
+ match metric_id {
+{% for metric_id, object in objects_by_id.items() %}
+ {{metric_id}} => {
+ super::{{object}}.set_string(value);
+ Ok(())
+ }
+{% endfor %}
+ _ => Err(()),
+ }
+ }
+
+ /// Wrapper to get the currently stored object for object metric as a string.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - (Optional) The ping name to look into.
+ /// Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// Returns the recorded object serialized as a JSON string or `None` if nothing stored.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no object by the given metric ID could be found.
+ pub(crate) fn object_test_get_value(metric_id: u32, ping_name: Option<String>) -> Option<String> {
+ match metric_id {
+{% for metric_id, object in objects_by_id.items() %}
+ {{metric_id}} => super::{{object}}.test_get_value_as_str(ping_name.as_deref()),
+{% endfor %}
+ _ => panic!("No object for metric id {}", metric_id),
+ }
+ }
+
+ /// Check the provided object for errors.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ ///
+ /// # Returns
+ ///
+ /// Returns a string for the recorded error or `None`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no object by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn object_test_get_error(metric_id: u32) -> Option<String> {
+ #[cfg(feature = "with_gecko")]
+ match metric_id {
+{% for metric_id, object in objects_by_id.items() %}
+ {{metric_id}} => test_get_errors!(super::{{object}}),
+{% endfor %}
+ _ => panic!("No object for metric id {}", metric_id),
+ }
+
+ #[cfg(not(feature = "with_gecko"))]
+ {
+ return None;
+ }
+ }
+
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2
index c041f663a6..8afdae61ae 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2
@@ -20,6 +20,7 @@ pub static {{ obj.name|snake_case }}: Lazy<Ping> = Lazy::new(|| {
{{ obj.include_client_id|rust }},
{{ obj.send_if_empty|rust }},
{{ obj.precise_timestamps|rust }},
+ {{ obj.include_info_sections|rust }},
{{ obj.reason_codes|rust }},
)
});
diff --git a/toolkit/components/glean/build_scripts/mach_commands.py b/toolkit/components/glean/build_scripts/mach_commands.py
index d385e53605..4a0f6dbc68 100644
--- a/toolkit/components/glean/build_scripts/mach_commands.py
+++ b/toolkit/components/glean/build_scripts/mach_commands.py
@@ -183,8 +183,13 @@ def update_glean(command_context, version):
)
replace_in_file_or_die(
topsrcdir / "gfx" / "wr" / "webrender" / "Cargo.toml",
- r'^glean = "[0-9.]+"',
- f'glean = "{version}"',
+ r'^glean = { version = "[0-9.]+"(.+)}',
+ f'glean = {{ version = "{version}"\\1}}',
+ )
+ replace_in_file_or_die(
+ topsrcdir / "gfx" / "wr" / "wr_glyph_rasterizer" / "Cargo.toml",
+ r'^glean = { version = "[0-9.]+"(.+)}',
+ f'glean = {{ version = "{version}"\\1}}',
)
replace_in_file_or_die(
topsrcdir / "python" / "sites" / "mach.txt",
@@ -193,13 +198,9 @@ def update_glean(command_context, version):
)
instructions = f"""
- We've edited most of the necessary files to require Glean SDK {version}.
-
- You will have to edit the following files yourself:
-
- gfx/wr/wr_glyph_rasterizer/Cargo.toml
+ We've edited the necessary files to require Glean SDK {version}.
- Then, to ensure Glean and Firefox's other Rust dependencies are appropriately vendored,
+ To ensure Glean and Firefox's other Rust dependencies are appropriately vendored,
please run the following commands:
cargo update -p glean
diff --git a/toolkit/components/glean/metrics_index.py b/toolkit/components/glean/metrics_index.py
index aa7eea6645..8e20658810 100644
--- a/toolkit/components/glean/metrics_index.py
+++ b/toolkit/components/glean/metrics_index.py
@@ -22,6 +22,7 @@ gecko_metrics = [
"dom/media/webrtc/metrics.yaml",
"dom/metrics.yaml",
"dom/performance/metrics.yaml",
+ "dom/security/metrics.yaml",
"gfx/metrics.yaml",
"image/decoders/metrics.yaml",
"js/xpconnect/metrics.yaml",
@@ -50,6 +51,7 @@ gecko_metrics = [
# Metrics that are sent by Firefox Desktop
# Order is lexicographical, enforced by t/c/glean/tests/pytest/test_yaml_indices.py
firefox_desktop_metrics = [
+ "browser/components/backup/metrics.yaml",
"browser/components/metrics.yaml",
"browser/components/migration/metrics.yaml",
"browser/components/newtab/metrics.yaml",
@@ -124,6 +126,7 @@ firefox_desktop_pings = [
"browser/components/search/pings.yaml",
"browser/components/urlbar/pings.yaml",
"toolkit/components/crashes/pings.yaml",
+ "toolkit/components/resistfingerprinting/pings.yaml",
"toolkit/components/telemetry/pings.yaml",
"toolkit/modules/pings.yaml",
]
diff --git a/toolkit/components/glean/moz.build b/toolkit/components/glean/moz.build
index d6876167e6..df76dbcbc4 100644
--- a/toolkit/components/glean/moz.build
+++ b/toolkit/components/glean/moz.build
@@ -42,6 +42,7 @@ EXPORTS.mozilla.glean.bindings += [
"bindings/private/Labeled.h",
"bindings/private/MemoryDistribution.h",
"bindings/private/Numerator.h",
+ "bindings/private/Object.h",
"bindings/private/Ping.h",
"bindings/private/Quantity.h",
"bindings/private/Rate.h",
@@ -88,6 +89,7 @@ UNIFIED_SOURCES += [
"bindings/private/Labeled.cpp",
"bindings/private/MemoryDistribution.cpp",
"bindings/private/Numerator.cpp",
+ "bindings/private/Object.cpp",
"bindings/private/Ping.cpp",
"bindings/private/Quantity.cpp",
"bindings/private/Rate.cpp",
@@ -197,6 +199,17 @@ if CONFIG["MOZ_ARTIFACT_BUILDS"]:
# Once generated, it needs to be placed in GreD so it can be found.
FINAL_TARGET_FILES += ["!jogfile.json"]
+# OHTTP support requires the fog_control crate know which pings wish to be sent
+# using OHTTP. fog_control has no access to the firefox_on_glean crate, so it
+# needs its own codegen.
+GeneratedFile(
+ "src/ohttp_pings.rs",
+ script="build_scripts/glean_parser_ext/run_glean_parser.py",
+ entry_point="ohttp_pings",
+ flags=[CONFIG["MOZ_APP_VERSION"]],
+ inputs=pings_yamls + tags_yamls,
+)
+
DIRS += [
"tests", # Must be in DIRS, not TEST_DIRS or python-test won't find it.
"xpcom",
diff --git a/toolkit/components/glean/pings.yaml b/toolkit/components/glean/pings.yaml
index d4710a47c2..22c49aab7e 100644
--- a/toolkit/components/glean/pings.yaml
+++ b/toolkit/components/glean/pings.yaml
@@ -6,8 +6,9 @@
# automatically converted to platform-specific code at build time using the
# `glean_parser` PyPI package.
-# This file is presently for Internal FOG Use Only.
-# You should not add pings here until probably about January of 2021.
+# This file is for Internal FOG Use Only.
+# You probably don't want to add pings here. For more information consult:
+# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
---
$schema: moz://mozilla.org/schemas/glean/pings/2-0-0
diff --git a/toolkit/components/glean/src/init/viaduct_uploader.rs b/toolkit/components/glean/src/init/viaduct_uploader.rs
index d9ce4e0488..a009e664bc 100644
--- a/toolkit/components/glean/src/init/viaduct_uploader.rs
+++ b/toolkit/components/glean/src/init/viaduct_uploader.rs
@@ -2,7 +2,9 @@
// 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 glean::net::{PingUploader, UploadResult};
+use glean::net::{PingUploadRequest, PingUploader, UploadResult};
+use once_cell::sync::OnceCell;
+use std::sync::Once;
use url::Url;
use viaduct::{Error::*, Request};
@@ -19,55 +21,176 @@ impl PingUploader for ViaductUploader {
///
/// # Arguments
///
- /// * `url` - the URL path to upload the data to.
- /// * `body` - the serialized text data to send.
- /// * `headers` - a vector of tuples containing the headers to send with
- /// the request, i.e. (Name, Value).
- fn upload(&self, url: String, body: Vec<u8>, headers: Vec<(String, String)>) -> UploadResult {
- log::trace!("FOG Ping Uploader uploading to {}", url);
- let url_clone = url.clone();
- let result: std::result::Result<UploadResult, viaduct::Error> = (move || {
- // SAFETY NOTE: Safe because it returns a primitive by value.
- if unsafe { FOG_TooLateToSend() } {
- log::trace!("Attempted to send ping too late into shutdown.");
- return Ok(UploadResult::done());
- }
- let debug_tagged = headers.iter().any(|(name, _)| name == "X-Debug-ID");
- let localhost_port = static_prefs::pref!("telemetry.fog.test.localhost_port");
- if localhost_port < 0
- || (localhost_port == 0 && !debug_tagged && cfg!(feature = "disable_upload"))
- {
- log::info!("FOG Ping uploader faking success");
- return Ok(UploadResult::http_status(200));
- }
- let parsed_url = Url::parse(&url_clone)?;
-
- log::info!("FOG Ping uploader uploading to {:?}", parsed_url);
-
- let mut req = Request::post(parsed_url.clone()).body(body.clone());
- for (header_key, header_value) in &headers {
- req = req.header(header_key.to_owned(), header_value)?;
- }
-
- log::trace!("FOG Ping Uploader sending ping to {}", parsed_url);
- let res = req.send()?;
- Ok(UploadResult::http_status(res.status as i32))
- })();
+ /// * `upload_request` - the ping and its metadata to upload.
+ fn upload(&self, upload_request: PingUploadRequest) -> UploadResult {
+ log::trace!("FOG Ping Uploader uploading to {}", upload_request.url);
+
+ // SAFETY NOTE: Safe because it returns a primitive by value.
+ if unsafe { FOG_TooLateToSend() } {
+ log::trace!("Attempted to send ping too late into shutdown.");
+ return UploadResult::done();
+ }
+
+ let debug_tagged = upload_request
+ .headers
+ .iter()
+ .any(|(name, _)| name == "X-Debug-ID");
+ let localhost_port = static_prefs::pref!("telemetry.fog.test.localhost_port");
+ if localhost_port < 0
+ || (localhost_port == 0 && !debug_tagged && cfg!(feature = "disable_upload"))
+ {
+ log::info!("FOG Ping uploader faking success");
+ return UploadResult::http_status(200);
+ }
+
+ // Localhost-destined pings are sent without OHTTP,
+ // even if configured to use OHTTP.
+ let result = if localhost_port == 0 && should_ohttp_upload(&upload_request) {
+ ohttp_upload(upload_request)
+ } else {
+ viaduct_upload(upload_request)
+ };
+
log::trace!(
- "FOG Ping Uploader completed uploading to {} (Result {:?})",
- url,
+ "FOG Ping Uploader completed uploading (Result {:?})",
result
);
+
match result {
Ok(result) => result,
- Err(NonTlsUrl | UrlError(_)) => UploadResult::unrecoverable_failure(),
- Err(
+ Err(ViaductUploaderError::Viaduct(ve)) => match ve {
+ NonTlsUrl | UrlError(_) => UploadResult::unrecoverable_failure(),
RequestHeaderError(_)
| BackendError(_)
| NetworkError(_)
| BackendNotInitialized
- | SetBackendError,
- ) => UploadResult::recoverable_failure(),
+ | SetBackendError => UploadResult::recoverable_failure(),
+ },
+ Err(
+ ViaductUploaderError::Bhttp(_)
+ | ViaductUploaderError::Ohttp(_)
+ | ViaductUploaderError::Fatal,
+ ) => UploadResult::unrecoverable_failure(),
}
}
}
+
+fn viaduct_upload(upload_request: PingUploadRequest) -> Result<UploadResult, ViaductUploaderError> {
+ let parsed_url = Url::parse(&upload_request.url)?;
+
+ log::info!("FOG viaduct uploader uploading to {:?}", parsed_url);
+
+ let mut req = Request::post(parsed_url.clone()).body(upload_request.body);
+ for (header_key, header_value) in &upload_request.headers {
+ req = req.header(header_key.to_owned(), header_value)?;
+ }
+
+ log::trace!("FOG viaduct uploader sending ping to {:?}", parsed_url);
+ let res = req.send()?;
+ Ok(UploadResult::http_status(res.status as i32))
+}
+
+fn should_ohttp_upload(upload_request: &PingUploadRequest) -> bool {
+ crate::ohttp_pings::uses_ohttp(&upload_request.ping_name)
+ && !upload_request.body_has_info_sections
+}
+
+fn ohttp_upload(upload_request: PingUploadRequest) -> Result<UploadResult, ViaductUploaderError> {
+ static CELL: OnceCell<Vec<u8>> = once_cell::sync::OnceCell::new();
+ let config = CELL.get_or_try_init(|| get_config())?;
+
+ let binary_request = bhttp_encode(upload_request)?;
+
+ static OHTTP_INIT: Once = Once::new();
+ OHTTP_INIT.call_once(|| {
+ ohttp::init();
+ });
+
+ let ohttp_request = ohttp::ClientRequest::new(config)?;
+ let (capsule, ohttp_response) = ohttp_request.encapsulate(&binary_request)?;
+
+ const OHTTP_RELAY_URL: &str = "https://mozilla-ohttp.fastly-edge.com/";
+ let parsed_relay_url = Url::parse(OHTTP_RELAY_URL)?;
+
+ log::trace!("FOG ohttp uploader uploading to {}", parsed_relay_url);
+
+ const OHTTP_MESSAGE_CONTENT_TYPE: &str = "message/ohttp-req";
+ let req = Request::post(parsed_relay_url)
+ .header(
+ viaduct::header_names::CONTENT_TYPE,
+ OHTTP_MESSAGE_CONTENT_TYPE,
+ )?
+ .body(capsule);
+ let res = req.send()?;
+
+ if res.status == 200 {
+ // This just tells us the HTTP went well. Check OHTTP's status.
+ let binary_response = ohttp_response.decapsulate(&res.body)?;
+ let mut cursor = std::io::Cursor::new(binary_response);
+ let bhttp_message = bhttp::Message::read_bhttp(&mut cursor)?;
+ let res = bhttp_message
+ .control()
+ .status()
+ .ok_or(ViaductUploaderError::Fatal)?;
+ Ok(UploadResult::http_status(res as i32))
+ } else {
+ Ok(UploadResult::http_status(res.status as i32))
+ }
+}
+
+fn get_config() -> Result<Vec<u8>, ViaductUploaderError> {
+ const OHTTP_CONFIG_URL: &str =
+ "https://prod.ohttp-gateway.prod.webservices.mozgcp.net/ohttp-configs";
+ log::trace!("Getting OHTTP config from {}", OHTTP_CONFIG_URL);
+ let parsed_config_url = Url::parse(OHTTP_CONFIG_URL)?;
+ Ok(Request::get(parsed_config_url).send()?.body)
+}
+
+/// Encode the ping upload request in binary HTTP.
+/// (draft-ietf-httpbis-binary-message)
+fn bhttp_encode(upload_request: PingUploadRequest) -> Result<Vec<u8>, ViaductUploaderError> {
+ let parsed_url = Url::parse(&upload_request.url)?;
+ let mut message = bhttp::Message::request(
+ "POST".into(),
+ parsed_url.scheme().into(),
+ parsed_url
+ .host_str()
+ .ok_or(ViaductUploaderError::Fatal)?
+ .into(),
+ parsed_url.path().into(),
+ );
+
+ upload_request
+ .headers
+ .into_iter()
+ .for_each(|(k, v)| message.put_header(k, v));
+
+ message.write_content(upload_request.body);
+
+ let mut encoded = vec![];
+ message.write_bhttp(bhttp::Mode::KnownLength, &mut encoded)?;
+
+ Ok(encoded)
+}
+
+/// Unioned error across upload backends.
+#[derive(Debug, thiserror::Error)]
+enum ViaductUploaderError {
+ #[error("bhttp::Error {0}")]
+ Bhttp(#[from] bhttp::Error),
+
+ #[error("ohttp::Error {0}")]
+ Ohttp(#[from] ohttp::Error),
+
+ #[error("viaduct::Error {0}")]
+ Viaduct(#[from] viaduct::Error),
+
+ #[error("Fatal upload error")]
+ Fatal,
+}
+
+impl From<url::ParseError> for ViaductUploaderError {
+ fn from(e: url::ParseError) -> Self {
+ ViaductUploaderError::Viaduct(viaduct::Error::from(e))
+ }
+}
diff --git a/toolkit/components/glean/src/lib.rs b/toolkit/components/glean/src/lib.rs
index 79f3258bb7..b682f43066 100644
--- a/toolkit/components/glean/src/lib.rs
+++ b/toolkit/components/glean/src/lib.rs
@@ -28,6 +28,7 @@ extern crate cstr;
extern crate xpcom;
mod init;
+mod ohttp_pings;
pub use init::fog_init;
@@ -46,6 +47,7 @@ static mut PENDING_BUF: Vec<u8> = Vec::new();
// IPC serialization/deserialization methods
// Crucially important that the first two not be called on multiple threads.
+/// # Safety
/// Only safe if only called on a single thread (the same single thread you call
/// fog_give_ipc_buf on).
#[no_mangle]
@@ -59,6 +61,7 @@ pub unsafe extern "C" fn fog_serialize_ipc_buf() -> usize {
}
}
+/// # Safety
/// Only safe if called on a single thread (the same single thread you call
/// fog_serialize_ipc_buf on), and if buf points to an allocated buffer of at
/// least buf_len bytes.
@@ -73,6 +76,7 @@ pub unsafe extern "C" fn fog_give_ipc_buf(buf: *mut u8, buf_len: usize) -> usize
pending_len
}
+/// # Safety
/// Only safe if buf points to an allocated buffer of at least buf_len bytes.
/// No ownership is transfered to Rust by this method: caller owns the memory at
/// buf before and after this call.
@@ -92,9 +96,9 @@ pub unsafe extern "C" fn fog_use_ipc_buf(buf: *const u8, buf_len: usize) {
pub extern "C" fn fog_set_debug_view_tag(value: &nsACString) -> nsresult {
let result = glean::set_debug_view_tag(&value.to_string());
if result {
- return NS_OK;
+ NS_OK
} else {
- return NS_ERROR_FAILURE;
+ NS_ERROR_FAILURE
}
}
@@ -137,7 +141,7 @@ pub extern "C" fn fog_set_experiment_active(
extra_values.len(),
"Experiment extra keys and values differ in length."
);
- let extra = if extra_keys.len() == 0 {
+ let extra = if extra_keys.is_empty() {
None
} else {
Some(
diff --git a/toolkit/components/glean/src/ohttp_pings.rs b/toolkit/components/glean/src/ohttp_pings.rs
new file mode 100644
index 0000000000..71b9512a8b
--- /dev/null
+++ b/toolkit/components/glean/src/ohttp_pings.rs
@@ -0,0 +1,13 @@
+// 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 file contains the generated logic for ohttp pings.
+//!
+//! The contents of this module are generated by
+//! `toolkit/components/glean/build_scripts/glean_parser_ext/run_glean_parser.py`, from
+//! ping definitions files identified by `toolkit/components/glean/metrics_index.py`.
+
+include!(mozbuild::objdir_path!(
+ "toolkit/components/glean/src/ohttp_pings.rs"
+));
diff --git a/toolkit/components/glean/tags.yaml b/toolkit/components/glean/tags.yaml
index 9c6c09bb9c..c935371b28 100644
--- a/toolkit/components/glean/tags.yaml
+++ b/toolkit/components/glean/tags.yaml
@@ -35,6 +35,8 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Core :: Cycle Collector':
description: The Bugzilla component which applies to this object.
+'Core :: DLL Services':
+ description: The Bugzilla component which applies to this object.
'Core :: DOM: Animation':
description: The Bugzilla component which applies to this object.
'Core :: DOM: Bindings (WebIDL)':
@@ -95,8 +97,6 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Core :: Disability Access APIs':
description: The Bugzilla component which applies to this object.
-'Core :: Document Navigation':
- description: The Bugzilla component which applies to this object.
'Core :: Gecko Profiler':
description: The Bugzilla component which applies to this object.
'Core :: General':
@@ -119,6 +119,8 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Core :: IPC':
description: The Bugzilla component which applies to this object.
+'Core :: IPC: MSCOM':
+ description: The Bugzilla component which applies to this object.
'Core :: Internationalization':
description: The Bugzilla component which applies to this object.
'Core :: JavaScript Engine':
@@ -169,6 +171,8 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Core :: MFBT':
description: The Bugzilla component which applies to this object.
+'Core :: Machine Learning':
+ description: The Bugzilla component which applies to this object.
'Core :: MathML':
description: The Bugzilla component which applies to this object.
'Core :: Memory Allocator':
@@ -211,6 +215,8 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Core :: Security: Process Sandboxing':
description: The Bugzilla component which applies to this object.
+'Core :: Security: RLBox':
+ description: The Bugzilla component which applies to this object.
'Core :: Spelling checker':
description: The Bugzilla component which applies to this object.
'Core :: Storage: Cache API':
@@ -337,6 +343,8 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Firefox :: Keyboard Navigation':
description: The Bugzilla component which applies to this object.
+'Firefox :: Launcher Process':
+ description: The Bugzilla component which applies to this object.
'Firefox :: Menus':
description: The Bugzilla component which applies to this object.
'Firefox :: Messaging System':
@@ -357,6 +365,8 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Firefox :: Private Browsing':
description: The Bugzilla component which applies to this object.
+'Firefox :: Profiles':
+ description: The Bugzilla component which applies to this object.
'Firefox :: Protections UI':
description: The Bugzilla component which applies to this object.
'Firefox :: Remote Settings Client':
@@ -497,8 +507,6 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Toolkit :: IOUtils and PathUtils':
description: The Bugzilla component which applies to this object.
-'Toolkit :: NSIS Installer':
- description: The Bugzilla component which applies to this object.
'Toolkit :: Password Manager':
description: The Bugzilla component which applies to this object.
'Toolkit :: Performance Monitoring':
@@ -525,14 +533,14 @@ $schema: moz://mozilla.org/schemas/glean/tags/1-0-0
description: The Bugzilla component which applies to this object.
'Toolkit :: Themes':
description: The Bugzilla component which applies to this object.
+'Toolkit :: UI Widgets':
+ description: The Bugzilla component which applies to this object.
'Toolkit :: UniFFI Bindings':
description: The Bugzilla component which applies to this object.
'Toolkit :: Video/Audio Controls':
description: The Bugzilla component which applies to this object.
'Toolkit :: View Source':
description: The Bugzilla component which applies to this object.
-'Toolkit :: UI Widgets':
- description: The Bugzilla component which applies to this object.
'Toolkit :: about:memory':
description: The Bugzilla component which applies to this object.
'Web Compatibility :: Tooling & Investigations':
diff --git a/toolkit/components/glean/tests/gtest/TestFog.cpp b/toolkit/components/glean/tests/gtest/TestFog.cpp
index 2de8f9ba4d..0c1621911e 100644
--- a/toolkit/components/glean/tests/gtest/TestFog.cpp
+++ b/toolkit/components/glean/tests/gtest/TestFog.cpp
@@ -181,6 +181,7 @@ TEST_F(FOGFixture, TestCppMemoryDistWorks) {
// Sum is in bytes, test_only::do_you_remember is in megabytes. So
// multiplication ahoy!
ASSERT_EQ(data.sum, 24UL * 1024 * 1024);
+ ASSERT_EQ(data.count, 2UL);
for (const auto& entry : data.values) {
const uint64_t bucket = entry.GetKey();
const uint64_t count = entry.GetData();
@@ -196,6 +197,7 @@ TEST_F(FOGFixture, TestCppCustomDistWorks) {
DistributionData data =
test_only_ipc::a_custom_dist.TestGetValue("store1"_ns).unwrap().ref();
ASSERT_EQ(data.sum, 7UL + 268435458);
+ ASSERT_EQ(data.count, 2UL);
for (const auto& entry : data.values) {
const uint64_t bucket = entry.GetKey();
const uint64_t count = entry.GetData();
@@ -253,6 +255,10 @@ TEST_F(FOGFixture, TestCppTimingDistWorks) {
DistributionData data =
test_only::what_time_is_it.TestGetValue().unwrap().ref();
+
+ // Cancelled timers should not increase count.
+ ASSERT_EQ(data.count, 2UL);
+
const uint64_t NANOS_IN_MILLIS = 1e6;
// bug 1701847 - Sleeps don't necessarily round up as you'd expect.
diff --git a/toolkit/components/glean/tests/pytest/gifft_output_Event b/toolkit/components/glean/tests/pytest/gifft_output_Event
index ba80e3300b..203dc19a62 100644
--- a/toolkit/components/glean/tests/pytest/gifft_output_Event
+++ b/toolkit/components/glean/tests/pytest/gifft_output_Event
@@ -26,10 +26,10 @@ using Telemetry::EventID;
static inline Maybe<EventID> EventIdForMetric(uint32_t aId) {
switch(aId) {
- case 17: { // test.nested.event_metric
+ case 18: { // test.nested.event_metric
return Some(EventID::EventMetric_EnumNames_AreStrange);
}
- case 18: { // test.nested.event_metric_with_extra
+ case 19: { // test.nested.event_metric_with_extra
return Some(EventID::EventMetric_EnumName_WithExtra);
}
default: {
diff --git a/toolkit/components/glean/tests/pytest/gifft_output_Scalar b/toolkit/components/glean/tests/pytest/gifft_output_Scalar
index 854455b584..9e1db3e84d 100644
--- a/toolkit/components/glean/tests/pytest/gifft_output_Scalar
+++ b/toolkit/components/glean/tests/pytest/gifft_output_Scalar
@@ -58,13 +58,13 @@ static inline Maybe<ScalarID> ScalarIdForMetric(uint32_t aId) {
case 14: { // test.timespan_metric
return Some(ScalarID::SOME_OTHER_UINT_SCALAR);
}
- case 16: { // test.nested.datetime_metric
+ case 17: { // test.nested.datetime_metric
return Some(ScalarID::SOME_STILL_OTHER_STRING_SCALAR);
}
- case 21: { // test.nested.quantity_metric
+ case 22: { // test.nested.quantity_metric
return Some(ScalarID::TELEMETRY_TEST_MIRROR_FOR_QUANTITY);
}
- case 24: { // test.nested.uuid_metric
+ case 25: { // test.nested.uuid_metric
return Some(ScalarID::SOME_OTHER_STRING_SCALAR);
}
default: {
diff --git a/toolkit/components/glean/tests/pytest/jogfile_output b/toolkit/components/glean/tests/pytest/jogfile_output
index d75c08c8ba..510b4995c5 100644
--- a/toolkit/components/glean/tests/pytest/jogfile_output
+++ b/toolkit/components/glean/tests/pytest/jogfile_output
@@ -189,6 +189,15 @@
],
"test.nested": [
[
+ "object",
+ "an_object",
+ [
+ "metrics"
+ ],
+ "ping",
+ false
+ ],
+ [
"datetime",
"datetime_metric",
[
@@ -303,6 +312,7 @@
true,
false,
true,
+ true,
[
"background",
"dirty_startup",
@@ -314,6 +324,7 @@
true,
true,
true,
+ true,
[]
],
[
@@ -321,6 +332,7 @@
true,
false,
true,
+ true,
[
"background",
"max_capacity",
@@ -332,6 +344,7 @@
true,
false,
true,
+ true,
[
"overdue",
"reschedule",
@@ -339,6 +352,14 @@
"tomorrow",
"upgrade"
]
+ ],
+ [
+ "not-ohttp",
+ false,
+ true,
+ true,
+ false,
+ []
]
]
} \ No newline at end of file
diff --git a/toolkit/components/glean/tests/pytest/metrics_test.yaml b/toolkit/components/glean/tests/pytest/metrics_test.yaml
index cbbb0220b5..6d9a4f8c0e 100644
--- a/toolkit/components/glean/tests/pytest/metrics_test.yaml
+++ b/toolkit/components/glean/tests/pytest/metrics_test.yaml
@@ -396,3 +396,23 @@ test.nested:
- https://bugzilla.mozilla.org/1635260/
data_reviews:
- https://example.com
+
+ an_object:
+ type: object
+ description: An example object
+ bugs:
+ - https://bugzilla.mozilla.org/1839640
+ data_reviews:
+ - http://example.com/reviews
+ notification_emails:
+ - CHANGE-ME@example.com
+ expires: never
+ structure:
+ type: array
+ items:
+ type: object
+ properties:
+ colour:
+ type: string
+ diameter:
+ type: number
diff --git a/toolkit/components/glean/tests/pytest/metrics_test_output b/toolkit/components/glean/tests/pytest/metrics_test_output
index 76808cc984..0f93eb032d 100644
--- a/toolkit/components/glean/tests/pytest/metrics_test_output
+++ b/toolkit/components/glean/tests/pytest/metrics_test_output
@@ -6,6 +6,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
pub enum DynamicLabel { }
pub mod test {
@@ -16,6 +17,9 @@ pub mod test {
use glean::HistogramType;
use once_cell::sync::Lazy;
+ #[allow(unused_imports)]
+ use std::convert::TryFrom;
+
#[allow(non_upper_case_globals)]
/// generated from test.boolean_metric
///
@@ -361,13 +365,40 @@ pub mod test_nested {
use glean::HistogramType;
use once_cell::sync::Lazy;
+ #[allow(unused_imports)]
+ use std::convert::TryFrom;
+
+ pub type AnObjectObject = Vec<AnObjectObjectItem>;
+
+ #[derive(Debug, Hash, Eq, PartialEq, ::glean::traits::__serde::Serialize, ::glean::traits::__serde::Deserialize)]
+ #[serde(deny_unknown_fields)]
+ pub struct AnObjectObjectItem {
+ pub colour: Option<String>,
+ pub diameter: Option<i64>,
+ }
+
+ #[allow(non_upper_case_globals)]
+ /// generated from test.nested.an_object
+ ///
+ /// An example object
+ pub static an_object: Lazy<ObjectMetric<AnObjectObject>> = Lazy::new(|| {
+ ObjectMetric::new(16.into(), CommonMetricData {
+ name: "an_object".into(),
+ category: "test.nested".into(),
+ send_in_pings: vec!["metrics".into()],
+ lifetime: Lifetime::Ping,
+ disabled: false,
+ ..Default::default()
+ })
+ });
+
#[allow(non_upper_case_globals)]
/// generated from test.nested.datetime_metric
///
/// A multi-line
/// description
pub static datetime_metric: Lazy<DatetimeMetric> = Lazy::new(|| {
- DatetimeMetric::new(16.into(), CommonMetricData {
+ DatetimeMetric::new(17.into(), CommonMetricData {
name: "datetime_metric".into(),
category: "test.nested".into(),
send_in_pings: vec!["metrics".into()],
@@ -383,7 +414,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static event_metric: Lazy<EventMetric<NoExtraKeys>> = Lazy::new(|| {
- EventMetric::new(17.into(), CommonMetricData {
+ EventMetric::new(18.into(), CommonMetricData {
name: "event_metric".into(),
category: "test.nested".into(),
send_in_pings: vec!["events".into()],
@@ -415,7 +446,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static event_metric_with_extra: Lazy<EventMetric<EventMetricWithExtraExtra>> = Lazy::new(|| {
- EventMetric::new(18.into(), CommonMetricData {
+ EventMetric::new(19.into(), CommonMetricData {
name: "event_metric_with_extra".into(),
category: "test.nested".into(),
send_in_pings: vec!["events".into()],
@@ -431,7 +462,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static external_denominator: Lazy<DenominatorMetric> = Lazy::new(|| {
- DenominatorMetric::new(19.into(), CommonMetricData {
+ DenominatorMetric::new(20.into(), CommonMetricData {
name: "external_denominator".into(),
category: "test.nested".into(),
send_in_pings: vec!["metrics".into()],
@@ -448,7 +479,7 @@ pub mod test_nested {
/// description
pub static optimizable_counter_metric: Lazy<CounterMetric> = Lazy::new(|| {
CounterMetric::codegen_new(
- 20,
+ 21,
"test.nested",
"optimizable_counter_metric",
"metrics"
@@ -461,7 +492,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static quantity_metric: Lazy<QuantityMetric> = Lazy::new(|| {
- QuantityMetric::new(21.into(), CommonMetricData {
+ QuantityMetric::new(22.into(), CommonMetricData {
name: "quantity_metric".into(),
category: "test.nested".into(),
send_in_pings: vec!["metrics".into()],
@@ -477,7 +508,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static rate_metric: Lazy<RateMetric> = Lazy::new(|| {
- RateMetric::new(22.into(), CommonMetricData {
+ RateMetric::new(23.into(), CommonMetricData {
name: "rate_metric".into(),
category: "test.nested".into(),
send_in_pings: vec!["metrics".into()],
@@ -493,7 +524,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static rate_with_external_denominator: Lazy<NumeratorMetric> = Lazy::new(|| {
- NumeratorMetric::new(23.into(), CommonMetricData {
+ NumeratorMetric::new(24.into(), CommonMetricData {
name: "rate_with_external_denominator".into(),
category: "test.nested".into(),
send_in_pings: vec!["metrics".into()],
@@ -509,7 +540,7 @@ pub mod test_nested {
/// A multi-line
/// description
pub static uuid_metric: Lazy<UuidMetric> = Lazy::new(|| {
- UuidMetric::new(24.into(), CommonMetricData {
+ UuidMetric::new(25.into(), CommonMetricData {
name: "uuid_metric".into(),
category: "test.nested".into(),
send_in_pings: vec!["metrics".into()],
@@ -538,7 +569,7 @@ pub(crate) mod __glean_metric_maps {
pub static COUNTER_MAP: Lazy<HashMap<MetricId, &Lazy<CounterMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(2);
map.insert(2.into(), &super::test::counter_metric);
- map.insert(20.into(), &super::test_nested::optimizable_counter_metric);
+ map.insert(21.into(), &super::test_nested::optimizable_counter_metric);
map
});
@@ -586,41 +617,100 @@ pub(crate) mod __glean_metric_maps {
pub static DATETIME_MAP: Lazy<HashMap<MetricId, &Lazy<DatetimeMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(1);
- map.insert(16.into(), &super::test_nested::datetime_metric);
+ map.insert(17.into(), &super::test_nested::datetime_metric);
map
});
pub static DENOMINATOR_MAP: Lazy<HashMap<MetricId, &Lazy<DenominatorMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(1);
- map.insert(19.into(), &super::test_nested::external_denominator);
+ map.insert(20.into(), &super::test_nested::external_denominator);
map
});
pub static QUANTITY_MAP: Lazy<HashMap<MetricId, &Lazy<QuantityMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(1);
- map.insert(21.into(), &super::test_nested::quantity_metric);
+ map.insert(22.into(), &super::test_nested::quantity_metric);
map
});
pub static RATE_MAP: Lazy<HashMap<MetricId, &Lazy<RateMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(1);
- map.insert(22.into(), &super::test_nested::rate_metric);
+ map.insert(23.into(), &super::test_nested::rate_metric);
map
});
pub static NUMERATOR_MAP: Lazy<HashMap<MetricId, &Lazy<NumeratorMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(1);
- map.insert(23.into(), &super::test_nested::rate_with_external_denominator);
+ map.insert(24.into(), &super::test_nested::rate_with_external_denominator);
map
});
pub static UUID_MAP: Lazy<HashMap<MetricId, &Lazy<UuidMetric>>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(1);
- map.insert(24.into(), &super::test_nested::uuid_metric);
+ map.insert(25.into(), &super::test_nested::uuid_metric);
map
});
+ pub(crate) fn set_object_by_id(metric_id: u32, value: String) -> Result<(), ()> {
+ match metric_id {
+ 16 => {
+ super::test_nested::an_object.set_string(value);
+ Ok(())
+ }
+ _ => Err(()),
+ }
+ }
+
+ /// Wrapper to get the currently stored object for object metric as a string.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - (Optional) The ping name to look into.
+ /// Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// Returns the recorded object serialized as a JSON string or `None` if nothing stored.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no object by the given metric ID could be found.
+ pub(crate) fn object_test_get_value(metric_id: u32, ping_name: Option<String>) -> Option<String> {
+ match metric_id {
+ 16 => super::test_nested::an_object.test_get_value_as_str(ping_name.as_deref()),
+ _ => panic!("No object for metric id {}", metric_id),
+ }
+ }
+
+ /// Check the provided object for errors.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ ///
+ /// # Returns
+ ///
+ /// Returns a string for the recorded error or `None`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no object by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn object_test_get_error(metric_id: u32) -> Option<String> {
+ #[cfg(feature = "with_gecko")]
+ match metric_id {
+ 16 => test_get_errors!(super::test_nested::an_object),
+ _ => panic!("No object for metric id {}", metric_id),
+ }
+
+ #[cfg(not(feature = "with_gecko"))]
+ {
+ return None;
+ }
+ }
+
/// Wrapper to record an event based on its metric ID.
///
/// # Arguments
@@ -635,7 +725,7 @@ pub(crate) mod __glean_metric_maps {
/// or an `EventRecordingError::InvalidExtraKey` if the `extra` map could not be deserialized.
pub(crate) fn record_event_by_id(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
match metric_id {
- 17 => {
+ 18 => {
assert!(
extra_keys_len(&super::test_nested::event_metric) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
@@ -644,7 +734,7 @@ pub(crate) mod __glean_metric_maps {
super::test_nested::event_metric.record_raw(extra);
Ok(())
}
- 18 => {
+ 19 => {
assert!(
extra_keys_len(&super::test_nested::event_metric_with_extra) != 0 || extra.is_empty(),
"No extra keys allowed, but some were passed"
@@ -673,7 +763,7 @@ pub(crate) mod __glean_metric_maps {
/// but some are passed in.
pub(crate) fn record_event_by_id_with_time(metric_id: MetricId, timestamp: u64, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
match metric_id {
- MetricId(17) => {
+ MetricId(18) => {
if extra_keys_len(&super::test_nested::event_metric) == 0 && !extra.is_empty() {
return Err(EventRecordingError::InvalidExtraKey);
}
@@ -681,7 +771,7 @@ pub(crate) mod __glean_metric_maps {
super::test_nested::event_metric.record_with_time(timestamp, extra);
Ok(())
}
- MetricId(18) => {
+ MetricId(19) => {
if extra_keys_len(&super::test_nested::event_metric_with_extra) == 0 && !extra.is_empty() {
return Err(EventRecordingError::InvalidExtraKey);
}
@@ -710,8 +800,8 @@ pub(crate) mod __glean_metric_maps {
/// Panics if no event by the given metric ID could be found.
pub(crate) fn event_test_get_value_wrapper(metric_id: u32, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
match metric_id {
- 17 => super::test_nested::event_metric.test_get_value(ping_name.as_deref()),
- 18 => super::test_nested::event_metric_with_extra.test_get_value(ping_name.as_deref()),
+ 18 => super::test_nested::event_metric.test_get_value(ping_name.as_deref()),
+ 19 => super::test_nested::event_metric_with_extra.test_get_value(ping_name.as_deref()),
_ => panic!("No event for metric id {}", metric_id),
}
}
@@ -735,8 +825,8 @@ pub(crate) mod __glean_metric_maps {
pub(crate) fn event_test_get_error(metric_id: u32) -> Option<String> {
#[cfg(feature = "with_gecko")]
match metric_id {
- 17 => test_get_errors!(super::test_nested::event_metric),
- 18 => test_get_errors!(super::test_nested::event_metric_with_extra),
+ 18 => test_get_errors!(super::test_nested::event_metric),
+ 19 => test_get_errors!(super::test_nested::event_metric_with_extra),
_ => panic!("No event for metric id {}", metric_id),
}
diff --git a/toolkit/components/glean/tests/pytest/metrics_test_output_cpp b/toolkit/components/glean/tests/pytest/metrics_test_output_cpp
index 5b9c7bfae4..615e1c857d 100644
--- a/toolkit/components/glean/tests/pytest/metrics_test_output_cpp
+++ b/toolkit/components/glean/tests/pytest/metrics_test_output_cpp
@@ -181,6 +181,7 @@ namespace test {
}
namespace test_nested {
+
/**
* generated from test.nested.datetime_metric
*/
@@ -188,7 +189,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::DatetimeMetric datetime_metric(16);
+ constexpr impl::DatetimeMetric datetime_metric(17);
/**
* generated from test.nested.event_metric
@@ -197,7 +198,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::EventMetric<NoExtraKeys> event_metric(17);
+ constexpr impl::EventMetric<NoExtraKeys> event_metric(18);
/**
* generated from test.nested.event_metric_with_extra
@@ -224,7 +225,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::EventMetric<EventMetricWithExtraExtra> event_metric_with_extra(18);
+ constexpr impl::EventMetric<EventMetricWithExtraExtra> event_metric_with_extra(19);
/**
* generated from test.nested.external_denominator
@@ -233,7 +234,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::DenominatorMetric external_denominator(19);
+ constexpr impl::DenominatorMetric external_denominator(20);
/**
* generated from test.nested.optimizable_counter_metric
@@ -242,7 +243,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::CounterMetric optimizable_counter_metric(20);
+ constexpr impl::CounterMetric optimizable_counter_metric(21);
/**
* generated from test.nested.quantity_metric
@@ -251,7 +252,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::QuantityMetric quantity_metric(21);
+ constexpr impl::QuantityMetric quantity_metric(22);
/**
* generated from test.nested.rate_metric
@@ -260,7 +261,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::RateMetric rate_metric(22);
+ constexpr impl::RateMetric rate_metric(23);
/**
* generated from test.nested.rate_with_external_denominator
@@ -269,7 +270,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::NumeratorMetric rate_with_external_denominator(23);
+ constexpr impl::NumeratorMetric rate_with_external_denominator(24);
/**
* generated from test.nested.uuid_metric
@@ -278,7 +279,7 @@ namespace test_nested {
* A multi-line
* description
*/
- constexpr impl::UuidMetric uuid_metric(24);
+ constexpr impl::UuidMetric uuid_metric(25);
}
diff --git a/toolkit/components/glean/tests/pytest/metrics_test_output_js_cpp b/toolkit/components/glean/tests/pytest/metrics_test_output_js_cpp
index ba4619bb57..7cb5ce7d12 100644
--- a/toolkit/components/glean/tests/pytest/metrics_test_output_js_cpp
+++ b/toolkit/components/glean/tests/pytest/metrics_test_output_js_cpp
@@ -39,8 +39,8 @@ using metric_entry_t = uint64_t;
static_assert(GLEAN_INDEX_BITS + GLEAN_TYPE_BITS + GLEAN_ID_BITS == sizeof(metric_entry_t) * 8, "Index, Type, and ID bits need to fit into a metric_entry_t");
static_assert(GLEAN_TYPE_BITS + GLEAN_ID_BITS <= sizeof(uint32_t) * 8, "Metric Types and IDs need to fit into at most 32 bits");
static_assert(2 < UINT32_MAX, "Too many metric categories generated.");
-static_assert(24 < 33554432, "Too many metrics generated. Need room for 2 signal bits.");
-static_assert(19 < 32, "Too many different metric types.");
+static_assert(25 < 33554432, "Too many metrics generated. Need room for 2 signal bits.");
+static_assert(20 < 32, "Too many different metric types.");
already_AddRefed<GleanMetric> NewMetricFromId(uint32_t id, nsISupports* aParent) {
uint32_t typeId = GLEAN_TYPE_ID(id);
@@ -95,31 +95,35 @@ already_AddRefed<GleanMetric> NewMetricFromId(uint32_t id, nsISupports* aParent)
{
return MakeAndAddRef<GleanTimingDistribution>(metricId, aParent);
}
- case 13: /* datetime */
+ case 13: /* object */
+ {
+ return MakeAndAddRef<GleanObject>(metricId, aParent);
+ }
+ case 14: /* datetime */
{
return MakeAndAddRef<GleanDatetime>(metricId, aParent);
}
- case 14: /* event */
+ case 15: /* event */
{
return MakeAndAddRef<GleanEvent>(metricId, aParent);
}
- case 15: /* denominator */
+ case 16: /* denominator */
{
return MakeAndAddRef<GleanDenominator>(metricId, aParent);
}
- case 16: /* quantity */
+ case 17: /* quantity */
{
return MakeAndAddRef<GleanQuantity>(metricId, aParent);
}
- case 17: /* rate */
+ case 18: /* rate */
{
return MakeAndAddRef<GleanRate>(metricId, aParent);
}
- case 18: /* numerator */
+ case 19: /* numerator */
{
return MakeAndAddRef<GleanNumerator>(metricId, aParent);
}
- case 19: /* uuid */
+ case 20: /* uuid */
{
return MakeAndAddRef<GleanUuid>(metricId, aParent);
}
@@ -289,45 +293,47 @@ constexpr char gMetricStringTable[] = {
/* 310 - "test.textMetric" */ 't', 'e', 's', 't', '.', 't', 'e', 'x', 't', 'M', 'e', 't', 'r', 'i', 'c', '\0',
/* 326 - "test.timespanMetric" */ 't', 'e', 's', 't', '.', 't', 'i', 'm', 'e', 's', 'p', 'a', 'n', 'M', 'e', 't', 'r', 'i', 'c', '\0',
/* 346 - "test.timingDistributionMetric" */ 't', 'e', 's', 't', '.', 't', 'i', 'm', 'i', 'n', 'g', 'D', 'i', 's', 't', 'r', 'i', 'b', 'u', 't', 'i', 'o', 'n', 'M', 'e', 't', 'r', 'i', 'c', '\0',
- /* 376 - "testNested.datetimeMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'd', 'a', 't', 'e', 't', 'i', 'm', 'e', 'M', 'e', 't', 'r', 'i', 'c', '\0',
- /* 402 - "testNested.eventMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'v', 'e', 'n', 't', 'M', 'e', 't', 'r', 'i', 'c', '\0',
- /* 425 - "testNested.eventMetricWithExtra" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'v', 'e', 'n', 't', 'M', 'e', 't', 'r', 'i', 'c', 'W', 'i', 't', 'h', 'E', 'x', 't', 'r', 'a', '\0',
- /* 457 - "testNested.externalDenominator" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'x', 't', 'e', 'r', 'n', 'a', 'l', 'D', 'e', 'n', 'o', 'm', 'i', 'n', 'a', 't', 'o', 'r', '\0',
- /* 488 - "testNested.optimizableCounterMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'o', 'p', 't', 'i', 'm', 'i', 'z', 'a', 'b', 'l', 'e', 'C', 'o', 'u', 'n', 't', 'e', 'r', 'M', 'e', 't', 'r', 'i', 'c', '\0',
- /* 524 - "testNested.quantityMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'q', 'u', 'a', 'n', 't', 'i', 't', 'y', 'M', 'e', 't', 'r', 'i', 'c', '\0',
- /* 550 - "testNested.rateMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'r', 'a', 't', 'e', 'M', 'e', 't', 'r', 'i', 'c', '\0',
- /* 572 - "testNested.rateWithExternalDenominator" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'r', 'a', 't', 'e', 'W', 'i', 't', 'h', 'E', 'x', 't', 'e', 'r', 'n', 'a', 'l', 'D', 'e', 'n', 'o', 'm', 'i', 'n', 'a', 't', 'o', 'r', '\0',
- /* 611 - "testNested.uuidMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'u', 'u', 'i', 'd', 'M', 'e', 't', 'r', 'i', 'c', '\0',
+ /* 376 - "testNested.anObject" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'a', 'n', 'O', 'b', 'j', 'e', 'c', 't', '\0',
+ /* 396 - "testNested.datetimeMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'd', 'a', 't', 'e', 't', 'i', 'm', 'e', 'M', 'e', 't', 'r', 'i', 'c', '\0',
+ /* 422 - "testNested.eventMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'v', 'e', 'n', 't', 'M', 'e', 't', 'r', 'i', 'c', '\0',
+ /* 445 - "testNested.eventMetricWithExtra" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'v', 'e', 'n', 't', 'M', 'e', 't', 'r', 'i', 'c', 'W', 'i', 't', 'h', 'E', 'x', 't', 'r', 'a', '\0',
+ /* 477 - "testNested.externalDenominator" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'x', 't', 'e', 'r', 'n', 'a', 'l', 'D', 'e', 'n', 'o', 'm', 'i', 'n', 'a', 't', 'o', 'r', '\0',
+ /* 508 - "testNested.optimizableCounterMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'o', 'p', 't', 'i', 'm', 'i', 'z', 'a', 'b', 'l', 'e', 'C', 'o', 'u', 'n', 't', 'e', 'r', 'M', 'e', 't', 'r', 'i', 'c', '\0',
+ /* 544 - "testNested.quantityMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'q', 'u', 'a', 'n', 't', 'i', 't', 'y', 'M', 'e', 't', 'r', 'i', 'c', '\0',
+ /* 570 - "testNested.rateMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'r', 'a', 't', 'e', 'M', 'e', 't', 'r', 'i', 'c', '\0',
+ /* 592 - "testNested.rateWithExternalDenominator" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'r', 'a', 't', 'e', 'W', 'i', 't', 'h', 'E', 'x', 't', 'e', 'r', 'n', 'a', 'l', 'D', 'e', 'n', 'o', 'm', 'i', 'n', 'a', 't', 'o', 'r', '\0',
+ /* 631 - "testNested.uuidMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'u', 'u', 'i', 'd', 'M', 'e', 't', 'r', 'i', 'c', '\0',
};
static_assert(sizeof(gMetricStringTable) < 4294967296, "Metric string table is too large.");
const metric_entry_t sMetricByNameLookupEntries[] = {
- 6341068335467200838ull,
- 3458764548180279480ull,
- 1152921590506193384ull,
- 576460756598390784ull,
- 2882303787286921342ull,
- 6917529092065591642ull,
- 9799832883647480358ull,
- 2305843026393563204ull,
- 7493989848663982456ull,
+ 5764607578868810038ull,
1152921513196781587ull,
- 8070450609557340585ull,
- 2305843030688530526ull,
- 5188146822270419236ull,
- 8646911366155731401ull,
1729382269795172390ull,
- 10952754396844261987ull,
+ 3458764552475246801ull,
+ 10952754396844261968ull,
+ 4611686065672028430ull,
+ 2305843026393563204ull,
+ 1152921594801160700ull,
+ 5188146822270419236ull,
+ 3458764548180279480ull,
+ 8646911361860764070ull,
+ 8646911366155731389ull,
+ 8070450605262373260ull,
+ 9799832883647480352ull,
+ 11529215153442652791ull,
+ 2305843030688530526ull,
+ 6917529092065591642ull,
+ 9223372122754122205ull,
+ 10376293640245871162ull,
4035225309073637616ull,
- 10376293640245871164ull,
- 9223372127049089548ull,
- 5764607578868810038ull,
- 8070450605262373266ull,
+ 2882303787286921342ull,
+ 576460756598390784ull,
+ 7493989848663982456ull,
2882303791581888664ull,
- 4611686065672028430ull,
- 3458764552475246801ull
+ 6341068335467200838ull
};
@@ -365,39 +371,39 @@ MetricByNameLookup(const nsACString& aKey)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
- 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 6, 0, 12, 0, 0, 0, 0, 0, 0, 0,
- 11, 0, 0, 0, 3, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 6, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
diff --git a/toolkit/components/glean/tests/pytest/metrics_test_output_js_h b/toolkit/components/glean/tests/pytest/metrics_test_output_js_h
index 6ce9833732..99887c5a74 100644
--- a/toolkit/components/glean/tests/pytest/metrics_test_output_js_h
+++ b/toolkit/components/glean/tests/pytest/metrics_test_output_js_h
@@ -69,7 +69,7 @@ Maybe<uint32_t> MetricByNameLookup(const nsACString&);
Maybe<uint32_t> CategoryByNameLookup(const nsACString&);
extern const category_entry_t sCategoryByNameLookupEntries[2];
-extern const metric_entry_t sMetricByNameLookupEntries[24];
+extern const metric_entry_t sMetricByNameLookupEntries[25];
} // namespace mozilla::glean
#endif // mozilla_GleanJSMetricsLookup_h
diff --git a/toolkit/components/glean/tests/pytest/pings_test.yaml b/toolkit/components/glean/tests/pytest/pings_test.yaml
index efa6ba9e0a..558cbb1022 100644
--- a/toolkit/components/glean/tests/pytest/pings_test.yaml
+++ b/toolkit/components/glean/tests/pytest/pings_test.yaml
@@ -7,7 +7,7 @@
# `glean_parser` PyPI package.
---
-$schema: moz://mozilla.org/schemas/glean/pings/1-0-0
+$schema: moz://mozilla.org/schemas/glean/pings/2-0-0
not-baseline:
description: >
@@ -114,3 +114,19 @@ not-deletion-request:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1587095#c6
notification_emails:
- glean-team@mozilla.com
+
+not-ohttp:
+ description: >
+ A fake OHTTP-using ping
+ include_client_id: false
+ metadata:
+ include_info_sections: false
+ use_ohttp: true
+ send_if_empty: true
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1862002
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1862002
+ notification_emails:
+ - chutten@mozilla.com
+ - glean-team@mozilla.com
diff --git a/toolkit/components/glean/tests/pytest/pings_test_output b/toolkit/components/glean/tests/pytest/pings_test_output
index 9e037eb22a..97c0793b1f 100644
--- a/toolkit/components/glean/tests/pytest/pings_test_output
+++ b/toolkit/components/glean/tests/pytest/pings_test_output
@@ -20,6 +20,7 @@ pub static not_baseline: Lazy<Ping> = Lazy::new(|| {
true,
false,
true,
+ true,
vec!["background".into(), "dirty_startup".into(), "foreground".into()],
)
});
@@ -36,6 +37,7 @@ pub static not_deletion_request: Lazy<Ping> = Lazy::new(|| {
true,
true,
true,
+ true,
vec![],
)
});
@@ -50,6 +52,7 @@ pub static not_events: Lazy<Ping> = Lazy::new(|| {
true,
false,
true,
+ true,
vec!["background".into(), "max_capacity".into(), "startup".into()],
)
});
@@ -68,10 +71,24 @@ pub static not_metrics: Lazy<Ping> = Lazy::new(|| {
true,
false,
true,
+ true,
vec!["overdue".into(), "reschedule".into(), "today".into(), "tomorrow".into(), "upgrade".into()],
)
});
+#[allow(non_upper_case_globals)]
+/// A fake OHTTP-using ping
+pub static not_ohttp: Lazy<Ping> = Lazy::new(|| {
+ Ping::new(
+ "not-ohttp",
+ false,
+ true,
+ true,
+ false,
+ vec![],
+ )
+});
+
/// Instantiate custom pings once to trigger registration.
///
@@ -87,6 +104,7 @@ pub fn register_pings(application_id: Option<&str>) {
let _ = &*not_deletion_request;
let _ = &*not_events;
let _ = &*not_metrics;
+ let _ = &*not_ohttp;
}
}
}
@@ -110,6 +128,7 @@ pub(crate) fn submit_ping_by_id(id: u32, reason: Option<&str>) {
2 => not_deletion_request.submit(reason),
3 => not_events.submit(reason),
4 => not_metrics.submit(reason),
+ 5 => not_ohttp.submit(reason),
_ => {
// TODO: instrument this error.
log::error!("Cannot submit unknown ping {} by id.", id);
diff --git a/toolkit/components/glean/tests/pytest/pings_test_output_cpp b/toolkit/components/glean/tests/pytest/pings_test_output_cpp
index 289529b118..8994204634 100644
--- a/toolkit/components/glean/tests/pytest/pings_test_output_cpp
+++ b/toolkit/components/glean/tests/pytest/pings_test_output_cpp
@@ -56,6 +56,13 @@ constexpr glean::impl::Ping NotEvents(3);
*/
constexpr glean::impl::Ping NotMetrics(4);
+/*
+ * Generated from not-ohttp.
+ *
+ * A fake OHTTP-using ping
+ */
+constexpr glean::impl::Ping NotOhttp(5);
+
} // namespace mozilla::glean_pings
diff --git a/toolkit/components/glean/tests/pytest/pings_test_output_js_cpp b/toolkit/components/glean/tests/pytest/pings_test_output_js_cpp
index 139eb29148..bc4f235d1e 100644
--- a/toolkit/components/glean/tests/pytest/pings_test_output_js_cpp
+++ b/toolkit/components/glean/tests/pytest/pings_test_output_js_cpp
@@ -33,15 +33,17 @@ constexpr char gPingStringTable[] = {
/* 12 - "notDeletionRequest" */ 'n', 'o', 't', 'D', 'e', 'l', 'e', 't', 'i', 'o', 'n', 'R', 'e', 'q', 'u', 'e', 's', 't', '\0',
/* 31 - "notEvents" */ 'n', 'o', 't', 'E', 'v', 'e', 'n', 't', 's', '\0',
/* 41 - "notMetrics" */ 'n', 'o', 't', 'M', 'e', 't', 'r', 'i', 'c', 's', '\0',
+ /* 52 - "notOhttp" */ 'n', 'o', 't', 'O', 'h', 't', 't', 'p', '\0',
};
const ping_entry_t sPingByNameLookupEntries[] = {
65536,
+ 327732,
+ 262185,
131084,
- 196639,
- 262185
+ 196639
};
@@ -88,7 +90,7 @@ PingByNameLookup(const nsACString& aKey)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -108,7 +110,7 @@ PingByNameLookup(const nsACString& aKey)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/toolkit/components/glean/tests/pytest/pings_test_output_js_h b/toolkit/components/glean/tests/pytest/pings_test_output_js_h
index 0c89de93ae..af21515ccc 100644
--- a/toolkit/components/glean/tests/pytest/pings_test_output_js_h
+++ b/toolkit/components/glean/tests/pytest/pings_test_output_js_h
@@ -28,6 +28,6 @@ const char* GetPingName(ping_entry_t aEntry);
*/
Maybe<uint32_t> PingByNameLookup(const nsACString&);
-extern const ping_entry_t sPingByNameLookupEntries[4];
+extern const ping_entry_t sPingByNameLookupEntries[5];
} // namespace mozilla::glean
#endif // mozilla_GleanJSPingsLookup_h
diff --git a/toolkit/components/glean/tests/test_metrics.yaml b/toolkit/components/glean/tests/test_metrics.yaml
index bc290d470f..47587241b1 100644
--- a/toolkit/components/glean/tests/test_metrics.yaml
+++ b/toolkit/components/glean/tests/test_metrics.yaml
@@ -403,6 +403,25 @@ test_only:
- test-ping
telemetry_mirror: TELEMETRY_TEST_MIRROR_FOR_QUANTITY
+ balloons:
+ type: object
+ description: A collection of balloons
+ bugs:
+ - https://bugzilla.mozilla.org/1839640
+ data_reviews:
+ - http://example.com/reviews
+ notification_emails:
+ - CHANGE-ME@example.com
+ expires: never
+ structure:
+ type: array
+ items:
+ type: object
+ properties:
+ colour:
+ type: string
+ diameter:
+ type: number
test_only.ipc:
a_counter:
diff --git a/toolkit/components/glean/tests/test_pings.yaml b/toolkit/components/glean/tests/test_pings.yaml
index d62f682109..8bae13215e 100644
--- a/toolkit/components/glean/tests/test_pings.yaml
+++ b/toolkit/components/glean/tests/test_pings.yaml
@@ -40,3 +40,22 @@ test-ping:
- glean-team@mozilla.com
no_lint:
- REDUNDANT_PING
+
+test-ohttp-ping:
+ description: |
+ This ping is for tests only.
+ Resembles how OHTTP pings are defined.
+ include_client_id: false
+ metadata:
+ include_info_sections: false
+ use_ohttp: true
+ send_if_empty: true
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1862002
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1862002
+ notification_emails:
+ - chutten@mozilla.com
+ - glean-team@mozilla.com
+ no_lint:
+ - REDUNDANT_PING
diff --git a/toolkit/components/glean/tests/xpcshell/head.js b/toolkit/components/glean/tests/xpcshell/head.js
index f42bd02822..eaf8fa9c61 100644
--- a/toolkit/components/glean/tests/xpcshell/head.js
+++ b/toolkit/components/glean/tests/xpcshell/head.js
@@ -4,3 +4,155 @@
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
);
+
+ChromeUtils.defineESModuleGetters(this, {
+ HttpServer: "resource://testing-common/httpd.sys.mjs",
+ NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
+});
+
+const PingServer = {
+ _httpServer: null,
+ _started: false,
+ _defers: [Promise.withResolvers()],
+ _currentDeferred: 0,
+
+ get port() {
+ return this._httpServer.identity.primaryPort;
+ },
+
+ get host() {
+ return this._httpServer.identity.primaryHost;
+ },
+
+ get started() {
+ return this._started;
+ },
+
+ registerPingHandler(handler) {
+ this._httpServer.registerPrefixHandler("/submit/", handler);
+ },
+
+ resetPingHandler() {
+ this.registerPingHandler(request => {
+ let r = request;
+ console.trace(
+ `defaultPingHandler() - ${r.method} ${r.scheme}://${r.host}:${r.port}${r.path}`
+ );
+ let deferred = this._defers[this._defers.length - 1];
+ this._defers.push(Promise.withResolvers());
+ deferred.resolve(request);
+ });
+ },
+
+ start() {
+ this._httpServer = new HttpServer();
+ this._httpServer.start(-1);
+ this._started = true;
+ this.clearRequests();
+ this.resetPingHandler();
+ },
+
+ stop() {
+ return new Promise(resolve => {
+ this._httpServer.stop(resolve);
+ this._started = false;
+ });
+ },
+
+ clearRequests() {
+ this._defers = [Promise.withResolvers()];
+ this._currentDeferred = 0;
+ },
+
+ promiseNextRequest() {
+ const deferred = this._defers[this._currentDeferred++];
+ // Send the ping to the consumer on the next tick, so that the completion gets
+ // signaled to Telemetry.
+ return new Promise(r =>
+ Services.tm.dispatchToMainThread(() => r(deferred.promise))
+ );
+ },
+
+ promiseNextPing() {
+ return this.promiseNextRequest().then(request =>
+ decodeRequestPayload(request)
+ );
+ },
+
+ async promiseNextRequests(count) {
+ let results = [];
+ for (let i = 0; i < count; ++i) {
+ results.push(await this.promiseNextRequest());
+ }
+
+ return results;
+ },
+
+ promiseNextPings(count) {
+ return this.promiseNextRequests(count).then(requests => {
+ return Array.from(requests, decodeRequestPayload);
+ });
+ },
+};
+
+/**
+ * Decode the payload of an HTTP request into a ping.
+ *
+ * @param {object} request The data representing an HTTP request (nsIHttpRequest).
+ * @returns {object} The decoded ping payload.
+ */
+function decodeRequestPayload(request) {
+ let s = request.bodyInputStream;
+ let payload = null;
+
+ if (
+ request.hasHeader("content-encoding") &&
+ request.getHeader("content-encoding") == "gzip"
+ ) {
+ let observer = {
+ buffer: "",
+ onStreamComplete(loader, context, status, length, result) {
+ // String.fromCharCode can only deal with 500,000 characters
+ // at a time, so chunk the result into parts of that size.
+ const chunkSize = 500000;
+ for (let offset = 0; offset < result.length; offset += chunkSize) {
+ this.buffer += String.fromCharCode.apply(
+ String,
+ result.slice(offset, offset + chunkSize)
+ );
+ }
+ },
+ };
+
+ let scs = Cc["@mozilla.org/streamConverters;1"].getService(
+ Ci.nsIStreamConverterService
+ );
+ let listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance(
+ Ci.nsIStreamLoader
+ );
+ listener.init(observer);
+ let converter = scs.asyncConvertData(
+ "gzip",
+ "uncompressed",
+ listener,
+ null
+ );
+ converter.onStartRequest(null, null);
+ converter.onDataAvailable(null, s, 0, s.available());
+ converter.onStopRequest(null, null, null);
+ // TODO: nsIScriptableUnicodeConverter is deprecated
+ // But I can't figure out how else to ungzip bodyInputStream.
+ let unicodeConverter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ unicodeConverter.charset = "UTF-8";
+ let utf8string = unicodeConverter.ConvertToUnicode(observer.buffer);
+ utf8string += unicodeConverter.Finish();
+ payload = JSON.parse(utf8string);
+ } else {
+ let bytes = NetUtil.readInputStream(s, s.available());
+ payload = JSON.parse(new TextDecoder().decode(bytes));
+ }
+
+ return payload;
+}
diff --git a/toolkit/components/glean/tests/xpcshell/test_GIFFT.js b/toolkit/components/glean/tests/xpcshell/test_GIFFT.js
index 015b4d4e38..8fcf755d3a 100644
--- a/toolkit/components/glean/tests/xpcshell/test_GIFFT.js
+++ b/toolkit/components/glean/tests/xpcshell/test_GIFFT.js
@@ -176,10 +176,7 @@ add_task(async function test_gifft_timing_dist() {
// But we can guarantee it's only two samples.
Assert.equal(
2,
- Object.entries(data.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- ),
+ Object.entries(data.values).reduce((acc, [, count]) => acc + count, 0),
"Only two buckets with samples"
);
@@ -188,10 +185,7 @@ add_task(async function test_gifft_timing_dist() {
Assert.greaterOrEqual(data.sum, 13, "Histogram's in milliseconds");
Assert.equal(
2,
- Object.entries(data.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- ),
+ Object.entries(data.values).reduce((acc, [, count]) => acc + count, 0),
"Only two samples"
);
});
diff --git a/toolkit/components/glean/tests/xpcshell/test_GIFFTIPC.js b/toolkit/components/glean/tests/xpcshell/test_GIFFTIPC.js
index a318d57b9c..ae4c48b6e8 100644
--- a/toolkit/components/glean/tests/xpcshell/test_GIFFTIPC.js
+++ b/toolkit/components/glean/tests/xpcshell/test_GIFFTIPC.js
@@ -291,10 +291,7 @@ add_task(
// but we can assert there are only two samples.
Assert.equal(
2,
- Object.entries(times.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- )
+ Object.entries(times.values).reduce((acc, [, count]) => acc + count, 0)
);
const timingHist = histSnapshot.content.TELEMETRY_TEST_EXPONENTIAL;
Assert.greaterOrEqual(timingHist.sum, 13, "Histogram's in milliseconds.");
@@ -303,7 +300,7 @@ add_task(
Assert.equal(
2,
Object.entries(timingHist.values).reduce(
- (acc, [bucket, count]) => acc + count,
+ (acc, [, count]) => acc + count,
0
),
"Only two samples"
diff --git a/toolkit/components/glean/tests/xpcshell/test_Glean.js b/toolkit/components/glean/tests/xpcshell/test_Glean.js
index 8375c22a3e..18b450a69b 100644
--- a/toolkit/components/glean/tests/xpcshell/test_Glean.js
+++ b/toolkit/components/glean/tests/xpcshell/test_Glean.js
@@ -182,6 +182,7 @@ add_task(async function test_fog_memory_distribution_works() {
Glean.testOnly.doYouRemember.accumulate(17);
let data = Glean.testOnly.doYouRemember.testGetValue("test-ping");
+ Assert.equal(2, data.count, "Count of entries is correct");
// `data.sum` is in bytes, but the metric is in MB.
Assert.equal(24 * 1024 * 1024, data.sum, "Sum's correct");
for (let [bucket, count] of Object.entries(data.values)) {
@@ -196,6 +197,7 @@ add_task(async function test_fog_custom_distribution_works() {
Glean.testOnlyIpc.aCustomDist.accumulateSamples([7, 268435458]);
let data = Glean.testOnlyIpc.aCustomDist.testGetValue("store1");
+ Assert.equal(2, data.count, "Count of entries is correct");
Assert.equal(7 + 268435458, data.sum, "Sum's correct");
for (let [bucket, count] of Object.entries(data.values)) {
Assert.ok(
@@ -216,7 +218,7 @@ add_task(function test_fog_custom_pings() {
Assert.ok("onePingOnly" in GleanPings);
let submitted = false;
Glean.testOnly.onePingOneBool.set(false);
- GleanPings.onePingOnly.testBeforeNextSubmit(reason => {
+ GleanPings.onePingOnly.testBeforeNextSubmit(() => {
submitted = true;
Assert.equal(false, Glean.testOnly.onePingOneBool.testGetValue());
});
@@ -227,7 +229,7 @@ add_task(function test_fog_custom_pings() {
add_task(function test_recursive_testBeforeNextSubmit() {
Assert.ok("onePingOnly" in GleanPings);
let submitted = 0;
- let rec = reason => {
+ let rec = () => {
submitted++;
GleanPings.onePingOnly.testBeforeNextSubmit(rec);
};
@@ -255,6 +257,10 @@ add_task(async function test_fog_timing_distribution_works() {
Glean.testOnly.whatTimeIsIt.stopAndAccumulate(t3); // 5ms
let data = Glean.testOnly.whatTimeIsIt.testGetValue();
+
+ // Cancelled timers should not be counted.
+ Assert.equal(2, data.count, "Count of entries is correct");
+
const NANOS_IN_MILLIS = 1e6;
// bug 1701949 - Sleep gets close, but sometimes doesn't wait long enough.
const EPSILON = 40000;
@@ -266,10 +272,7 @@ add_task(async function test_fog_timing_distribution_works() {
// But we can guarantee it's only two samples.
Assert.equal(
2,
- Object.entries(data.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- ),
+ Object.entries(data.values).reduce((acc, [, count]) => acc + count, 0),
"Only two buckets with samples"
);
});
@@ -456,3 +459,106 @@ add_task(async function test_fog_text_works_unusual_character() {
Assert.greater(rslt.length, 100);
});
+
+add_task(async function test_fog_object_works() {
+ if (!Glean.testOnly.balloons) {
+ // FIXME(bug 1883857): object metric type not available, e.g. in artifact builds.
+ // Skipping this test.
+ return;
+ }
+
+ Assert.equal(
+ undefined,
+ Glean.testOnly.balloons.testGetValue(),
+ "No object stored"
+ );
+
+ // Can't store not-objects.
+ let invalidValues = [1, "str", false, undefined, null, NaN, Infinity];
+ for (let value of invalidValues) {
+ Assert.throws(
+ () => Glean.testOnly.balloons.set(value),
+ /is not an object/,
+ "Should throw a type error"
+ );
+ }
+
+ // No invalid value will be stored.
+ Assert.equal(
+ undefined,
+ Glean.testOnly.balloons.testGetValue(),
+ "No object stored"
+ );
+
+ // `JS_Stringify` internally throws
+ // an `TypeError: cyclic object value` exception.
+ // That's cleared and `set` should not throw on it.
+ // This eventually should log a proper error in Glean.
+ let selfref = {};
+ selfref.a = selfref;
+ Glean.testOnly.balloons.set(selfref);
+ Assert.equal(
+ undefined,
+ Glean.testOnly.balloons.testGetValue(),
+ "No object stored"
+ );
+
+ let balloons = [
+ { colour: "red", diameter: 5 },
+ { colour: "blue", diameter: 7 },
+ { colour: "orange" },
+ ];
+ Glean.testOnly.balloons.set(balloons);
+
+ let result = Glean.testOnly.balloons.testGetValue();
+ let expected = [
+ { colour: "red", diameter: 5 },
+ { colour: "blue", diameter: 7 },
+ { colour: "orange", diameter: null },
+ ];
+ Assert.deepEqual(expected, result);
+
+ // These values are coerced to null or removed.
+ balloons = [
+ { colour: "inf", diameter: Infinity },
+ { colour: "negative-inf", diameter: -1 / 0 },
+ { colour: "nan", diameter: NaN },
+ { colour: "undef", diameter: undefined },
+ ];
+ Glean.testOnly.balloons.set(balloons);
+ result = Glean.testOnly.balloons.testGetValue();
+ expected = [
+ { colour: "inf", diameter: null },
+ { colour: "negative-inf", diameter: null },
+ { colour: "nan", diameter: null },
+ { colour: "undef", diameter: null },
+ ];
+ Assert.deepEqual(expected, result);
+
+ // colour != color.
+ let invalid = [{ color: "orange" }, { color: "red", diameter: "small" }];
+ Glean.testOnly.balloons.set(invalid);
+ Assert.throws(
+ () => Glean.testOnly.balloons.testGetValue(),
+ /invalid_value/,
+ "Should throw because last object was invalid."
+ );
+
+ Services.fog.testResetFOG();
+ // set again to ensure it's stored
+ balloons = [
+ { colour: "red", diameter: 5 },
+ { colour: "blue", diameter: 7 },
+ ];
+ Glean.testOnly.balloons.set(balloons);
+ result = Glean.testOnly.balloons.testGetValue();
+ Assert.deepEqual(balloons, result);
+
+ invalid = [{ colour: "red", diameter: 5, extra: "field" }];
+ Glean.testOnly.balloons.set(invalid);
+ Assert.throws(
+ () => Glean.testOnly.balloons.testGetValue(),
+ /invalid_value/,
+ "Should throw because last object was invalid."
+ );
+});
diff --git a/toolkit/components/glean/tests/xpcshell/test_GleanIPC.js b/toolkit/components/glean/tests/xpcshell/test_GleanIPC.js
index 3d665c23b9..2db1c53218 100644
--- a/toolkit/components/glean/tests/xpcshell/test_GleanIPC.js
+++ b/toolkit/components/glean/tests/xpcshell/test_GleanIPC.js
@@ -127,10 +127,7 @@ add_task(
// but we can assert there are only two samples.
Assert.equal(
2,
- Object.entries(times.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- )
+ Object.entries(times.values).reduce((acc, [, count]) => acc + count, 0)
);
const mabelsCounters = Glean.testOnly.mabelsKitchenCounters;
diff --git a/toolkit/components/glean/tests/xpcshell/test_JOG.js b/toolkit/components/glean/tests/xpcshell/test_JOG.js
index 53b1d25962..00f3ded135 100644
--- a/toolkit/components/glean/tests/xpcshell/test_JOG.js
+++ b/toolkit/components/glean/tests/xpcshell/test_JOG.js
@@ -289,11 +289,11 @@ add_task(async function test_jog_custom_pings() {
`"ping"`,
false
);
- Services.fog.testRegisterRuntimePing("jog-ping", true, true, true, []);
+ Services.fog.testRegisterRuntimePing("jog-ping", true, true, true, true, []);
Assert.ok("jogPing" in GleanPings);
let submitted = false;
Glean.jogCat.jogPingBool.set(false);
- GleanPings.jogPing.testBeforeNextSubmit(reason => {
+ GleanPings.jogPing.testBeforeNextSubmit(() => {
submitted = true;
Assert.equal(false, Glean.jogCat.jogPingBool.testGetValue());
});
@@ -338,10 +338,7 @@ add_task(async function test_jog_timing_distribution_works() {
// But we can guarantee it's only two samples.
Assert.equal(
2,
- Object.entries(data.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- ),
+ Object.entries(data.values).reduce((acc, [, count]) => acc + count, 0),
"Only two buckets with samples"
);
});
@@ -642,7 +639,9 @@ add_task(function test_jog_dotted_categories_work() {
add_task(async function test_jog_ping_works() {
const kReason = "reason-1";
- Services.fog.testRegisterRuntimePing("my-ping", true, true, true, [kReason]);
+ Services.fog.testRegisterRuntimePing("my-ping", true, true, true, true, [
+ kReason,
+ ]);
let submitted = false;
GleanPings.myPing.testBeforeNextSubmit(reason => {
submitted = true;
@@ -652,6 +651,20 @@ add_task(async function test_jog_ping_works() {
Assert.ok(submitted, "Ping must have been submitted");
});
+add_task(async function test_jog_noinfo_ping_works() {
+ const kReason = "reason-1";
+ Services.fog.testRegisterRuntimePing("noinfo-ping", true, true, true, false, [
+ kReason,
+ ]);
+ let submitted = false;
+ GleanPings.noinfoPing.testBeforeNextSubmit(reason => {
+ submitted = true;
+ Assert.equal(kReason, reason);
+ });
+ GleanPings.noinfoPing.submit("reason-1");
+ Assert.ok(submitted, "Ping must have been submitted");
+});
+
add_task(function test_jog_name_collision() {
Assert.ok("aCounter" in Glean.testOnlyJog);
Assert.equal(undefined, Glean.testOnlyJog.aCounter.testGetValue());
diff --git a/toolkit/components/glean/tests/xpcshell/test_JOGIPC.js b/toolkit/components/glean/tests/xpcshell/test_JOGIPC.js
index 505dba6825..b43a448c53 100644
--- a/toolkit/components/glean/tests/xpcshell/test_JOGIPC.js
+++ b/toolkit/components/glean/tests/xpcshell/test_JOGIPC.js
@@ -226,10 +226,7 @@ add_task(
// but we can assert there are only two samples.
Assert.equal(
2,
- Object.entries(times.values).reduce(
- (acc, [bucket, count]) => acc + count,
- 0
- )
+ Object.entries(times.values).reduce((acc, [, count]) => acc + count, 0)
);
const labeledCounter = Glean.jogIpc.jogLabeledCounter;
diff --git a/toolkit/components/glean/tests/xpcshell/test_OHTTP.js b/toolkit/components/glean/tests/xpcshell/test_OHTTP.js
new file mode 100644
index 0000000000..76d1d2a67b
--- /dev/null
+++ b/toolkit/components/glean/tests/xpcshell/test_OHTTP.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_setup(async () => {
+ // FOG needs a profile dir to put its data in.
+ do_get_profile();
+
+ PingServer.start();
+
+ registerCleanupFunction(async () => {
+ await PingServer.stop();
+ });
+
+ Services.prefs.setIntPref(
+ "telemetry.fog.test.localhost_port",
+ PingServer.port
+ );
+ // Port pref needs to be set before init, so let's reset to reinit.
+ Services.fog.testResetFOG();
+});
+
+add_task(async () => {
+ PingServer.clearRequests();
+ GleanPings.testOhttpPing.submit();
+
+ let ping = await PingServer.promiseNextPing();
+
+ ok(!("client_info" in ping), "No client_info allowed.");
+ ok(!("ping_info" in ping), "No ping_info allowed.");
+});
diff --git a/toolkit/components/glean/tests/xpcshell/xpcshell.toml b/toolkit/components/glean/tests/xpcshell/xpcshell.toml
index 40b1a22bf4..22806a32e6 100644
--- a/toolkit/components/glean/tests/xpcshell/xpcshell.toml
+++ b/toolkit/components/glean/tests/xpcshell/xpcshell.toml
@@ -30,3 +30,6 @@ skip-if = ["os == 'android'"] # Server Knobs on mobile will be handled by the sp
["test_MillionQ.js"]
skip-if = ["os == 'android'"] # Android inits its own FOG, so the test won't work.
+
+["test_OHTTP.js"]
+skip-if = ["os == 'android'"] # FOG isn't responsible for monitoring prefs and controlling upload on Android
diff --git a/toolkit/components/glean/xpcom/FOG.cpp b/toolkit/components/glean/xpcom/FOG.cpp
index 3673b23707..d4c03a5b9e 100644
--- a/toolkit/components/glean/xpcom/FOG.cpp
+++ b/toolkit/components/glean/xpcom/FOG.cpp
@@ -419,12 +419,13 @@ FOG::TestRegisterRuntimePing(const nsACString& aName,
const bool aIncludeClientId,
const bool aSendIfEmpty,
const bool aPreciseTimestamps,
+ const bool aIncludeInfoSections,
const nsTArray<nsCString>& aReasonCodes,
uint32_t* aPingIdOut) {
*aPingIdOut = 0;
- *aPingIdOut =
- glean::jog::jog_test_register_ping(&aName, aIncludeClientId, aSendIfEmpty,
- aPreciseTimestamps, &aReasonCodes);
+ *aPingIdOut = glean::jog::jog_test_register_ping(
+ &aName, aIncludeClientId, aSendIfEmpty, aPreciseTimestamps,
+ aIncludeInfoSections, &aReasonCodes);
return NS_OK;
}
diff --git a/toolkit/components/glean/xpcom/nsIFOG.idl b/toolkit/components/glean/xpcom/nsIFOG.idl
index 682df85f1a..dd5d0ec21a 100644
--- a/toolkit/components/glean/xpcom/nsIFOG.idl
+++ b/toolkit/components/glean/xpcom/nsIFOG.idl
@@ -180,11 +180,14 @@ interface nsIFOG : nsISupports
* @param aName - The ping's name.
* @param aIncludeClientId - Whether the ping should include the client_id.
* @param aSendIfEmpty - Whether the ping should send even if empty.
+ * @param aIncludeInfoSections - Whether the ping should include
+ * {client|ping}_info sections.
* @param aReasonCodes - The list of valid reasons for ping submission.
*/
uint32_t testRegisterRuntimePing(in ACString aName,
in boolean aIncludeClientId,
in boolean aSendIfEmpty,
in boolean aPreciseTimestamps,
+ in boolean aIncludeInfoSections,
in Array<ACString> aReasonCodes);
};