blob: d83c34319b82bb84a7b5be287f2b562477405fa7 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.postprocessor;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.MockitoAnnotations.initMocks;
import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.postprocessor.PerfettoGenericPostProcessor.METRIC_FILE_FORMAT;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.LogFile;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.ZipUtil;
import com.google.protobuf.TextFormat;
import com.google.protobuf.TextFormat.ParseException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import perfetto.protos.PerfettoMergedMetrics.TraceMetrics;
/** Unit tests for {@link PerfettoGenericPostProcessor}. */
@RunWith(JUnit4.class)
public class PerfettoGenericPostProcessorTest {
@Mock private ITestInvocationListener mListener;
private PerfettoGenericPostProcessor mProcessor;
private OptionSetter mOptionSetter;
private static final String PREFIX_OPTION = "perfetto-proto-file-prefix";
private static final String PREFIX_OPTION_VALUE = "metric-perfetto";
private static final String INDEX_OPTION = "perfetto-indexed-list-field";
private static final String KEY_PREFIX_OPTION = "perfetto-prefix-key-field";
private static final String REGEX_OPTION_VALUE = "perfetto-metric-filter-regex";
private static final String ALL_METRICS_OPTION = "perfetto-include-all-metrics";
private static final String ALL_METRICS_PREFIX_OPTION = "perfetto-all-metric-prefix";
private static final String REPLACE_REGEX_OPTION = "perfetto-metric-replace-prefix";
private static final String FILE_FORMAT_OPTION = "trace-processor-output-format";
File perfettoMetricProtoFile = null;
@Before
public void setUp() throws ConfigurationException {
initMocks(this);
mProcessor = new PerfettoGenericPostProcessor();
mProcessor.init(mListener);
mOptionSetter = new OptionSetter(mProcessor);
}
/**
* Test metrics count should be zero if "perfetto-include-all-metrics" is not set or set to
* false;
*/
@Test
public void testNoMetricsByDefault() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(INDEX_OPTION, "perfetto.protos.AndroidStartupMetric.startup");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertTrue(
"Number of metrics parsed without indexing is incorrect.",
parsedMetrics.size() == 0);
}
/**
* Test metrics are filtered correctly when filter regex are passed and
* "perfetto-include-all-metrics" is set to false (Note: by default false)
*/
@Test
public void testMetricsFilterWithRegEx() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(INDEX_OPTION, "perfetto.protos.AndroidStartupMetric.startup");
mOptionSetter.setOptionValue(REGEX_OPTION_VALUE, "android_startup-startup-1.*");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-1-startup_id", 1);
assertMetricsContain(
parsedMetrics,
"perfetto_android_startup-startup-1-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
36175473);
}
/**
* Test metrics are filtered correctly when filter regex are passed and
* prefix are replaced with the given string.
*/
@Test
public void testMetricsFilterWithRegExAndReplacePrefix()
throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(INDEX_OPTION, "perfetto.protos.AndroidStartupMetric.startup");
mOptionSetter.setOptionValue(REGEX_OPTION_VALUE, "android_startup-startup-1.*");
mOptionSetter.setOptionValue(REPLACE_REGEX_OPTION, "android_startup-startup-1",
"newprefix");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics = mProcessor
.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertFalse("Metric key not expected but found",
parsedMetrics.containsKey("android_startup-startup-1-startup_id"));
assertMetricsContain(parsedMetrics, "perfetto_newprefix-startup_id", 1);
assertMetricsContain(
parsedMetrics,
"perfetto_newprefix-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
36175473);
}
/**
* Test all metrics are included when "perfetto-include-all-metrics" is set to true and ignores
* any of the filter regex set.
*/
@Test
public void testAllMetricsOptionIgnoresFilter() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(INDEX_OPTION, "perfetto.protos.AndroidStartupMetric.startup");
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
// Test for non startup metrics exists.
assertMetricsContain(
parsedMetrics,
"perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
/** Test that the post processor can parse reports from test metrics. */
@Test
public void testParsingTestMetrics() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processTestMetricsAndLogs(
new TestDescription("class", "test"), new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
"perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
/** Test custom all metric suffix is applied correctly. */
@Test
public void testParsingWithAllMetricsPrefix() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
mOptionSetter.setOptionValue(ALL_METRICS_PREFIX_OPTION, "custom_all_prefix");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processTestMetricsAndLogs(
new TestDescription("class", "test"), new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
"custom_all_prefix_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
/** Test the post processor can parse reports from run metrics. */
@Test
public void testParsingRunMetrics() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
"perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
/**
* Test metrics count and metrics without indexing. In case of app startup metrics startup
* messages for same package name will be overridden without indexing.
*/
@Test
public void testParsingWithoutIndexing() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-startup_id", 2);
assertMetricsContain(
parsedMetrics,
"perfetto_android_startup-startup-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
53102401);
}
/**
* Test metrics count and metrics with indexing. In case of app startup metrics, startup
* messages for same package name will not be overridden with indexing.
*/
@Test
public void testParsingWithIndexing() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(INDEX_OPTION, "perfetto.protos.AndroidStartupMetric.startup");
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-1-startup_id", 1);
assertMetricsContain(
parsedMetrics,
"perfetto_android_startup-startup-1-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
36175473);
assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-2-startup_id", 2);
assertMetricsContain(
parsedMetrics,
"perfetto_android_startup-startup-2-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
53102401);
}
/**
* Test metrics enabled with key prefixing.
*/
@Test
public void testParsingWithKeyPrefixing() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(KEY_PREFIX_OPTION,
"perfetto.protos.ProcessRenderInfo.process_name");
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(parsedMetrics,
"perfetto_android_hwui_metric-process_info-process_name-com.android.systemui-all_mem_min",
15120269);
}
/** Test the post processor can parse binary perfetto metric proto format. */
@Test
public void testParsingBinaryProto() throws ConfigurationException, IOException {
setupPerfettoMetricFile(METRIC_FILE_FORMAT.binary, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
mOptionSetter.setOptionValue(FILE_FORMAT_OPTION, "binary");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.PB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
"perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
/** Test the post processor can parse binary perfetto metric proto format. */
@Test
public void testNoSupportForJsonParsing() throws ConfigurationException, IOException {
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
mOptionSetter.setOptionValue(FILE_FORMAT_OPTION, "json");
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertTrue("Should not have any metrics if json format is set", parsedMetrics.size() == 0);
}
/**
* Test the post processor can parse reports from run metrics when the text proto file is
* compressed format.
*/
@Test
public void testParsingRunMetricsWithCompressedFile()
throws ConfigurationException, IOException {
// Setup compressed text proto metric file.
setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
Map<String, LogFile> testLogs = new HashMap<>();
testLogs.put(
PREFIX_OPTION_VALUE,
new LogFile(
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
"perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
/** Creates sample perfetto metric proto file used for testing. */
private File setupPerfettoMetricFile(METRIC_FILE_FORMAT format, boolean isCompressed)
throws IOException {
String perfettoTextContent =
"android_mem {\n"
+ " process_metrics {\n"
+ " process_name: \".dataservices\"\n"
+ " total_counters {\n"
+ " anon_rss {\n"
+ " min: 27938816\n"
+ " max: 27938816\n"
+ " avg: 27938816\n"
+ " }\n"
+ " file_rss {\n"
+ " min: 62390272\n"
+ " max: 62390272\n"
+ " avg: 62390272\n"
+ " }\n"
+ " swap {\n"
+ " min: 0\n"
+ " max: 0\n"
+ " avg: 0\n"
+ " }\n"
+ " anon_and_swap {\n"
+ " min: 27938816\n"
+ " max: 27938816\n"
+ " avg: 27938816\n"
+ " }\n"
+ " }\n"
+ "}}"
+ "android_startup {\n"
+ " startup {\n"
+ " startup_id: 1\n"
+ " package_name: \"com.google.android.apps.nexuslauncher\"\n"
+ " process_name: \"com.google.android.apps.nexuslauncher\"\n"
+ " zygote_new_process: false\n"
+ " to_first_frame {\n"
+ " dur_ns: 36175473\n"
+ " main_thread_by_task_state {\n"
+ " running_dur_ns: 11496200\n"
+ " runnable_dur_ns: 487290\n"
+ " uninterruptible_sleep_dur_ns: 0\n"
+ " interruptible_sleep_dur_ns: 23645107\n"
+ " }\n"
+ " other_processes_spawned_count: 0\n"
+ " time_activity_manager {\n"
+ " dur_ns: 4135001\n"
+ " }\n"
+ " time_activity_resume {\n"
+ " dur_ns: 345105\n"
+ " }\n"
+ " time_choreographer {\n"
+ " dur_ns: 15314324\n"
+ " }\n"
+ " }\n"
+ " activity_hosting_process_count: 1\n"
+ " }\n"
+ " startup {\n"
+ " startup_id: 2\n"
+ " package_name: \"com.google.android.apps.nexuslauncher\"\n"
+ " process_name: \"com.google.android.apps.nexuslauncher\"\n"
+ " zygote_new_process: false\n"
+ " to_first_frame {\n"
+ " dur_ns: 53102401\n"
+ " main_thread_by_task_state {\n"
+ " running_dur_ns: 9766774\n"
+ " runnable_dur_ns: 320103\n"
+ " uninterruptible_sleep_dur_ns: 0\n"
+ " interruptible_sleep_dur_ns: 42358858\n"
+ " }\n"
+ " other_processes_spawned_count: 0\n"
+ " time_activity_manager {\n"
+ " dur_ns: 4742396\n"
+ " }\n"
+ " time_activity_resume {\n"
+ " dur_ns: 280208\n"
+ " }\n"
+ " time_choreographer {\n"
+ " dur_ns: 13705366\n"
+ " }\n"
+ " }\n"
+ " activity_hosting_process_count: 1\n"
+ " }\n"
+ "}\n"
+ "android_hwui_metric {\n" +
" process_info {\n" +
" process_name: \"com.android.systemui\"\n" +
" rt_cpu_time_ms: 2481\n" +
" draw_frame_count: 889\n" +
" draw_frame_max: 21990523\n" +
" draw_frame_min: 660573\n" +
" draw_frame_avg: 3515215.0101237344\n" +
" flush_count: 884\n" +
" flush_max: 8101094\n" +
" flush_min: 127760\n" +
" flush_avg: 773943.91515837109\n" +
" prepare_tree_count: 889\n" +
" prepare_tree_max: 1718593\n" +
" prepare_tree_min: 25052\n" +
" prepare_tree_avg: 133403.03374578178\n" +
" gpu_completion_count: 572\n" +
" gpu_completion_max: 8600365\n" +
" gpu_completion_min: 3594\n" +
" gpu_completion_avg: 737765.40209790214\n" +
" ui_record_count: 889\n" +
" ui_record_max: 7079949\n" +
" ui_record_min: 4583\n" +
" ui_record_avg: 477551.82902137231\n" +
" graphics_cpu_mem_max: 265242\n" +
" graphics_cpu_mem_min: 244198\n" +
" graphics_cpu_mem_avg: 260553.33484162897\n" +
" graphics_gpu_mem_max: 34792176\n" +
" graphics_gpu_mem_min: 9855728\n" +
" graphics_gpu_mem_avg: 19030174.914027151\n" +
" texture_mem_max: 5217091\n" +
" texture_mem_min: 5020343\n" +
" texture_mem_avg: 5177376.0407239823\n" +
" all_mem_max: 40274509\n" +
" all_mem_min: 15120269\n" +
" all_mem_avg: 24468104.289592762\n" +
" }\n" +
"}";
FileWriter fileWriter = null;
try {
perfettoMetricProtoFile = FileUtil.createTempFile("metric_perfetto", "");
fileWriter = new FileWriter(perfettoMetricProtoFile);
fileWriter.write(perfettoTextContent);
} finally {
if (fileWriter != null) {
fileWriter.close();
}
}
if (format.equals(METRIC_FILE_FORMAT.binary)) {
File perfettoBinaryFile = FileUtil.createTempFile("metric_perfetto_binary", ".pb");
try (BufferedReader bufferedReader =
new BufferedReader(new FileReader(perfettoMetricProtoFile))) {
TraceMetrics.Builder builder = TraceMetrics.newBuilder();
TextFormat.merge(bufferedReader, builder);
builder.build().writeTo(new FileOutputStream(perfettoBinaryFile));
} catch (ParseException e) {
CLog.e("Failed to merge the perfetto metric file." + e.getMessage());
} catch (IOException ioe) {
CLog.e(
"IOException happened when reading the perfetto metric file."
+ ioe.getMessage());
} finally {
perfettoMetricProtoFile.delete();
perfettoMetricProtoFile = perfettoBinaryFile;
}
return perfettoMetricProtoFile;
}
if (isCompressed) {
perfettoMetricProtoFile = compressFile(perfettoMetricProtoFile);
}
return perfettoMetricProtoFile;
}
/** Create a zip file with perfetto metric proto file */
private File compressFile(File decompressedFile) throws IOException {
File compressedFile = FileUtil.createTempFile("compressed_temp", ".zip");
try {
ZipUtil.createZip(decompressedFile, compressedFile);
} catch (IOException ioe) {
CLog.e("Unable to gzip the file.");
} finally {
decompressedFile.delete();
}
return compressedFile;
}
@After
public void teardown() {
if (perfettoMetricProtoFile != null) {
perfettoMetricProtoFile.delete();
}
}
/** Assert that metrics contain a key and a corresponding value. */
private void assertMetricsContain(
Map<String, Metric.Builder> metrics, String key, Object value) {
assertTrue(
String.format(
"Metric with key containing %s and value %s was expected but not found.",
key, value),
metrics.entrySet()
.stream()
.anyMatch(
e ->
e.getKey().contains(key)
&& String.valueOf(value)
.equals(
e.getValue()
.build()
.getMeasurements()
.getSingleString())));
}
}