blob: c19eb4b3e28b889530681a7ccb38ed95ab7bbb64 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/latency/histograms.h"
#include <algorithm>
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/sample_vector.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/latency/fixed_point.h"
#include "ui/latency/frame_metrics_test_common.h"
namespace ui {
namespace frame_metrics {
// Verifies the ratio boundaries generated internally match the reference
// boundaries.
TEST(FrameMetricsHistogramsTest, RatioBoundariesDirect) {
const TestRatioBoundaries kTestRatioBoundaries;
std::unique_ptr<BoundaryIterator> ratio_impl =
CreateRatioIteratorForTesting();
for (uint32_t boundary : kTestRatioBoundaries.boundaries) {
if (boundary == 0)
continue;
EXPECT_EQ(boundary, ratio_impl->Next());
}
}
// Verifies the VSync boundaries generated internally match the reference
// boundaries.
TEST(FrameMetricsHistogramsTest, VSyncBoundariesDirect) {
std::unique_ptr<BoundaryIterator> vsync_impl =
CreateVSyncIteratorForTesting();
for (uint32_t boundary : kTestVSyncBoundries) {
if (boundary == 0)
continue;
EXPECT_EQ(boundary, vsync_impl->Next());
}
}
// Results should be 0 if no samples have been added yet.
TEST(FrameMetricsHistogramsTest, ResultsAreZeroWithoutSamples) {
RatioHistogram ratio_histogram;
EXPECT_EQ(0, ratio_histogram.ComputePercentiles().values[0]);
EXPECT_EQ(0, ratio_histogram.ComputePercentiles().values[1]);
VSyncHistogram vsync_histogram;
EXPECT_EQ(0, vsync_histogram.ComputePercentiles().values[0]);
EXPECT_EQ(0, vsync_histogram.ComputePercentiles().values[1]);
}
// A non-zero value implies samples were added since, even if those samples
// were zero, they would go into the [0,N) bucket and result in a non-zero
// estimate.
TEST(FrameMetricsHistogramsTest, ResultsAreNonZeroWithSamplesOfZero) {
RatioHistogram ratio_histogram;
ratio_histogram.AddSample(0, 1);
EXPECT_LT(0, ratio_histogram.ComputePercentiles().values[0]);
EXPECT_LT(0, ratio_histogram.ComputePercentiles().values[1]);
VSyncHistogram vsync_histogram;
vsync_histogram.AddSample(0, 1);
EXPECT_LT(0, vsync_histogram.ComputePercentiles().values[0]);
EXPECT_LT(0, vsync_histogram.ComputePercentiles().values[1]);
}
template <typename ReferenceBoundaryT>
void BoundaryTestCommon(const ReferenceBoundaryT& reference_boundaries,
std::unique_ptr<Histogram> histogram) {
PercentileResults percentiles;
for (size_t i = 0; i < reference_boundaries.size() - 1; i++) {
uint64_t bucket_start = reference_boundaries[i];
uint64_t bucket_end = reference_boundaries[i + 1];
// Verify values within the current bucket don't affect percentile.
// This also checks the first value in the bucket.
uint32_t stride = std::max<uint32_t>(1u, (bucket_end - bucket_start) / 8);
for (uint64_t value = bucket_start; value < bucket_end; value += stride) {
histogram->AddSample(value, 1);
percentiles = histogram->ComputePercentiles();
histogram->Reset();
EXPECT_LE(bucket_start, percentiles.values[0]);
EXPECT_GT(bucket_end, percentiles.values[0]);
}
// Verify the value just before the next bucket doesn't affect percentile.
histogram->AddSample(bucket_end - 1, 1);
percentiles = histogram->ComputePercentiles();
histogram->Reset();
EXPECT_LE(bucket_start, percentiles.values[0]);
EXPECT_GT(bucket_end, percentiles.values[0]);
}
}
TEST(FrameMetricsHistogramsTest, RatioBoundaries) {
const TestRatioBoundaries kTestRatioBoundaries;
BoundaryTestCommon(kTestRatioBoundaries, std::make_unique<RatioHistogram>());
}
TEST(FrameMetricsHistogramsTest, VSyncBoundaries) {
const TestRatioBoundaries kTestRatioBoundaries;
BoundaryTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>());
}
template <typename ReferenceBoundaryT>
void PercentilesTestCommon(const ReferenceBoundaryT& reference_boundaries,
std::unique_ptr<Histogram> histogram,
int percentile_index) {
double percentile = PercentileResults::kPercentiles[percentile_index];
PercentileResults percentiles;
for (size_t i = 0; i < reference_boundaries.size() - 1; i++) {
uint64_t bucket_start = reference_boundaries[i];
uint64_t bucket_end = reference_boundaries[i + 1];
// Add samples to current bucket.
// Where the samples are added in the current bucket should not affect the
// result.
uint32_t stride = std::max<uint32_t>(1u, (bucket_end - bucket_start) / 100);
int samples_added_inside = 0;
for (uint64_t value = bucket_start; value < bucket_end; value += stride) {
histogram->AddSample(value, 10);
samples_added_inside += 10;
}
// Add samples to left and right of current bucket.
// Don't worry about doing this for the left most and right most buckets.
int samples_added_left = 0;
int samples_added_outside = 0;
if (i != 0 && i < reference_boundaries.size() - 2) {
samples_added_outside = 10000;
samples_added_left = samples_added_outside * percentile;
histogram->AddSample(bucket_start / 3, samples_added_left);
histogram->AddSample(bucket_start * 3,
samples_added_outside - samples_added_left);
}
percentiles = histogram->ComputePercentiles();
histogram->Reset();
double index = (samples_added_inside + samples_added_outside) * percentile -
samples_added_left;
double w = index / samples_added_inside;
double expected_value = bucket_end * w + bucket_start * (1.0 - w);
EXPECT_DOUBLE_EQ(expected_value, percentiles.values[percentile_index]);
}
}
TEST(FrameMetricsHistogramsTest, RatioPercentiles50th) {
const TestRatioBoundaries kTestRatioBoundaries;
PercentilesTestCommon(kTestRatioBoundaries,
std::make_unique<RatioHistogram>(), 0);
}
TEST(FrameMetricsHistogramsTest, RatioPercentiles99th) {
const TestRatioBoundaries kTestRatioBoundaries;
PercentilesTestCommon(kTestRatioBoundaries,
std::make_unique<RatioHistogram>(), 1);
}
TEST(FrameMetricsHistogramsTest, VSyncPercentiles50th) {
const TestRatioBoundaries kTestRatioBoundaries;
PercentilesTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>(),
0);
}
TEST(FrameMetricsHistogramsTest, VSyncPercentiles99th) {
const TestRatioBoundaries kTestRatioBoundaries;
PercentilesTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>(),
1);
}
} // namespace frame_metrics
} // namespace ui