[UniFFI] Migrate timespan metric implementation

This commit is contained in:
Jan-Erik Rediger 2021-11-18 16:25:22 +01:00 коммит произвёл Jan-Erik Rediger
Родитель c5f9686bab
Коммит dd8056d2aa
7 изменённых файлов: 218 добавлений и 279 удалений

Просмотреть файл

@ -4,14 +4,7 @@
package mozilla.telemetry.glean.private
import android.os.SystemClock
import androidx.annotation.VisibleForTesting
import com.sun.jna.StringArray
import mozilla.telemetry.glean.Dispatchers
import mozilla.telemetry.glean.rust.LibGleanFFI
import mozilla.telemetry.glean.rust.toBoolean
import mozilla.telemetry.glean.rust.toByte
import mozilla.telemetry.glean.testing.ErrorType
/**
* This implements the developer facing API for recording timespans.
@ -21,184 +14,30 @@ import mozilla.telemetry.glean.testing.ErrorType
*
* The timespans API exposes the [start], [stop] and [cancel] methods.
*/
class TimespanMetricType internal constructor(
private var handle: Long,
private val disabled: Boolean,
private val sendInPings: List<String>
) {
/**
* The public constructor used by automatically generated metrics.
*/
constructor(
disabled: Boolean,
category: String,
lifetime: Lifetime,
name: String,
sendInPings: List<String>,
timeUnit: TimeUnit = TimeUnit.Minute
) : this(handle = 0, disabled = disabled, sendInPings = sendInPings) {
val ffiPingsList = StringArray(sendInPings.toTypedArray(), "utf-8")
this.handle = LibGleanFFI.INSTANCE.glean_new_timespan_metric(
category = category,
name = name,
send_in_pings = ffiPingsList,
send_in_pings_len = sendInPings.size,
lifetime = lifetime.ordinal,
disabled = disabled.toByte(),
time_unit = timeUnit.ordinal
)
typealias TimespanMetricType = mozilla.telemetry.glean.internal.TimespanMetric
/**
* Convenience method to simplify measuring a function or block of code
*
* If the measured function throws, the measurement is canceled and the exception rethrown.
*/
@Suppress("TooGenericExceptionCaught")
fun <U> TimespanMetricType.measure(funcToMeasure: () -> U): U {
this.start()
val returnValue = try {
funcToMeasure()
} catch (e: Exception) {
this.cancel()
throw e
}
/**
* Start tracking time for the provided metric.
* This records an error if its already tracking time (i.e. start was already
* called with no corresponding [stop]): in that case the original
* start time will be preserved.
*/
fun start() {
if (disabled) {
return
}
val startTime = SystemClock.elapsedRealtimeNanos()
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_timespan_set_start(this@TimespanMetricType.handle, startTime)
}
}
/**
* Stop tracking time for the provided metric.
* Sets the metric to the elapsed time, but does not overwrite an already
* existing value.
* This will record an error if no [start] was called or there is an already
* existing value.
*/
fun stop() {
if (disabled) {
return
}
val stopTime = SystemClock.elapsedRealtimeNanos()
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_timespan_set_stop(this@TimespanMetricType.handle, stopTime)
}
}
/**
* Convenience method to simplify measuring a function or block of code
*
* If the measured function throws, the measurement is canceled and the exception rethrown.
*/
@Suppress("TooGenericExceptionCaught")
fun <U> measure(funcToMeasure: () -> U): U {
start()
val returnValue = try {
funcToMeasure()
} catch (e: Exception) {
cancel()
throw e
}
stop()
return returnValue
}
/**
* Abort a previous [start] call. No error is recorded if no [start] was called.
*/
fun cancel() {
if (disabled) {
return
}
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_timespan_cancel(this@TimespanMetricType.handle)
}
}
/**
* Explicitly set the timespan value, in nanoseconds.
*
* This API should only be used if your library or application requires recording
* times in a way that can not make use of [start]/[stop]/[cancel].
*
* [setRawNanos] does not overwrite a running timer or an already existing value.
*
* @param elapsedNanos The elapsed time to record, in nanoseconds.
*/
fun setRawNanos(elapsedNanos: Long) {
if (disabled) {
return
}
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_timespan_set_raw_nanos(
this@TimespanMetricType.handle,
elapsedNanos)
}
}
/**
* Tests whether a value is stored for the metric for testing purposes only
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return true if metric value exists, otherwise false
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testHasValue(pingName: String = sendInPings.first()): Boolean {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
return LibGleanFFI
.INSTANCE.glean_timespan_test_has_value(this.handle, pingName)
.toBoolean()
}
/**
* Returns the stored value for testing purposes only
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return value of the stored metric
* @throws [NullPointerException] if no value is stored
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetValue(pingName: String = sendInPings.first()): Long {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
if (!testHasValue(pingName)) {
throw NullPointerException("Metric has no value")
}
return LibGleanFFI.INSTANCE.glean_timespan_test_get_value(this.handle, pingName)
}
/**
* Returns the number of errors recorded for the given metric.
*
* @param errorType The type of the error recorded.
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return the number of errors recorded for the metric.
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetNumRecordedErrors(errorType: ErrorType, pingName: String = sendInPings.first()): Int {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
return LibGleanFFI.INSTANCE.glean_timespan_test_get_num_recorded_errors(
this.handle, errorType.ordinal, pingName
)
}
this.stop()
return returnValue
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun TimespanMetricType.testHasValue(pingName: String? = null): Boolean {
return this.testGetValue(pingName) != null
}

Просмотреть файл

@ -14,6 +14,7 @@ import mozilla.telemetry.glean.testing.GleanTestRule
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
@ -28,13 +29,13 @@ class TimespanMetricTypeTest {
@Test
fun `The API must record to its storage engine`() {
// Define a timespan metric, which will be stored in "store1"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Record a timespan.
@ -43,19 +44,19 @@ class TimespanMetricTypeTest {
// Check that data was properly recorded.
assertTrue(metric.testHasValue())
assertTrue(metric.testGetValue() >= 0)
assertTrue(metric.testGetValue()!! >= 0)
}
@Test
fun `The API should not record if the metric is disabled`() {
// Define a timespan metric, which will be stored in "store1"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = true,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Record a timespan.
@ -73,13 +74,13 @@ class TimespanMetricTypeTest {
@Test
fun `The API must correctly cancel`() {
// Define a timespan metric, which will be stored in "store1"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Record a timespan.
@ -90,32 +91,33 @@ class TimespanMetricTypeTest {
// Check that data was not recorded.
assertFalse("The API should not record a counter if metric is cancelled",
metric.testHasValue())
assertEquals(1, metric.testGetNumRecordedErrors(ErrorType.InvalidState))
assertEquals(1, metric.testGetNumRecordedErrors(ErrorType.INVALID_STATE))
}
@Test(expected = NullPointerException::class)
// TODO: Fixme: should we continue throwing an exception instead?
@Test //(expected = NullPointerException::class)
fun `testGetValue() throws NullPointerException if nothing is stored`() {
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
metric.testGetValue()
assertNull(metric.testGetValue())
}
@Test
fun `The API saves to secondary pings`() {
// Define a timespan metric, which will be stored in "store1" and "store2"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1", "store2"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Record a timespan.
@ -124,19 +126,19 @@ class TimespanMetricTypeTest {
// Check that data was properly recorded in the second ping.
assertTrue(metric.testHasValue("store2"))
assertTrue(metric.testGetValue("store2") >= 0)
assertTrue(metric.testGetValue("store2")!! >= 0)
}
@Test
fun `Records an error if started twice`() {
// Define a timespan metric, which will be stored in "store1" and "store2"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1", "store2"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Record a timespan.
@ -146,20 +148,20 @@ class TimespanMetricTypeTest {
// Check that data was properly recorded in the second ping.
assertTrue(metric.testHasValue("store2"))
assertTrue(metric.testGetValue("store2") >= 0)
assertEquals(1, metric.testGetNumRecordedErrors(ErrorType.InvalidState))
assertTrue(metric.testGetValue("store2")!! >= 0)
assertEquals(1, metric.testGetNumRecordedErrors(ErrorType.INVALID_STATE))
}
@Test
fun `Value unchanged if stopped twice`() {
// Define a timespan metric, which will be stored in "store1" and "store2"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Nanosecond
), timeUnit = TimeUnit.NANOSECOND
)
// Record a timespan.
@ -177,52 +179,51 @@ class TimespanMetricTypeTest {
fun `test setRawNanos`() {
val timespanNanos = 6 * 1000000000L
val metric = TimespanMetricType(
false,
"telemetry",
Lifetime.Ping,
"explicit_timespan",
listOf("store1"),
timeUnit = TimeUnit.Second
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.PING,
name = "explicit_timespan",
sendInPings = listOf("store1"),
), timeUnit = TimeUnit.SECOND
)
metric.setRawNanos(timespanNanos)
assertEquals(6, metric.testGetValue())
assertEquals(6, metric.testGetValue()!!)
}
@Test
fun `test setRawNanos followed by other API`() {
val timespanNanos = 6 * 1000000000L
val metric = TimespanMetricType(
false,
"telemetry",
Lifetime.Ping,
"explicit_timespan_1",
listOf("store1"),
timeUnit = TimeUnit.Second
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.PING,
name = "explicit_timespan_1",
sendInPings = listOf("store1"),
), timeUnit = TimeUnit.SECOND
)
metric.setRawNanos(timespanNanos)
assertEquals(6, metric.testGetValue())
assertEquals(6, metric.testGetValue()!!)
metric.start()
metric.stop()
val value = metric.testGetValue()
assertEquals(6, value)
assertEquals(6, metric.testGetValue()!!)
}
@Test
fun `setRawNanos does not overwrite value`() {
val timespanNanos = 6 * 1000000000L
val metric = TimespanMetricType(
false,
"telemetry",
Lifetime.Ping,
"explicit_timespan_1",
listOf("store1"),
timeUnit = TimeUnit.Second
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.PING,
name = "explicit_timespan_2",
sendInPings = listOf("store1"),
), timeUnit = TimeUnit.SECOND
)
metric.start()
@ -231,20 +232,20 @@ class TimespanMetricTypeTest {
metric.setRawNanos(timespanNanos)
assertEquals(value, metric.testGetValue())
assertEquals(value, metric.testGetValue()!!)
}
@Test
fun `setRawNanos does nothing when timer is running`() {
val timespanNanos = 1000000000L
val metric = TimespanMetricType(
false,
"telemetry",
Lifetime.Ping,
"explicit_timespan",
listOf("store1"),
timeUnit = TimeUnit.Second
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.PING,
name = "explicit_timespan_3",
sendInPings = listOf("store1"),
), timeUnit = TimeUnit.SECOND
)
metric.start()
@ -253,19 +254,19 @@ class TimespanMetricTypeTest {
// If setRawNanos worked, (which it's not supposed to in this case), it would
// have recorded 1000000000 ns == 1s. Make sure it's not that.
assertNotEquals(1, metric.testGetValue())
assertNotEquals(1, metric.testGetValue()!!)
}
@Test
fun `measure function correctly measures values`() {
// Define a timespan metric, which will be stored in "store1"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Create a function to measure, which also returns a value to test that we properly pass
@ -285,18 +286,18 @@ class TimespanMetricTypeTest {
// Check that data was properly recorded.
assertTrue("Metric must have a value", metric.testHasValue())
assertTrue("Metric value must be greater than zero", metric.testGetValue() >= 0)
assertTrue("Metric value must be greater than zero", metric.testGetValue()!! >= 0)
}
@Test
fun `measure function does not change behavior with early return`() {
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Ping,
lifetime = Lifetime.PING,
name = "inlined",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Nanosecond
), timeUnit = TimeUnit.NANOSECOND
)
// We define a function that measures the whole function call runtime
@ -315,19 +316,19 @@ class TimespanMetricTypeTest {
assertEquals("Test value must match", 17, res)
assertTrue("Metric must have a value", metric.testHasValue())
assertTrue("Metric value must be greater than zero", metric.testGetValue() >= 0)
assertTrue("Metric value must be greater than zero", metric.testGetValue()!! >= 0)
}
@Test
fun `measure function bubbles up exceptions and timing is canceled`() {
// Define a timespan metric, which will be stored in "store1"
val metric = TimespanMetricType(
val metric = TimespanMetricType(CommonMetricData(
disabled = false,
category = "telemetry",
lifetime = Lifetime.Application,
lifetime = Lifetime.APPLICATION,
name = "timespan_metric",
sendInPings = listOf("store1"),
timeUnit = TimeUnit.Millisecond
), timeUnit = TimeUnit.MILLISECOND
)
// Create a function that will throw a NPE

Просмотреть файл

@ -126,6 +126,17 @@ enum Lifetime {
"User",
};
enum ErrorType {
// For when the value to be recorded does not match the metric-specific restrictions
"InvalidValue",
// For when the label of a labeled metric does not match the restrictions
"InvalidLabel",
// For when the metric caught an invalid state while recording
"InvalidState",
// For when the value to be recorded overflows the metric-specific upper range
"InvalidOverflow",
};
dictionary CommonMetricData {
string category;
string name;
@ -149,3 +160,38 @@ interface PingType {
constructor(string name, boolean include_client_id, boolean send_if_empty, sequence<string> reason_codes);
void submit(optional string? reason = null);
};
// Different resolutions supported by the time related metric types
// (e.g. DatetimeMetric).
enum TimeUnit {
// Represents nanosecond precision.
"Nanosecond",
// Represents microsecond precision.
"Microsecond",
// Represents millisecond precision.
"Millisecond",
// Represents second precision.
"Second",
// Represents minute precision.
"Minute",
// Represents hour precision.
"Hour",
// Represents day precision.
"Day",
};
interface TimespanMetric {
constructor(CommonMetricData meta, TimeUnit time_unit);
void start();
void stop();
void cancel();
void set_raw_nanos(i64 elapsed);
i64? test_get_value(optional string? ping_name = null);
i32 test_get_num_recorded_errors(ErrorType error, optional string? ping_name = null);
};

Просмотреть файл

@ -48,7 +48,7 @@ pub use crate::core::Glean;
use crate::core_metrics::ClientInfoMetrics;
pub use crate::error::{Error, ErrorKind, Result};
pub use crate::error_recording::{test_get_num_recorded_errors, ErrorType};
pub use crate::metrics::{CounterMetric, PingType, RecordedExperiment};
pub use crate::metrics::{CounterMetric, PingType, RecordedExperiment, TimeUnit, TimespanMetric};
pub use crate::upload::{PingRequest, PingUploadTask, UploadResult};
const GLEAN_VERSION: &str = env!("CARGO_PKG_VERSION");

Просмотреть файл

@ -687,14 +687,14 @@ fn test_change_metric_type_runtime() {
timespan_metric.set_stop(&glean, duration);
assert_eq!(
timespan_metric.get_value(&glean, Some(ping_name)).unwrap(),
timespan_metric.get_value(&glean, ping_name).unwrap(),
60,
"Expected properly deserialized time"
);
// We expect old data to be lost forever. See the following bug comment
// https://bugzilla.mozilla.org/show_bug.cgi?id=1621757#c1 for more context.
assert_eq!(None, string_metric.test_get_value(&glean, ping_name));
assert_eq!(None, string_metric.get_value(&glean, ping_name));
}
#[test]

Просмотреть файл

@ -2,10 +2,11 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::convert::TryInto;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use crate::error_recording::{record_error, ErrorType};
use crate::error_recording::{record_error, test_get_num_recorded_errors, ErrorType};
use crate::metrics::time_unit::TimeUnit;
use crate::metrics::Metric;
use crate::metrics::MetricType;
@ -173,6 +174,25 @@ impl TimespanMetric {
crate::launch_with_glean(move |glean| metric.set_raw_sync(glean, elapsed));
}
/// Explicitly sets the timespan value.
///
/// This API should only be used if your library or application requires
/// recording times in a way that can not make use of
/// [`set_start`](TimespanMetric::set_start)/[`set_stop`](TimespanMetric::set_stop)/[`cancel`](TimespanMetric::cancel).
///
/// Care should be taken using this if the ping lifetime might contain more
/// than one timespan measurement. To be safe,
/// [`set_raw`](TimespanMetric::set_raw) should generally be followed by
/// sending a custom ping containing the timespan.
///
/// # Arguments
///
/// * `elapsed_nanos` - The elapsed time to record, in nanoseconds.
pub fn set_raw_nanos(&self, elapsed_nanos: i64) {
let elapsed = Duration::from_nanos(elapsed_nanos.try_into().unwrap_or(0));
self.set_raw(elapsed)
}
/// Set raw but sync
pub fn set_raw_sync(&self, glean: &Glean, elapsed: Duration) {
if !self.meta.should_record() {
@ -229,14 +249,25 @@ impl TimespanMetric {
/// Gets the currently stored value as an integer.
///
/// This doesn't clear the stored value.
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<u64> {
pub fn test_get_value(&self, ping_name: Option<String>) -> Option<i64> {
crate::block_on_dispatcher();
crate::core::with_glean(|glean| self.get_value(glean, ping_name.as_deref()))
crate::core::with_glean(|glean| {
self.get_value(glean, ping_name.as_deref()).map(|val| {
val.try_into()
.expect("Timespan can't be represented as i64")
})
})
}
/// Get the current value
pub fn get_value(&self, glean: &Glean, ping_name: Option<&str>) -> Option<u64> {
let queried_ping_name = ping_name.unwrap_or_else(|| &self.meta.send_in_pings[0]);
pub fn get_value<'a, S: Into<Option<&'a str>>>(
&self,
glean: &Glean,
ping_name: S,
) -> Option<u64> {
let queried_ping_name = ping_name
.into()
.unwrap_or_else(|| &self.meta().send_in_pings[0]);
match StorageManager.snapshot_metric_for_test(
glean.storage(),
@ -248,4 +279,26 @@ impl TimespanMetric {
_ => None,
}
}
/// **Exported for test purposes.**
///
/// Gets the number of recorded errors for the given metric and error type.
///
/// # Arguments
///
/// * `error` - The type of error
/// * `ping_name` - represents the optional name of the ping to retrieve the
/// metric for. Defaults to the first value in `send_in_pings`.
///
/// # Returns
///
/// The number of errors reported.
pub fn test_get_num_recorded_errors(&self, error: ErrorType, ping_name: Option<String>) -> i32 {
crate::block_on_dispatcher();
crate::core::with_glean_mut(|glean| {
test_get_num_recorded_errors(glean, self.meta(), error, ping_name.as_deref())
.unwrap_or(0)
})
}
}

Просмотреть файл

@ -44,7 +44,7 @@ fn serializer_should_correctly_serialize_timespans() {
metric.set_stop(&glean, duration);
let val = metric
.get_value(&glean, Some("store1"))
.get_value(&glean, "store1")
.expect("Value should be stored");
assert_eq!(duration, val, "Recorded timespan should be positive.");
}
@ -86,7 +86,7 @@ fn single_elapsed_time_must_be_recorded() {
metric.set_stop(&glean, duration);
let val = metric
.get_value(&glean, Some("store1"))
.get_value(&glean, "store1")
.expect("Value should be stored");
assert_eq!(duration, val, "Recorded timespan should be positive.");
}
@ -119,13 +119,13 @@ fn second_timer_run_is_skipped() {
test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidState, None).is_err()
);
let first_value = metric.get_value(&glean, Some("store1")).unwrap();
let first_value = metric.get_value(&glean, "store1").unwrap();
assert_eq!(duration, first_value);
metric.set_start(&glean, 0);
metric.set_stop(&glean, duration * 2);
let second_value = metric.get_value(&glean, Some("store1")).unwrap();
let second_value = metric.get_value(&glean, "store1").unwrap();
assert_eq!(second_value, first_value);
// Make sure that the error has been recorded: we had a stored value, the
@ -168,7 +168,7 @@ fn recorded_time_conforms_to_resolution() {
ns_metric.set_start(&glean, 0);
ns_metric.set_stop(&glean, duration);
let ns_value = ns_metric.get_value(&glean, Some("store1")).unwrap();
let ns_value = ns_metric.get_value(&glean, "store1").unwrap();
assert_eq!(duration, ns_value);
// 1 minute in nanoseconds
@ -176,7 +176,7 @@ fn recorded_time_conforms_to_resolution() {
minute_metric.set_start(&glean, 0);
minute_metric.set_stop(&glean, duration_minute);
let minute_value = minute_metric.get_value(&glean, Some("store1")).unwrap();
let minute_value = minute_metric.get_value(&glean, "store1").unwrap();
assert_eq!(1, minute_value);
}
@ -201,7 +201,7 @@ fn cancel_does_not_store() {
metric.set_start(&glean, 0);
metric.cancel();
assert_eq!(None, metric.get_value(&glean, Some("store1")));
assert_eq!(None, metric.get_value(&glean, "store1"));
}
#[test]
@ -224,10 +224,10 @@ fn nothing_stored_before_stop() {
metric.set_start(&glean, 0);
assert_eq!(None, metric.get_value(&glean, Some("store1")));
assert_eq!(None, metric.get_value(&glean, "store1"));
metric.set_stop(&glean, duration);
assert_eq!(duration, metric.get_value(&glean, Some("store1")).unwrap());
assert_eq!(duration, metric.get_value(&glean, "store1").unwrap());
}
#[test]
@ -250,7 +250,7 @@ fn set_raw_time() {
metric.set_raw_sync(&glean, time);
let time_in_ns = time.as_nanos() as u64;
assert_eq!(Some(time_in_ns), metric.get_value(&glean, Some("store1")));
assert_eq!(Some(time_in_ns), metric.get_value(&glean, "store1"));
}
#[test]
@ -276,7 +276,7 @@ fn set_raw_time_does_nothing_when_timer_running() {
metric.set_stop(&glean, 60);
// We expect the start/stop value, not the raw value.
assert_eq!(Some(60), metric.get_value(&glean, Some("store1")));
assert_eq!(Some(60), metric.get_value(&glean, "store1"));
// Make sure that the error has been recorded
assert_eq!(
@ -319,7 +319,7 @@ fn timespan_is_not_tracked_across_upload_toggle() {
metric.set_stop(&glean, 200);
// Nothing should have been recorded.
assert_eq!(None, metric.get_value(&glean, Some("store1")));
assert_eq!(None, metric.get_value(&glean, "store1"));
// Make sure that the error has been recorded
assert_eq!(
@ -345,7 +345,7 @@ fn time_cannot_go_backwards() {
// Time cannot go backwards.
metric.set_start(&glean, 10);
metric.set_stop(&glean, 0);
assert!(metric.get_value(&glean, Some("test1")).is_none());
assert!(metric.get_value(&glean, "test1").is_none());
assert_eq!(
Ok(1),
test_get_num_recorded_errors(&glean, metric.meta(), ErrorType::InvalidValue, None),