blob: 1a51b1033112f4800da6d3fec9cfb779bcf38966 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.test.metrics;
import androidx.annotation.IntDef;
import org.junit.Assert;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.HistogramWatcher;
/**
* Tests the {@link HistogramWatcher} test util in the following contexts:
* - Before native load
* - Transitioning from CachingUmaRecorder (before native load) to NativeUmaRecorder (after).
* - After native load
*
* Tests are split into multiple classes so they can be batched by scenario.
*/
public class HistogramWatcherTestBase {
protected static final String TIMES_HISTOGRAM_1 = "TimesHistogram1";
protected static final String BOOLEAN_HISTOGRAM = "BooleanHistogram";
protected static final String EXACT_LINEAR_HISTOGRAM_1 = "ExactLinearHistogram"; // max 10
protected static final String EXACT_LINEAR_HISTOGRAM_2 = "ExactLinearHistogram2"; // max 20
protected static final String EXACT_LINEAR_HISTOGRAM_3 = "ExactLinearHistogram3"; // max 30
protected static final String ENUM_HISTOGRAM = "EnumHistogram"; // max 10
@IntDef({
TestScenario.WITHOUT_NATIVE,
TestScenario.TRANSITION_TO_NATIVE,
TestScenario.WITH_NATIVE
})
protected @interface TestScenario {
int WITHOUT_NATIVE = 0;
int TRANSITION_TO_NATIVE = 1;
int WITH_NATIVE = 2;
}
protected HistogramWatcher mWatcher;
protected void doTestSingleRecordMissing_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher = HistogramWatcher.newSingleRecordWatcher(TIMES_HISTOGRAM_1, 6000);
// Act
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(TIMES_HISTOGRAM_1, e.getMessage());
assertContains("1 record(s) expected: [6000]", e.getMessage());
assertContains("0 record(s) seen: []", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestFourTimesHistograms_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(TIMES_HISTOGRAM_1, 6000)
.expectIntRecord(TIMES_HISTOGRAM_1, 7000)
.expectIntRecordTimes(TIMES_HISTOGRAM_1, 8000, 2)
.build();
// Act
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 6000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 7000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestExtraRecord_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(TIMES_HISTOGRAM_1, 6000)
.expectIntRecord(TIMES_HISTOGRAM_1, 7000)
.expectIntRecordTimes(TIMES_HISTOGRAM_1, 8000, 2)
.build();
// Act
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 6000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 7000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
// Extra record that should break the test
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(TIMES_HISTOGRAM_1, e.getMessage());
assertContains("4 record(s) expected: [6000, 7000, 8000 (2 times)]", e.getMessage());
// Exact histogram buckets can be arbitrary
assertContains("5 record(s) seen: [", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExtraRecordAllowed_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(ENUM_HISTOGRAM, 6)
.expectIntRecord(ENUM_HISTOGRAM, 7)
.expectIntRecordTimes(ENUM_HISTOGRAM, 8, 2)
.allowExtraRecordsForHistogramsAbove()
.build();
// Act
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 6, 10);
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 7, 10);
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 8, 10);
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 8, 10);
// Extra record that should not break the test
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 8, 10);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestExtraRecordAllowed_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(ENUM_HISTOGRAM, 6)
.expectIntRecord(ENUM_HISTOGRAM, 7)
.expectIntRecordTimes(ENUM_HISTOGRAM, 8, 2)
.allowExtraRecordsForHistogramsAbove()
.build();
// Act
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 6, 10);
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 7, 10);
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 8, 10);
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 9, 10);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(ENUM_HISTOGRAM, e.getMessage());
assertContains("4 record(s) expected: [6, 7, 8 (2 times)]", e.getMessage());
// Exact histogram buckets can be arbitrary
assertContains("4 record(s) seen: [6, 7, 8, 9]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExtraRecordAllowedAny_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectAnyRecordTimes(BOOLEAN_HISTOGRAM, 3)
.allowExtraRecordsForHistogramsAbove()
.build();
// Act
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, true);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, true);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestExtraRecordAllowedAny_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectAnyRecordTimes(BOOLEAN_HISTOGRAM, 3)
.allowExtraRecordsForHistogramsAbove()
.build();
// Act
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(BOOLEAN_HISTOGRAM, e.getMessage());
assertContains("3 record(s) expected: [Any (3 times)]", e.getMessage());
assertContains("2 record(s) seen: [0 (2 times)]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestMissingLastRecord_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(TIMES_HISTOGRAM_1, 6000)
.expectIntRecord(TIMES_HISTOGRAM_1, 7000)
.expectIntRecordTimes(TIMES_HISTOGRAM_1, 8000, 2)
.build();
// Act
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 6000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 7000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(TIMES_HISTOGRAM_1, e.getMessage());
assertContains("4 record(s) expected: [6000, 7000, 8000 (2 times)]", e.getMessage());
// Exact histogram buckets can be arbitrary
assertContains("3 record(s) seen: [", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExpectNoRecords_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher = HistogramWatcher.newBuilder().expectNoRecords(ENUM_HISTOGRAM).build();
// Act
RecordHistogram.recordEnumeratedHistogram(ENUM_HISTOGRAM, 5, 10);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(ENUM_HISTOGRAM, e.getMessage());
assertContains("0 record(s) expected: []", e.getMessage());
assertContains("1 record(s) seen: [5]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExpectAnyRecords_missing_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher = HistogramWatcher.newBuilder().expectAnyRecordTimes(BOOLEAN_HISTOGRAM, 3).build();
// Act
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(BOOLEAN_HISTOGRAM, e.getMessage());
assertContains("3 record(s) expected: [Any (3 times)]", e.getMessage());
assertContains("2 record(s) seen: [0 (2 times)]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExpectAnyRecords_extras_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher = HistogramWatcher.newBuilder().expectAnyRecordTimes(BOOLEAN_HISTOGRAM, 3).build();
// Act
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, true);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, true);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(BOOLEAN_HISTOGRAM, e.getMessage());
assertContains("3 record(s) expected: [Any (3 times)]", e.getMessage());
assertContains("4 record(s) seen: [0 (2 times), 1 (2 times)]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExpectAnyRecords_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher = HistogramWatcher.newBuilder().expectAnyRecordTimes(BOOLEAN_HISTOGRAM, 3).build();
// Act
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, false);
RecordHistogram.recordBooleanHistogram(BOOLEAN_HISTOGRAM, true);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestMultipleHistograms_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_1, 5)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_1, 6)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_2, 15)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_2, 16)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_3, 25)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_3, 26)
.build();
// Act
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 5, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_2, 15, 20);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_3, 25, 30);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 6, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_2, 16, 20);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_3, 26, 30);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestMultipleHistograms_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_1, 5)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_1, 6)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_2, 15)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_2, 16)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_3, 25)
.expectIntRecord(EXACT_LINEAR_HISTOGRAM_3, 26)
.build();
// Act
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 5, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_2, 15, 20);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_3, 25, 30);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 6, 10);
// Miss recording EXACT_LINEAR_HISTOGRAM_2 with value 16.
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_3, 26, 30);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(EXACT_LINEAR_HISTOGRAM_2, e.getMessage());
assertContains("2 record(s) expected: [15, 16]", e.getMessage());
assertContains("1 record(s) seen: [15]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestExpectIntRecords_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecords(EXACT_LINEAR_HISTOGRAM_1, 5, 7, 6, 5)
.build();
// Act
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 6, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 5, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 7, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 5, 10);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestExpectIntRecords_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecords(EXACT_LINEAR_HISTOGRAM_1, 5, 7, 6, 5)
.build();
// Act
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 6, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 5, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 7, 10);
// Miss recording EXACT_LINEAR_HISTOGRAM_1 with value 5.
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(EXACT_LINEAR_HISTOGRAM_1, e.getMessage());
assertContains("4 record(s) expected: [5 (2 times), 6, 7]", e.getMessage());
assertContains("3 record(s) seen: [5, 6, 7]", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestIgnoreOtherHistograms_success(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder().expectIntRecord(EXACT_LINEAR_HISTOGRAM_1, 5).build();
// Act
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_1, 5, 10);
RecordHistogram.recordExactLinearHistogram(EXACT_LINEAR_HISTOGRAM_2, 15, 20);
maybeLoadNativeAfterRecord(scenario);
// Assert
mWatcher.assertExpected();
}
protected void doTestMissingFirstRecord_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(TIMES_HISTOGRAM_1, 6000)
.expectIntRecord(TIMES_HISTOGRAM_1, 7000)
.expectIntRecord(TIMES_HISTOGRAM_1, 8000)
.build();
// Act
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 7000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(TIMES_HISTOGRAM_1, e.getMessage());
assertContains("3 record(s) expected: [6000, 7000, 8000]", e.getMessage());
// Exact histogram buckets can be arbitrary
assertContains("2 record(s) seen: [", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void doTestMissingMiddleRecord_failure(@TestScenario int scenario) {
// Arrange
maybeLoadNativeFirst(scenario);
mWatcher =
HistogramWatcher.newBuilder()
.expectIntRecord(TIMES_HISTOGRAM_1, 6000)
.expectIntRecord(TIMES_HISTOGRAM_1, 7000)
.expectIntRecord(TIMES_HISTOGRAM_1, 8000)
.build();
// Act
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 6000);
RecordHistogram.recordTimesHistogram(TIMES_HISTOGRAM_1, 8000);
maybeLoadNativeAfterRecord(scenario);
// Assert
try {
mWatcher.assertExpected();
} catch (AssertionError e) {
assertContains(TIMES_HISTOGRAM_1, e.getMessage());
assertContains("3 record(s) expected: [6000, 7000, 8000]", e.getMessage());
// Exact histogram buckets can be arbitrary
assertContains("2 record(s) seen: [", e.getMessage());
return;
}
Assert.fail("Expected AssertionError");
}
protected void maybeLoadNativeAfterRecord(int scenario) {
if (scenario == TestScenario.TRANSITION_TO_NATIVE) {
LibraryLoader.getInstance().ensureInitialized();
}
}
protected void maybeLoadNativeFirst(int scenario) {
if (scenario == TestScenario.WITH_NATIVE) {
LibraryLoader.getInstance().ensureInitialized();
}
}
protected static void assertContains(String expectedSubstring, String actualString) {
Assert.assertNotNull(actualString);
if (!actualString.contains(expectedSubstring)) {
Assert.fail(
String.format(
"Substring <%s> not found in string <%s>",
expectedSubstring, actualString));
}
}
}