diff options
Diffstat (limited to 'toolkit/components/glean/pytest')
14 files changed, 1944 insertions, 0 deletions
diff --git a/toolkit/components/glean/pytest/metrics_expires_number_test.yaml b/toolkit/components/glean/pytest/metrics_expires_number_test.yaml new file mode 100644 index 0000000000..701bc00443 --- /dev/null +++ b/toolkit/components/glean/pytest/metrics_expires_number_test.yaml @@ -0,0 +1,22 @@ +# 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/. + +# This file is FOR TESTING PURPOSES ONLY. + +--- +$schema: moz://mozilla.org/schemas/glean/metrics/1-0-0 + +test: + expires_number: + type: boolean + expires: 99 + description: | + A metric with an expires of an invalid type (number) + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1664306 + data_reviews: + - https://example.com diff --git a/toolkit/components/glean/pytest/metrics_expires_versions_test.yaml b/toolkit/components/glean/pytest/metrics_expires_versions_test.yaml new file mode 100644 index 0000000000..7520f749e5 --- /dev/null +++ b/toolkit/components/glean/pytest/metrics_expires_versions_test.yaml @@ -0,0 +1,84 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file is FOR TESTING PURPOSES ONLY. + +--- +$schema: moz://mozilla.org/schemas/glean/metrics/1-0-0 + +test: + expired1: + type: boolean + expires: "41" + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1664306 + data_reviews: + - https://example.com + no_lint: + - EXPIRED + + expired2: + type: labeled_boolean + expires: "42" + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1664306 + data_reviews: + - https://example.com + no_lint: + - EXPIRED + + unexpired: + type: labeled_boolean + expires: "100" + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1664306 + data_reviews: + - https://example.com + labels: + - one_label + - two_labels + + never: + type: string + expires: never + description: A never-expiring metric + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1664306 + data_reviews: + - https://example.com + + always: + type: string + expires: expired + description: An already-expired metric + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1664306 + data_reviews: + - https://example.com + no_lint: + - EXPIRED diff --git a/toolkit/components/glean/pytest/metrics_test.yaml b/toolkit/components/glean/pytest/metrics_test.yaml new file mode 100644 index 0000000000..ead9217466 --- /dev/null +++ b/toolkit/components/glean/pytest/metrics_test.yaml @@ -0,0 +1,270 @@ +# 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/. + +# This file defines the metrics that are recorded by the Glean SDK. They are +# 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 metrics here until probably about January of 2021. +# If you're looking for the metrics.yaml for Geckoveiw Streaming Telemetry, +# you can find that one in toolkit/components/telemetry/geckoview/streaming. + +--- +$schema: moz://mozilla.org/schemas/glean/metrics/1-0-0 + +test: + boolean_metric: + type: boolean + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + labeled_boolean_metric: + type: labeled_boolean + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + labeled_boolean_metric_labels: + type: labeled_boolean + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + labels: + - one_label + - two_labels + + counter_metric: + type: counter + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + labeled_counter_metric: + type: labeled_counter + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + labeled_counter_metric_labels: + type: labeled_counter + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + labels: + - one_label + - two_labels + + string_metric: + type: string + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + labeled_string_metric: + type: labeled_string + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + labeled_string_metric_labels: + type: labeled_string + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + labels: + - one_label + - two_labels + + string_list_metric: + type: string_list + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + timespan_metric: + type: timespan + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + timing_distribution_metric: + type: timing_distribution + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + memory_distribution_metric: + type: memory_distribution + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + memory_unit: kilobyte + +test.nested: + uuid_metric: + type: uuid + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + datetime_metric: + type: datetime + expires: never + description: | + A multi-line + description + lifetime: application + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + event_metric: + type: event + expires: never + description: | + A multi-line + description + lifetime: ping + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + + event_metric_with_extra: + type: event + expires: never + description: | + A multi-line + description + lifetime: ping + notification_emails: + - glean-team@mozilla.com + bugs: + - https://bugzilla.mozilla.org/1635260/ + data_reviews: + - https://example.com + extra_keys: + an_extra_key: + description: An extra key description + another_extra_key: + description: Another extra key description diff --git a/toolkit/components/glean/pytest/metrics_test_output b/toolkit/components/glean/pytest/metrics_test_output new file mode 100644 index 0000000000..0ac9f7b9ba --- /dev/null +++ b/toolkit/components/glean/pytest/metrics_test_output @@ -0,0 +1,542 @@ +// -*- mode: Rust -*- + +// AUTOGENERATED BY glean_parser. DO NOT EDIT. + +/* 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 mod test { + use crate::private::*; + use glean_core::CommonMetricData; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + /// generated from test.boolean_metric + /// + /// A multi-line + /// description + pub static boolean_metric: Lazy<BooleanMetric> = Lazy::new(|| { + BooleanMetric::new(1.into(), CommonMetricData { + name: "boolean_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.labeled_boolean_metric + /// + /// A multi-line + /// description + pub static labeled_boolean_metric: Lazy<LabeledMetric<BooleanMetric>> = Lazy::new(|| { + LabeledMetric::new(2.into(), CommonMetricData { + name: "labeled_boolean_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, None) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.labeled_boolean_metric_labels + /// + /// A multi-line + /// description + pub static labeled_boolean_metric_labels: Lazy<LabeledMetric<BooleanMetric>> = Lazy::new(|| { + LabeledMetric::new(3.into(), CommonMetricData { + name: "labeled_boolean_metric_labels".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, Some(vec!["one_label".into(), "two_labels".into()])) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.counter_metric + /// + /// A multi-line + /// description + pub static counter_metric: Lazy<CounterMetric> = Lazy::new(|| { + CounterMetric::new(4.into(), CommonMetricData { + name: "counter_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.labeled_counter_metric + /// + /// A multi-line + /// description + pub static labeled_counter_metric: Lazy<LabeledMetric<CounterMetric>> = Lazy::new(|| { + LabeledMetric::new(5.into(), CommonMetricData { + name: "labeled_counter_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, None) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.labeled_counter_metric_labels + /// + /// A multi-line + /// description + pub static labeled_counter_metric_labels: Lazy<LabeledMetric<CounterMetric>> = Lazy::new(|| { + LabeledMetric::new(6.into(), CommonMetricData { + name: "labeled_counter_metric_labels".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, Some(vec!["one_label".into(), "two_labels".into()])) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.string_metric + /// + /// A multi-line + /// description + pub static string_metric: Lazy<StringMetric> = Lazy::new(|| { + StringMetric::new(7.into(), CommonMetricData { + name: "string_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.labeled_string_metric + /// + /// A multi-line + /// description + pub static labeled_string_metric: Lazy<LabeledMetric<StringMetric>> = Lazy::new(|| { + LabeledMetric::new(8.into(), CommonMetricData { + name: "labeled_string_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, None) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.labeled_string_metric_labels + /// + /// A multi-line + /// description + pub static labeled_string_metric_labels: Lazy<LabeledMetric<StringMetric>> = Lazy::new(|| { + LabeledMetric::new(9.into(), CommonMetricData { + name: "labeled_string_metric_labels".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, Some(vec!["one_label".into(), "two_labels".into()])) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.string_list_metric + /// + /// A multi-line + /// description + pub static string_list_metric: Lazy<StringListMetric> = Lazy::new(|| { + StringListMetric::new(10.into(), CommonMetricData { + name: "string_list_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.timespan_metric + /// + /// A multi-line + /// description + pub static timespan_metric: Lazy<TimespanMetric> = Lazy::new(|| { + TimespanMetric::new(11.into(), CommonMetricData { + name: "timespan_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, TimeUnit::Millisecond) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.timing_distribution_metric + /// + /// A multi-line + /// description + pub static timing_distribution_metric: Lazy<TimingDistributionMetric> = Lazy::new(|| { + TimingDistributionMetric::new(12.into(), CommonMetricData { + name: "timing_distribution_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, TimeUnit::Nanosecond) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.memory_distribution_metric + /// + /// A multi-line + /// description + pub static memory_distribution_metric: Lazy<MemoryDistributionMetric> = Lazy::new(|| { + MemoryDistributionMetric::new(13.into(), CommonMetricData { + name: "memory_distribution_metric".into(), + category: "test".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, MemoryUnit::Kilobyte) + }); + +} +pub mod test_nested { + use crate::private::*; + use glean_core::CommonMetricData; + use once_cell::sync::Lazy; + + #[allow(non_upper_case_globals)] + /// generated from test.nested.uuid_metric + /// + /// A multi-line + /// description + pub static uuid_metric: Lazy<UuidMetric> = Lazy::new(|| { + UuidMetric::new(14.into(), CommonMetricData { + name: "uuid_metric".into(), + category: "test.nested".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + 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(15.into(), CommonMetricData { + name: "datetime_metric".into(), + category: "test.nested".into(), + send_in_pings: vec!["metrics".into()], + lifetime: Lifetime::Application, + disabled: false, + ..Default::default() + }, TimeUnit::Millisecond) + }); + + #[allow(non_upper_case_globals)] + /// generated from test.nested.event_metric + /// + /// A multi-line + /// description + pub static event_metric: Lazy<EventMetric<NoExtraKeys>> = Lazy::new(|| { + EventMetric::new(16.into(), CommonMetricData { + name: "event_metric".into(), + category: "test.nested".into(), + send_in_pings: vec!["events".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }) + }); + + #[derive(Clone, Copy, Hash, Eq, PartialEq)] + pub enum EventMetricWithExtraKeys { + AnExtraKey, + AnotherExtraKey, + } + + impl ExtraKeys for EventMetricWithExtraKeys { + const ALLOWED_KEYS: &'static [&'static str] = &["an_extra_key", "another_extra_key"]; + + fn index(self) -> i32 { + self as i32 + } + } + + /// Convert from an extra key's index to its variant. + impl std::convert::TryFrom<i32> for EventMetricWithExtraKeys { + type Error = EventRecordingError; + + fn try_from(value: i32) -> Result<Self, Self::Error> { + match value { + 0 => Ok(Self::AnExtraKey), + 1 => Ok(Self::AnotherExtraKey), + _ => Err(EventRecordingError::InvalidExtraKey), + } + } + } + + /// Convert from an extra key's string representation to its variant. + impl std::convert::TryFrom<&str> for EventMetricWithExtraKeys { + type Error = EventRecordingError; + + fn try_from(value: &str) -> Result<Self, Self::Error> { + match value { + "an_extra_key" => Ok(Self::AnExtraKey), + "another_extra_key" => Ok(Self::AnotherExtraKey), + _ => Err(EventRecordingError::InvalidExtraKey), + } + } + } + + #[allow(non_upper_case_globals)] + /// generated from test.nested.event_metric_with_extra + /// + /// A multi-line + /// description + pub static event_metric_with_extra: Lazy<EventMetric<EventMetricWithExtraKeys>> = Lazy::new(|| { + EventMetric::new(17.into(), CommonMetricData { + name: "event_metric_with_extra".into(), + category: "test.nested".into(), + send_in_pings: vec!["events".into()], + lifetime: Lifetime::Ping, + disabled: false, + ..Default::default() + }) + }); + +} + +#[allow(dead_code)] +pub(crate) mod __glean_metric_maps { + use std::collections::HashMap; + use std::convert::TryInto; + + use crate::private::*; + use once_cell::sync::Lazy; + + pub static BOOLEAN_MAP: Lazy<HashMap<MetricId, &Lazy<BooleanMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(1.into(), &super::test::boolean_metric); + map + }); + + pub static LABELED_BOOLEAN_MAP: Lazy<HashMap<MetricId, &Lazy<LabeledMetric<BooleanMetric>>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(2); + map.insert(2.into(), &super::test::labeled_boolean_metric); + map.insert(3.into(), &super::test::labeled_boolean_metric_labels); + map + }); + + pub static COUNTER_MAP: Lazy<HashMap<MetricId, &Lazy<CounterMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(4.into(), &super::test::counter_metric); + map + }); + + pub static LABELED_COUNTER_MAP: Lazy<HashMap<MetricId, &Lazy<LabeledMetric<CounterMetric>>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(2); + map.insert(5.into(), &super::test::labeled_counter_metric); + map.insert(6.into(), &super::test::labeled_counter_metric_labels); + map + }); + + pub static STRING_MAP: Lazy<HashMap<MetricId, &Lazy<StringMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(7.into(), &super::test::string_metric); + map + }); + + pub static LABELED_STRING_MAP: Lazy<HashMap<MetricId, &Lazy<LabeledMetric<StringMetric>>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(2); + map.insert(8.into(), &super::test::labeled_string_metric); + map.insert(9.into(), &super::test::labeled_string_metric_labels); + map + }); + + pub static STRING_LIST_MAP: Lazy<HashMap<MetricId, &Lazy<StringListMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(10.into(), &super::test::string_list_metric); + map + }); + + pub static TIMESPAN_MAP: Lazy<HashMap<MetricId, &Lazy<TimespanMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(11.into(), &super::test::timespan_metric); + map + }); + + pub static TIMING_DISTRIBUTION_MAP: Lazy<HashMap<MetricId, &Lazy<TimingDistributionMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(12.into(), &super::test::timing_distribution_metric); + map + }); + + pub static MEMORY_DISTRIBUTION_MAP: Lazy<HashMap<MetricId, &Lazy<MemoryDistributionMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(13.into(), &super::test::memory_distribution_metric); + map + }); + + pub static UUID_MAP: Lazy<HashMap<MetricId, &Lazy<UuidMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(14.into(), &super::test_nested::uuid_metric); + map + }); + + pub static DATETIME_MAP: Lazy<HashMap<MetricId, &Lazy<DatetimeMetric>>> = Lazy::new(|| { + let mut map = HashMap::with_capacity(1); + map.insert(15.into(), &super::test_nested::datetime_metric); + map + }); + + + /// Helper to get the number of allowed extra keys for a given event metric. + fn extra_keys_len<K: ExtraKeys>(_event: &EventMetric<K>) -> usize { + K::ALLOWED_KEYS.len() + } + + /// Wrapper to record an event based on its metric ID. + /// + /// # Arguments + /// + /// * `metric_id` - The metric's ID to look up + /// * `extra` - An (optional) map of (extra key id, string) pairs. + /// The map will be decoded into the appropriate `ExtraKeys` type. + /// # Returns + /// + /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`, + /// or an `EventRecordingError::InvalidId` if no event by that ID exists + /// or an `EventRecordingError::InvalidExtraKey` the `extra` map could not be deserialized. + pub(crate) fn event_record_wrapper(metric_id: u32, extra: HashMap<i32, String>) -> Result<(), EventRecordingError> { + match metric_id { + 16 => { + assert!( + extra_keys_len(&super::test_nested::event_metric) != 0 || extra.is_empty(), + "No extra keys allowed, but some were passed" + ); + + // In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains. + #[allow(unused_variables)] + let extra: HashMap<_, _> = extra + .into_iter() + .map(|(k, v)| k.try_into().map(|k| (k, v))) + .collect::<Result<HashMap<_, _>, _>>()?; + super::test_nested::event_metric.record(Some(extra)); + Ok(()) + } + 17 => { + assert!( + extra_keys_len(&super::test_nested::event_metric_with_extra) != 0 || extra.is_empty(), + "No extra keys allowed, but some were passed" + ); + + // In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains. + #[allow(unused_variables)] + let extra: HashMap<_, _> = extra + .into_iter() + .map(|(k, v)| k.try_into().map(|k| (k, v))) + .collect::<Result<HashMap<_, _>, _>>()?; + super::test_nested::event_metric_with_extra.record(Some(extra)); + Ok(()) + } + _ => Err(EventRecordingError::InvalidId), + } + } + + /// Wrapper to record an event based on its metric ID. + /// + /// # Arguments + /// + /// * `metric_id` - The metric's ID to look up + /// * `extra` - An (optional) map of (string, string) pairs. + /// The map will be decoded into the appropriate `ExtraKeys` types. + /// # Returns + /// + /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`, + /// or an `EventRecordingError::InvalidId` if no event by that ID exists + /// or an `EventRecordingError::InvalidExtraKey` the `extra` map could not be deserialized. + pub(crate) fn event_record_wrapper_str(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> { + match metric_id { + 16 => { + assert!( + extra_keys_len(&super::test_nested::event_metric) != 0 || extra.is_empty(), + "No extra keys allowed, but some were passed" + ); + + // In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains. + #[allow(unused_variables)] + let extra = extra + .into_iter() + .map(|(k, v)| (&*k).try_into().map(|k| (k, v))) + .collect::<Result<HashMap<_, _>, _>>()?; + super::test_nested::event_metric.record(Some(extra)); + Ok(()) + } + 17 => { + assert!( + extra_keys_len(&super::test_nested::event_metric_with_extra) != 0 || extra.is_empty(), + "No extra keys allowed, but some were passed" + ); + + // In case of `NoExtraKeys` the whole iterator is impossible, so rustc complains. + #[allow(unused_variables)] + let extra = extra + .into_iter() + .map(|(k, v)| (&*k).try_into().map(|k| (k, v))) + .collect::<Result<HashMap<_, _>, _>>()?; + super::test_nested::event_metric_with_extra.record(Some(extra)); + Ok(()) + } + _ => Err(EventRecordingError::InvalidId), + } + } + + /// Wrapper to get the currently stored events for event metric. + /// + /// # Arguments + /// + /// * `metric_id` - The metric's ID to look up + /// * `storage_name` - the storage name to look into. + /// + /// # Returns + /// + /// Returns the recorded events or `None` if nothing stored. + /// + /// # Panics + /// + /// Panics if no event by the given metric ID could be found. + pub(crate) fn event_test_get_value_wrapper(metric_id: u32, storage_name: &str) -> Option<Vec<RecordedEvent>> { + match metric_id { + 16 => super::test_nested::event_metric.test_get_value(storage_name), + 17 => super::test_nested::event_metric_with_extra.test_get_value(storage_name), + _ => panic!("No event for metric id {}", metric_id), + } + } +} + diff --git a/toolkit/components/glean/pytest/metrics_test_output_cpp b/toolkit/components/glean/pytest/metrics_test_output_cpp new file mode 100644 index 0000000000..73cfc76bc3 --- /dev/null +++ b/toolkit/components/glean/pytest/metrics_test_output_cpp @@ -0,0 +1,130 @@ +// -*- mode: C++ -*- + +// AUTOGENERATED BY glean_parser. DO NOT EDIT. + +/* 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_Metrics_h +#define mozilla_Metrics_h + +#include "mozilla/glean/bindings/MetricTypes.h" + +namespace mozilla::glean { + +enum class NoExtraKeys {}; + + +namespace test { + /** + * generated from test.boolean_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::BooleanMetric boolean_metric(1); + + /** + * generated from test.counter_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::CounterMetric counter_metric(4); + + /** + * generated from test.string_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::StringMetric string_metric(7); + + /** + * generated from test.string_list_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::StringListMetric string_list_metric(10); + + /** + * generated from test.timespan_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::TimespanMetric timespan_metric(11); + + /** + * generated from test.timing_distribution_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::TimingDistributionMetric timing_distribution_metric(12); + + /** + * generated from test.memory_distribution_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::MemoryDistributionMetric memory_distribution_metric(13); + +} +namespace test_nested { + /** + * generated from test.nested.uuid_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::UuidMetric uuid_metric(14); + + /** + * generated from test.nested.datetime_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::DatetimeMetric datetime_metric(15); + + /** + * generated from test.nested.event_metric + */ + /** + * A multi-line + * description + */ + constexpr impl::EventMetric<uint32_t> event_metric(16); + + /** + * generated from test.nested.event_metric_with_extra + */ + enum class EventMetricWithExtraKeys : int32_t { + AnExtraKey, + AnotherExtraKey, + }; + + /** + * A multi-line + * description + */ + constexpr impl::EventMetric<EventMetricWithExtraKeys> event_metric_with_extra(17); + +} + +} // namespace mozilla::glean + +#endif // mozilla_Metrics_h diff --git a/toolkit/components/glean/pytest/metrics_test_output_js b/toolkit/components/glean/pytest/metrics_test_output_js new file mode 100644 index 0000000000..b9c351baa0 --- /dev/null +++ b/toolkit/components/glean/pytest/metrics_test_output_js @@ -0,0 +1,255 @@ +// -*- mode: C++ -*- + +// AUTOGENERATED BY glean_parser. DO NOT EDIT. + +/* 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_GleanJSMetricsLookup_h +#define mozilla_GleanJSMetricsLookup_h + +#include "mozilla/PerfectHash.h" +#include "mozilla/Maybe.h" +#include "mozilla/glean/bindings/MetricTypes.h" + +#define GLEAN_INDEX_BITS (32) +#define GLEAN_ID_BITS (27) +#define GLEAN_TYPE_ID(id) ((id) >> GLEAN_ID_BITS) +#define GLEAN_METRIC_ID(id) ((id) & ((1ULL << GLEAN_ID_BITS) - 1)) +#define GLEAN_OFFSET(entry) (entry & ((1ULL << GLEAN_INDEX_BITS) - 1)) + +namespace mozilla::glean { + +// The category lookup table's entry type +using category_entry_t = uint32_t; +// The metric lookup table's entry type +using metric_entry_t = uint64_t; + +static_assert(GLEAN_INDEX_BITS + GLEAN_ID_BITS < sizeof(metric_entry_t) * 8, "Index and ID bits need to fit into an category_entry_t"); +static_assert(GLEAN_ID_BITS < sizeof(uint32_t) * 8, "Metric IDs need to fit into less than 32 bit"); +static_assert(2 < UINT32_MAX, "Too many metric categories generated."); +static_assert(17 < 134217728, "Too many metrics generated."); +static_assert(13 < 32, "Too many different metric types."); + +static already_AddRefed<nsISupports> NewMetricFromId(uint32_t id) { + uint32_t typeId = GLEAN_TYPE_ID(id); + uint32_t metricId = GLEAN_METRIC_ID(id); + + switch (typeId) { + case 1: /* Boolean */ + { + return MakeAndAddRef<GleanBoolean>(metricId); + } + case 3: /* Counter */ + { + return MakeAndAddRef<GleanCounter>(metricId); + } + case 5: /* String */ + { + return MakeAndAddRef<GleanString>(metricId); + } + case 7: /* StringList */ + { + return MakeAndAddRef<GleanStringList>(metricId); + } + case 8: /* Timespan */ + { + return MakeAndAddRef<GleanTimespan>(metricId); + } + case 9: /* TimingDistribution */ + { + return MakeAndAddRef<GleanTimingDistribution>(metricId); + } + case 10: /* MemoryDistribution */ + { + return MakeAndAddRef<GleanMemoryDistribution>(metricId); + } + case 11: /* Uuid */ + { + return MakeAndAddRef<GleanUuid>(metricId); + } + case 12: /* Datetime */ + { + return MakeAndAddRef<GleanDatetime>(metricId); + } + case 13: /* Event */ + { + return MakeAndAddRef<GleanEvent>(metricId); + } + default: + MOZ_ASSERT_UNREACHABLE("Invalid type ID reached when trying to instantiate a new metric"); + return nullptr; + } +} + +static Maybe<uint32_t> category_result_check(const nsACString& aKey, category_entry_t entry); +static Maybe<uint32_t> metric_result_check(const nsACString& aKey, metric_entry_t entry); + +#if defined(_MSC_VER) && !defined(__clang__) +const char gCategoryStringTable[] = { +#else +constexpr char gCategoryStringTable[] = { +#endif + /* 0 - "test" */ 't', 'e', 's', 't', '\0', + /* 5 - "testNested" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '\0', +}; + + +static_assert(sizeof(gCategoryStringTable) < UINT32_MAX, "Category string table is too large."); + +const category_entry_t sCategoryByNameLookupEntries[] = { + 5, + 0 +}; + + + +static Maybe<uint32_t> +CategoryByNameLookup(const nsACString& aKey) +{ + static const uint8_t BASES[] = { + 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, 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, + }; + + + const char* bytes = aKey.BeginReading(); + size_t length = aKey.Length(); + auto& entry = mozilla::perfecthash::Lookup(bytes, length, BASES, + sCategoryByNameLookupEntries); + return category_result_check(aKey, entry); +} + + +#if defined(_MSC_VER) && !defined(__clang__) +const char gMetricStringTable[] = { +#else +constexpr char gMetricStringTable[] = { +#endif + /* 0 - "test.booleanMetric" */ 't', 'e', 's', 't', '.', 'b', 'o', 'o', 'l', 'e', 'a', 'n', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 19 - "test.labeledBooleanMetric" */ 't', 'e', 's', 't', '.', 'l', 'a', 'b', 'e', 'l', 'e', 'd', 'B', 'o', 'o', 'l', 'e', 'a', 'n', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 45 - "test.labeledBooleanMetricLabels" */ 't', 'e', 's', 't', '.', 'l', 'a', 'b', 'e', 'l', 'e', 'd', 'B', 'o', 'o', 'l', 'e', 'a', 'n', 'M', 'e', 't', 'r', 'i', 'c', 'L', 'a', 'b', 'e', 'l', 's', '\0', + /* 77 - "test.counterMetric" */ 't', 'e', 's', 't', '.', 'c', 'o', 'u', 'n', 't', 'e', 'r', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 96 - "test.labeledCounterMetric" */ 't', 'e', 's', 't', '.', 'l', 'a', 'b', 'e', 'l', 'e', 'd', 'C', 'o', 'u', 'n', 't', 'e', 'r', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 122 - "test.labeledCounterMetricLabels" */ 't', 'e', 's', 't', '.', 'l', 'a', 'b', 'e', 'l', 'e', 'd', 'C', 'o', 'u', 'n', 't', 'e', 'r', 'M', 'e', 't', 'r', 'i', 'c', 'L', 'a', 'b', 'e', 'l', 's', '\0', + /* 154 - "test.stringMetric" */ 't', 'e', 's', 't', '.', 's', 't', 'r', 'i', 'n', 'g', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 172 - "test.labeledStringMetric" */ 't', 'e', 's', 't', '.', 'l', 'a', 'b', 'e', 'l', 'e', 'd', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 197 - "test.labeledStringMetricLabels" */ 't', 'e', 's', 't', '.', 'l', 'a', 'b', 'e', 'l', 'e', 'd', 'S', 't', 'r', 'i', 'n', 'g', 'M', 'e', 't', 'r', 'i', 'c', 'L', 'a', 'b', 'e', 'l', 's', '\0', + /* 228 - "test.stringListMetric" */ 't', 'e', 's', 't', '.', 's', 't', 'r', 'i', 'n', 'g', 'L', 'i', 's', 't', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 250 - "test.timespanMetric" */ 't', 'e', 's', 't', '.', 't', 'i', 'm', 'e', 's', 'p', 'a', 'n', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 270 - "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', + /* 300 - "test.memoryDistributionMetric" */ 't', 'e', 's', 't', '.', 'm', 'e', 'm', 'o', 'r', 'y', 'D', 'i', 's', 't', 'r', 'i', 'b', 'u', 't', 'i', 'o', 'n', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 330 - "testNested.uuidMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'u', 'u', 'i', 'd', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 352 - "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', + /* 378 - "testNested.eventMetric" */ 't', 'e', 's', 't', 'N', 'e', 's', 't', 'e', 'd', '.', 'e', 'v', 'e', 'n', 't', 'M', 'e', 't', 'r', 'i', 'c', '\0', + /* 401 - "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', +}; + + +static_assert(sizeof(gMetricStringTable) < 4294967296, "Metric string table is too large."); + +const metric_entry_t sMetricByNameLookupEntries[] = { + 1729382274090139725, + 3458764552475246789, + 1152921513196781587, + 2305843030688530528, + 5188146822270419214, + 7493989848663982458, + 1152921517491748909, + 576460756598390784, + 4035225309073637604, + 6341068335467200842, + 6917529092065591648, + 4611686065672028410, + 7493989852958949777, + 2305843034983497850, + 5764607578868810028, + 3458764548180279468, + 2882303791581888666 +}; + + + +static Maybe<uint32_t> +MetricByNameLookup(const nsACString& aKey) +{ + static const uint8_t BASES[] = { + 0, 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 1, 0, 0, 1, 0, 5, 0, 0, 4, 0, 1, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 2, 0, 6, 0, 0, + 0, 0, 20, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + }; + + + const char* bytes = aKey.BeginReading(); + size_t length = aKey.Length(); + auto& entry = mozilla::perfecthash::Lookup(bytes, length, BASES, + sMetricByNameLookupEntries); + return metric_result_check(aKey, entry); +} + + +/** + * Get a category's name from the string table. + */ +static const char* GetCategoryName(category_entry_t entry) { + MOZ_ASSERT(entry < sizeof(gCategoryStringTable), "Entry identifier offset larger than string table"); + return &gCategoryStringTable[entry]; +} + +/** + * Get a metric's identifier from the string table. + */ +static const char* GetMetricIdentifier(metric_entry_t entry) { + uint32_t offset = GLEAN_OFFSET(entry); + MOZ_ASSERT(offset < sizeof(gMetricStringTable), "Entry identifier offset larger than string table"); + return &gMetricStringTable[offset]; +} + +/** + * Check that the found entry is pointing to the right key + * and return it. + * Or return `Nothing()` if the entry was not found. + */ +static Maybe<uint32_t> category_result_check(const nsACString& aKey, category_entry_t entry) { + if (MOZ_UNLIKELY(entry > sizeof(gCategoryStringTable))) { + return Nothing(); + } + if (aKey.EqualsASCII(gCategoryStringTable + entry)) { + return Some(entry); + } + return Nothing(); +} + +/** + * Check if the found entry index is pointing to the right key + * and return the corresponding metric ID. + * Or return `Nothing()` if the entry was not found. + */ +static Maybe<uint32_t> metric_result_check(const nsACString& aKey, uint64_t entry) { + uint32_t metricId = entry >> GLEAN_INDEX_BITS; + uint32_t offset = GLEAN_OFFSET(entry); + + if (offset > sizeof(gMetricStringTable)) { + return Nothing(); + } + + if (aKey.EqualsASCII(gMetricStringTable + offset)) { + return Some(metricId); + } + + return Nothing(); +} + + +#undef GLEAN_INDEX_BITS +#undef GLEAN_ID_BITS +#undef GLEAN_TYPE_ID +#undef GLEAN_METRIC_ID +#undef GLEAN_OFFSET + +} // namespace mozilla::glean +#endif // mozilla_GleanJSMetricsLookup_h diff --git a/toolkit/components/glean/pytest/pings_test.yaml b/toolkit/components/glean/pytest/pings_test.yaml new file mode 100644 index 0000000000..efa6ba9e0a --- /dev/null +++ b/toolkit/components/glean/pytest/pings_test.yaml @@ -0,0 +1,116 @@ +# 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/. + +# This file defines the built-in pings that are recorded by the Glean SDK. They +# are automatically converted to Kotlin code at build time using the +# `glean_parser` PyPI package. + +--- +$schema: moz://mozilla.org/schemas/glean/pings/1-0-0 + +not-baseline: + description: > + This ping is intended to provide metrics that are managed by the library + itself, and not explicitly set by the application or included in the + application's `metrics.yaml` file. + The `baseline` ping is automatically sent when the application is moved to + the background. + include_client_id: true + bugs: + - https://bugzilla.mozilla.org/1512938 + - https://bugzilla.mozilla.org/1599877 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1599877#c25 + notification_emails: + - glean-team@mozilla.com + reasons: + dirty_startup: | + The ping was submitted at startup, because the application process was + killed before the Glean SDK had the chance to generate this ping, when + going to background, in the last session. + + *Note*: this ping will not contain the `glean.baseline.duration` metric. + background: | + The ping was submitted before going to background. + foreground: | + The ping was submitted when the application went to foreground, which + includes when the application starts. + + *Note*: this ping will not contain the `glean.baseline.duration` metric. + +not-metrics: + description: > + The `metrics` ping is intended for all of the metrics that are explicitly + set by the application or are included in the application's `metrics.yaml` + file (except events). + The reported data is tied to the ping's *measurement window*, which is the + time between the collection of two `metrics` ping. Ideally, this window is + expected to be about 24 hours, given that the collection is scheduled daily + at 4AM. Data in the `ping_info` section of the ping can be used to infer the + length of this window. + include_client_id: true + bugs: + - https://bugzilla.mozilla.org/1512938 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1557048#c13 + notification_emails: + - glean-team@mozilla.com + reasons: + overdue: | + The last ping wasn't submitted on the current calendar day, but it's after + 4am, so this ping submitted immediately + today: | + The last ping wasn't submitted on the current calendar day, but it is + still before 4am, so schedule to send this ping on the current calendar + day at 4am. + tomorrow: | + The last ping was already submitted on the current calendar day, so + schedule this ping for the next calendar day at 4am. + upgrade: | + This ping was submitted at startup because the application was just + upgraded. + reschedule: | + A ping was just submitted. This ping was rescheduled for the next calendar + day at 4am. + +not-events: + description: > + The events ping's purpose is to transport all of the event metric + information. The `events` ping is automatically sent when the application is + moved to the background. + include_client_id: true + bugs: + - https://bugzilla.mozilla.org/1512938 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + notification_emails: + - glean-team@mozilla.com + reasons: + startup: | + The ping was submitted at startup. The events ping is always sent if there + are any pending events at startup, because event timestamps can not be + mixed across runs of the application. + background: | + The ping was submitted before going to background. + max_capacity: | + The maximum number of events was reached (default 500 events). + +not-deletion-request: + description: > + This ping is submitted when a user opts out of + sending technical and interaction data to Mozilla. + This ping is intended to communicate to the Data Pipeline + that the user wishes to have their reported Telemetry data deleted. + As such it attempts to send itself at the moment the user + opts out of data collection. + include_client_id: true + send_if_empty: true + bugs: + - https://bugzilla.mozilla.org/1587095 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1587095#c6 + notification_emails: + - glean-team@mozilla.com diff --git a/toolkit/components/glean/pytest/pings_test_output b/toolkit/components/glean/pytest/pings_test_output new file mode 100644 index 0000000000..df705967bc --- /dev/null +++ b/toolkit/components/glean/pytest/pings_test_output @@ -0,0 +1,93 @@ +// -*- mode: Rust -*- + +// AUTOGENERATED BY glean_parser. DO NOT EDIT. + +/* 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/. */ + +use crate::private::Ping; +use once_cell::sync::Lazy; + +#[allow(non_upper_case_globals)] +/// This ping is intended to provide metrics that are managed by the library +/// itself, and not explicitly set by the application or included in the +/// application's `metrics.yaml` file. The `baseline` ping is automatically sent +/// when the application is moved to the background. +pub static not_baseline: Lazy<Ping> = Lazy::new(|| { + Ping::new( + "not-baseline", + true, + false, + vec!["background".into(), "dirty_startup".into(), "foreground".into()], + ) +}); + +#[allow(non_upper_case_globals)] +/// The `metrics` ping is intended for all of the metrics that are explicitly set +/// by the application or are included in the application's `metrics.yaml` file +/// (except events). The reported data is tied to the ping's *measurement window*, +/// which is the time between the collection of two `metrics` ping. Ideally, this +/// window is expected to be about 24 hours, given that the collection is scheduled +/// daily at 4AM. Data in the `ping_info` section of the ping can be used to infer +/// the length of this window. +pub static not_metrics: Lazy<Ping> = Lazy::new(|| { + Ping::new( + "not-metrics", + true, + false, + vec!["overdue".into(), "reschedule".into(), "today".into(), "tomorrow".into(), "upgrade".into()], + ) +}); + +#[allow(non_upper_case_globals)] +/// The events ping's purpose is to transport all of the event metric information. +/// The `events` ping is automatically sent when the application is moved to the +/// background. +pub static not_events: Lazy<Ping> = Lazy::new(|| { + Ping::new( + "not-events", + true, + false, + vec!["background".into(), "max_capacity".into(), "startup".into()], + ) +}); + +#[allow(non_upper_case_globals)] +/// This ping is submitted when a user opts out of sending technical and +/// interaction data to Mozilla. This ping is intended to communicate to the Data +/// Pipeline that the user wishes to have their reported Telemetry data deleted. As +/// such it attempts to send itself at the moment the user opts out of data +/// collection. +pub static not_deletion_request: Lazy<Ping> = Lazy::new(|| { + Ping::new( + "not-deletion-request", + true, + true, + vec![], + ) +}); + + +/// Instantiate each custom ping once to trigger registration. +#[doc(hidden)] +pub fn register_pings() { + let _ = &*not_baseline; + let _ = &*not_metrics; + let _ = &*not_events; + let _ = &*not_deletion_request; +} + +#[cfg(feature = "with_gecko")] +pub(crate) fn submit_ping_by_id(id: u32, reason: Option<&str>) { + match id { + 1 => not_baseline.submit(reason), + 2 => not_metrics.submit(reason), + 3 => not_events.submit(reason), + 4 => not_deletion_request.submit(reason), + _ => { + // TODO: instrument this error. + log::error!("Cannot submit unknown ping {} by id.", id); + } + } +} diff --git a/toolkit/components/glean/pytest/pings_test_output_cpp b/toolkit/components/glean/pytest/pings_test_output_cpp new file mode 100644 index 0000000000..7f9cc03947 --- /dev/null +++ b/toolkit/components/glean/pytest/pings_test_output_cpp @@ -0,0 +1,62 @@ +// -*- mode: C++ -*- + +// AUTOGENERATED BY glean_parser. DO NOT EDIT. + +/* 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_Pings_h +#define mozilla_glean_Pings_h + +#include "mozilla/glean/bindings/Ping.h" + +namespace mozilla::glean_pings { + +/* + * Generated from not-baseline. + * + * This ping is intended to provide metrics that are managed by the library + * itself, and not explicitly set by the application or included in the + * application's `metrics.yaml` file. The `baseline` ping is automatically sent + * when the application is moved to the background. + */ +constexpr glean::impl::Ping NotBaseline(1); + +/* + * Generated from not-metrics. + * + * The `metrics` ping is intended for all of the metrics that are explicitly set + * by the application or are included in the application's `metrics.yaml` file + * (except events). The reported data is tied to the ping's *measurement window*, + * which is the time between the collection of two `metrics` ping. Ideally, this + * window is expected to be about 24 hours, given that the collection is scheduled + * daily at 4AM. Data in the `ping_info` section of the ping can be used to infer + * the length of this window. + */ +constexpr glean::impl::Ping NotMetrics(2); + +/* + * Generated from not-events. + * + * The events ping's purpose is to transport all of the event metric information. + * The `events` ping is automatically sent when the application is moved to the + * background. + */ +constexpr glean::impl::Ping NotEvents(3); + +/* + * Generated from not-deletion-request. + * + * This ping is submitted when a user opts out of sending technical and + * interaction data to Mozilla. This ping is intended to communicate to the Data + * Pipeline that the user wishes to have their reported Telemetry data deleted. As + * such it attempts to send itself at the moment the user opts out of data + * collection. + */ +constexpr glean::impl::Ping NotDeletionRequest(4); + + +} // namespace mozilla::glean_pings + +#endif // mozilla_glean_Pings_h diff --git a/toolkit/components/glean/pytest/pings_test_output_js b/toolkit/components/glean/pytest/pings_test_output_js new file mode 100644 index 0000000000..ecbc6ccf7c --- /dev/null +++ b/toolkit/components/glean/pytest/pings_test_output_js @@ -0,0 +1,99 @@ +// -*- mode: C++ -*- + +// AUTOGENERATED BY glean_parser. DO NOT EDIT. + +/* 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_GleanJSPingsLookup_h +#define mozilla_GleanJSPingsLookup_h + +#define GLEAN_PING_INDEX_BITS (16) +#define GLEAN_PING_ID(entry) ((entry) >> GLEAN_PING_INDEX_BITS) +#define GLEAN_PING_INDEX(entry) ((entry) & ((1UL << GLEAN_PING_INDEX_BITS) - 1)) + +namespace mozilla::glean { + +// Contains the ping id and the index into the ping string table. +using ping_entry_t = uint32_t; + +static Maybe<uint32_t> ping_result_check(const nsACString& aKey, ping_entry_t aEntry); + +#if defined(_MSC_VER) && !defined(__clang__) +const char gPingStringTable[] = { +#else +constexpr char gPingStringTable[] = { +#endif + /* 0 - "notBaseline" */ 'n', 'o', 't', 'B', 'a', 's', 'e', 'l', 'i', 'n', 'e', '\0', + /* 12 - "notMetrics" */ 'n', 'o', 't', 'M', 'e', 't', 'r', 'i', 'c', 's', '\0', + /* 23 - "notEvents" */ 'n', 'o', 't', 'E', 'v', 'e', 'n', 't', 's', '\0', + /* 33 - "notDeletionRequest" */ 'n', 'o', 't', 'D', 'e', 'l', 'e', 't', 'i', 'o', 'n', 'R', 'e', 'q', 'u', 'e', 's', 't', '\0', +}; + + + +const ping_entry_t sPingByNameLookupEntries[] = { + 65536, + 196631, + 262177, + 131084 +}; + + + +static Maybe<uint32_t> +PingByNameLookup(const nsACString& aKey) +{ + static const uint8_t BASES[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 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, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + + const char* bytes = aKey.BeginReading(); + size_t length = aKey.Length(); + auto& entry = mozilla::perfecthash::Lookup(bytes, length, BASES, + sPingByNameLookupEntries); + return ping_result_check(aKey, entry); +} + + +/** + * Get a ping's name given its entry from the PHF. + */ +static const char* GetPingName(ping_entry_t aEntry) { + uint32_t idx = GLEAN_PING_INDEX(aEntry); + MOZ_ASSERT(idx < sizeof(gPingStringTable), "Ping index larger than string table"); + return &gPingStringTable[idx]; +} + +/** + * Check if the found entry is pointing at the correct ping. + * PHF can false-positive a result when the key isn't present, so we check + * for a string match. If it fails, return Nothing(). If we found it, + * return the ping's id. + */ +static Maybe<uint32_t> ping_result_check(const nsACString& aKey, ping_entry_t aEntry) { + uint32_t idx = GLEAN_PING_INDEX(aEntry); + uint32_t id = GLEAN_PING_ID(aEntry); + + if (MOZ_UNLIKELY(idx > sizeof(gPingStringTable))) { + return Nothing(); + } + + if (aKey.EqualsASCII(&gPingStringTable[idx])) { + return Some(id); + } + + return Nothing(); +} + +#undef GLEAN_PING_INDEX_BITS +#undef GLEAN_PING_ID +#undef GLEAN_PING_INDEX + +} // namespace mozilla::glean +#endif // mozilla_GleanJSPingsLookup_h diff --git a/toolkit/components/glean/pytest/python.ini b/toolkit/components/glean/pytest/python.ini new file mode 100644 index 0000000000..d91d5bafa2 --- /dev/null +++ b/toolkit/components/glean/pytest/python.ini @@ -0,0 +1,6 @@ +[DEFAULT] +subsuite = fog + +[test_glean_parser_rust.py] +[test_glean_parser_cpp.py] +[test_glean_parser_js.py] diff --git a/toolkit/components/glean/pytest/test_glean_parser_cpp.py b/toolkit/components/glean/pytest/test_glean_parser_cpp.py new file mode 100644 index 0000000000..96bcd241a8 --- /dev/null +++ b/toolkit/components/glean/pytest/test_glean_parser_cpp.py @@ -0,0 +1,77 @@ +# 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/. + +import io +import mozunit +from os import path +from pathlib import Path +import sys + + +# Shenanigans to import the cpp outputter extension +FOG_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir)) +sys.path.append(path.join(FOG_ROOT_PATH, "build_scripts", "glean_parser_ext")) +import cpp + +# Shenanigans to import the in-tree glean_parser +GECKO_PATH = path.join(FOG_ROOT_PATH, path.pardir, path.pardir, path.pardir) +sys.path.append(path.join(GECKO_PATH, "third_party", "python", "glean_parser")) +from glean_parser import lint, parser, util + + +def test_all_metric_types(): + """Honestly, this is a pretty bad test. + It generates C++ for a given test metrics.yaml and compares it byte-for-byte + with an expected output C++ file. + Expect it to be fragile. + To generate a new expected output file, copy the test yaml over the one in t/c/g, + run mach build, then copy the C++ output from objdir/t/c/g/. + """ + + options = {"allow_reserved": False} + input_files = [Path(path.join(path.dirname(__file__), "metrics_test.yaml"))] + + all_objs = parser.parse_objects(input_files, options) + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + output_fd = io.StringIO() + cpp.output_cpp(all_objs.value, output_fd, options) + + with open( + path.join(path.dirname(__file__), "metrics_test_output_cpp"), "r" + ) as file: + EXPECTED_CPP = file.read() + assert output_fd.getvalue() == EXPECTED_CPP + + +def test_fake_pings(): + """Another similarly-fragile test. + It generates C++ for pings_test.yaml, comparing it byte-for-byte + with an expected output C++ file `pings_test_output_cpp`. + Expect it to be fragile. + To generate a new expected output file, edit t/c/g/metrics_index.py, + comment out all other ping yamls, and add one for + t/c/g/pytest/pings_test.yaml. Run `mach build` (it'll fail). Copy + objdir/t/c/g/GleanPings.h over pings_test_output_cpp. + (Don't forget to undo your edits to t/c/g/metrics_index.py) + """ + + options = {"allow_reserved": False} + input_files = [Path(path.join(path.dirname(__file__), "pings_test.yaml"))] + + all_objs = parser.parse_objects(input_files, options) + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + output_fd = io.StringIO() + cpp.output_cpp(all_objs.value, output_fd, options) + + with open(path.join(path.dirname(__file__), "pings_test_output_cpp"), "r") as file: + EXPECTED_CPP = file.read() + assert output_fd.getvalue() == EXPECTED_CPP + + +if __name__ == "__main__": + mozunit.main() diff --git a/toolkit/components/glean/pytest/test_glean_parser_js.py b/toolkit/components/glean/pytest/test_glean_parser_js.py new file mode 100644 index 0000000000..09ba623fcf --- /dev/null +++ b/toolkit/components/glean/pytest/test_glean_parser_js.py @@ -0,0 +1,75 @@ +# 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/. + +import io +import mozunit +from os import path +from pathlib import Path +import sys + + +# Shenanigans to import the js outputter extension +FOG_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir)) +sys.path.append(path.join(FOG_ROOT_PATH, "build_scripts", "glean_parser_ext")) +import js + +# Shenanigans to import the in-tree glean_parser +GECKO_PATH = path.join(FOG_ROOT_PATH, path.pardir, path.pardir, path.pardir) +sys.path.append(path.join(GECKO_PATH, "third_party", "python", "glean_parser")) +from glean_parser import lint, parser, util + + +def test_all_metric_types(): + """Honestly, this is a pretty bad test. + It generates C++ for a given test metrics.yaml and compares it byte-for-byte + with an expected output C++ file. + Expect it to be fragile. + To generate a new expected output file, copy the test yaml over the one in t/c/g, + run mach build, then copy the C++ output from objdir/t/c/g/. + """ + + options = {"allow_reserved": False} + input_files = [Path(path.join(path.dirname(__file__), "metrics_test.yaml"))] + + all_objs = parser.parse_objects(input_files, options) + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + output_fd = io.StringIO() + js.output_js(all_objs.value, output_fd, options) + + with open(path.join(path.dirname(__file__), "metrics_test_output_js"), "r") as file: + EXPECTED_JS = file.read() + assert output_fd.getvalue() == EXPECTED_JS + + +def test_fake_pings(): + """Another similarly-fragile test. + It generates C++ for pings_test.yaml, comparing it byte-for-byte + with an expected output C++ file `pings_test_output_js`. + Expect it to be fragile. + To generate a new expected output file, edit t/c/g/metrics_index.py, + comment out all other ping yamls, and add one for + t/c/g/pytest/pings_test.yaml. Run `mach build` (it'll fail). Copy + objdir/t/c/g/GleanJSPingsLookup.h over pings_test_output_js. + (Don't forget to undo your edits to t/c/g/metrics_index.py) + """ + + options = {"allow_reserved": False} + input_files = [Path(path.join(path.dirname(__file__), "pings_test.yaml"))] + + all_objs = parser.parse_objects(input_files, options) + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + output_fd = io.StringIO() + js.output_js(all_objs.value, output_fd, options) + + with open(path.join(path.dirname(__file__), "pings_test_output_js"), "r") as file: + EXPECTED_JS = file.read() + assert output_fd.getvalue() == EXPECTED_JS + + +if __name__ == "__main__": + mozunit.main() diff --git a/toolkit/components/glean/pytest/test_glean_parser_rust.py b/toolkit/components/glean/pytest/test_glean_parser_rust.py new file mode 100644 index 0000000000..6bf3fbe841 --- /dev/null +++ b/toolkit/components/glean/pytest/test_glean_parser_rust.py @@ -0,0 +1,113 @@ +# 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/. + +import io +import mozunit +from os import path +from pathlib import Path +import re +import sys + + +# Shenanigans to import the rust outputter extension +FOG_ROOT_PATH = path.abspath(path.join(path.dirname(__file__), path.pardir)) +sys.path.append(path.join(FOG_ROOT_PATH, "build_scripts", "glean_parser_ext")) +import run_glean_parser +import rust + +# Shenanigans to import the in-tree glean_parser +GECKO_PATH = path.join(FOG_ROOT_PATH, path.pardir, path.pardir, path.pardir) +sys.path.append(path.join(GECKO_PATH, "third_party", "python", "glean_parser")) +from glean_parser import lint, parser, util + + +def test_all_metric_types(): + """Honestly, this is a pretty bad test. + It generates Rust for a given test metrics.yaml and compares it byte-for-byte + with an expected output Rust file. + Expect it to be fragile. + To generate a new expected output file, copy the test yaml over the one in t/c/g, + run mach build, then copy the rust output from objdir/t/c/g/api/src/. + """ + + options = {"allow_reserved": False} + input_files = [Path(path.join(path.dirname(__file__), "metrics_test.yaml"))] + + all_objs = parser.parse_objects(input_files, options) + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + output_fd = io.StringIO() + rust.output_rust(all_objs.value, output_fd, options) + + with open(path.join(path.dirname(__file__), "metrics_test_output"), "r") as file: + EXPECTED_RUST = file.read() + assert output_fd.getvalue() == EXPECTED_RUST + + +def test_fake_pings(): + """Another similarly-bad test. + It generates Rust for pings_test.yaml, comparing it byte-for-byte + with an expected output Rust file. + Expect it to be fragile. + To generate a new expected output file, copy the test yaml over the one in t/c/g, + run mach build, then copy the rust output from objdir/t/c/g/api/src/. + """ + + options = {"allow_reserved": False} + input_files = [Path(path.join(path.dirname(__file__), "pings_test.yaml"))] + + all_objs = parser.parse_objects(input_files, options) + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + output_fd = io.StringIO() + rust.output_rust(all_objs.value, output_fd, options) + + with open(path.join(path.dirname(__file__), "pings_test_output"), "r") as file: + EXPECTED_RUST = file.read() + assert output_fd.getvalue() == EXPECTED_RUST + + +def test_expires_version(): + """This test relies on the intermediary object format output by glean_parser. + Expect it to be fragile on glean_parser updates that change that format. + """ + + # The test file has 41, 42, 100. Use 42.0a1 here to ensure "expires == version" means expired. + options = run_glean_parser.get_parser_options("42.0a1") + input_files = [ + Path(path.join(path.dirname(__file__), "metrics_expires_versions_test.yaml")) + ] + + all_objs = parser.parse_objects(input_files, options) + + assert not util.report_validation_errors(all_objs) + assert not lint.lint_metrics(all_objs.value, options) + + assert all_objs.value["test"]["expired1"].disabled is True + assert all_objs.value["test"]["expired2"].disabled is True + assert all_objs.value["test"]["unexpired"].disabled is False + + +def test_numeric_expires(): + """What if the expires value is a number, not a string? + This test relies on the intermediary object format output by glean_parser. + Expect it to be fragile on glean_parser updates that change that format. + """ + + # We'll never get to checking expires values, so this app version shouldn't matter. + options = run_glean_parser.get_parser_options("42.0a1") + input_files = [ + Path(path.join(path.dirname(__file__), "metrics_expires_number_test.yaml")) + ] + + all_objs = parser.parse_objects(input_files, options) + errors = list(all_objs) + assert len(errors) == 1 + assert re.search("99 is not of type 'string'", str(errors[0])) + + +if __name__ == "__main__": + mozunit.main() |