blob: 25f81ad24feceb52f98461ef52842bfe3de47534 [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 "content/browser/scheduler/responsiveness/calculator.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace responsiveness {
namespace {
// Copied from calculator.cc.
constexpr int kMeasurementIntervalInMs = 30 * 1000;
constexpr int kJankThresholdInMs = 100;
class FakeCalculator : public Calculator {
public:
std::vector<int>& Emissions() { return janky_slices_; }
void EmitResponsiveness(int janky_slices) override {
janky_slices_.push_back(janky_slices);
}
using Calculator::GetLastCalculationTime;
private:
std::vector<int> janky_slices_;
};
} // namespace
class ResponsivenessCalculatorTest : public testing::Test {
public:
void SetUp() override {
calculator_ = std::make_unique<FakeCalculator>();
last_calculation_time_ = calculator_->GetLastCalculationTime();
}
void AddEventUI(int schedule_time_in_ms, int finish_time_in_ms) {
calculator_->TaskOrEventFinishedOnUIThread(
last_calculation_time_ +
base::TimeDelta::FromMilliseconds(schedule_time_in_ms),
last_calculation_time_ +
base::TimeDelta::FromMilliseconds(finish_time_in_ms));
}
void AddEventIO(int schedule_time_in_ms, int finish_time_in_ms) {
calculator_->TaskOrEventFinishedOnIOThread(
last_calculation_time_ +
base::TimeDelta::FromMilliseconds(schedule_time_in_ms),
last_calculation_time_ +
base::TimeDelta::FromMilliseconds(finish_time_in_ms));
}
void TriggerCalculation() {
AddEventUI(kMeasurementIntervalInMs + 1, kMeasurementIntervalInMs + 2);
last_calculation_time_ = calculator_->GetLastCalculationTime();
}
protected:
// This member sets up BrowserThread::IO and BrowserThread::UI. It must be the
// first member, as other members may depend on these abstractions.
content::TestBrowserThreadBundle test_browser_thread_bundle_;
std::unique_ptr<FakeCalculator> calculator_;
base::TimeTicks last_calculation_time_;
};
// A single event of length slightly longer than kJankThresholdInMs.
TEST_F(ResponsivenessCalculatorTest, ShortJank) {
AddEventUI(40, 40 + kJankThresholdInMs + 5);
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(1, calculator_->Emissions()[0]);
}
// A single event of length slightly longer than 10 * kJankThresholdInMs.
TEST_F(ResponsivenessCalculatorTest, LongJank) {
AddEventUI(40, 40 + 10 * kJankThresholdInMs + 5);
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(10, calculator_->Emissions()[0]);
}
// Events that last less than 100ms do not jank, regardless of start time.
TEST_F(ResponsivenessCalculatorTest, NoJank) {
int base_time = 30;
for (int i = 0; i < kJankThresholdInMs; ++i) {
AddEventUI(base_time, base_time + i);
}
base_time += kJankThresholdInMs;
for (int i = 0; i < kJankThresholdInMs; ++i) {
AddEventUI(base_time + i, base_time + 2 * i);
}
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(0, calculator_->Emissions()[0]);
}
// 10 Jank events, but very closely overlapping. Time slices are discretized and
// fixed, e.g. [0 100] [100 200] [200 300]. In this test, the events all start
// in the [0 100] slice and end in the [100 200] slice. All of them end up
// marking the [100 200] slice as janky.
TEST_F(ResponsivenessCalculatorTest, OverlappingJank) {
int base_time = 30;
for (int i = 0; i < 10; ++i) {
AddEventUI(base_time, base_time + kJankThresholdInMs + 10);
}
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(1, calculator_->Emissions()[0]);
}
// UI thread has 3 jank events on slices 1, 2, 3
// IO thread has 3 jank events on slices 3, 4, 5,
// There should be a total of 5 jank events.
TEST_F(ResponsivenessCalculatorTest, OverlappingJankMultipleThreads) {
int base_time = 105;
for (int i = 0; i < 3; ++i) {
AddEventUI(base_time + i * kJankThresholdInMs,
base_time + (i + 1) * kJankThresholdInMs + 10);
}
base_time = 305;
for (int i = 0; i < 3; ++i) {
AddEventIO(base_time + i * kJankThresholdInMs,
base_time + (i + 1) * kJankThresholdInMs + 10);
}
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(5, calculator_->Emissions()[0]);
}
// Three janks, each of length 2, separated by some shorter events.
TEST_F(ResponsivenessCalculatorTest, SeparatedJanks) {
int base_time = 105;
for (int i = 0; i < 3; ++i) {
AddEventUI(base_time, base_time + 1);
AddEventUI(base_time, base_time + 2 * kJankThresholdInMs + 1);
base_time += 10 * kJankThresholdInMs;
}
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(6, calculator_->Emissions()[0]);
}
TEST_F(ResponsivenessCalculatorTest, MultipleTrigger) {
int base_time = 105;
// 3 Janks, then trigger, then repeat.
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 3; ++j) {
AddEventUI(base_time, base_time + 3 * kJankThresholdInMs + 1);
base_time += 3 * kJankThresholdInMs;
}
TriggerCalculation();
}
ASSERT_EQ(10u, calculator_->Emissions().size());
for (int i = 0; i < 10; ++i) {
EXPECT_EQ(9, calculator_->Emissions()[i]);
}
}
// A long delay means that the machine likely went to sleep.
TEST_F(ResponsivenessCalculatorTest, LongDelay) {
int base_time = 105;
AddEventUI(base_time, base_time + 3 * kJankThresholdInMs + 1);
base_time += 10 * kMeasurementIntervalInMs;
AddEventUI(base_time, base_time + 1);
ASSERT_EQ(0u, calculator_->Emissions().size());
}
// A long event means that the machine likely went to sleep.
TEST_F(ResponsivenessCalculatorTest, LongEvent) {
int base_time = 105;
AddEventUI(base_time, base_time + 10 * kMeasurementIntervalInMs);
ASSERT_EQ(0u, calculator_->Emissions().size());
}
// An event that crosses a measurement interval boundary should count towards
// both measurement intervals.
TEST_F(ResponsivenessCalculatorTest, EventCrossesBoundary) {
// Dummy event so that Calculator doesn't think the process is suspended.
AddEventUI(0.5 * kMeasurementIntervalInMs, 0.5 * kMeasurementIntervalInMs);
// The event goes from [29801, 30150]. It should count as 1 jank in the first
// measurement interval and 2 in the second.
AddEventUI(kMeasurementIntervalInMs - 2 * kJankThresholdInMs + 1,
kMeasurementIntervalInMs + 1.5 * kJankThresholdInMs);
// Dummy event so that Calculator doesn't think the process is suspended.
AddEventUI(1.5 * kMeasurementIntervalInMs, 1.5 * kMeasurementIntervalInMs);
// Trigger another calculation.
AddEventUI(2 * kMeasurementIntervalInMs + 1,
2 * kMeasurementIntervalInMs + 1);
ASSERT_EQ(2u, calculator_->Emissions().size());
EXPECT_EQ(1, calculator_->Emissions()[0]);
EXPECT_EQ(2, calculator_->Emissions()[1]);
}
// Events may not be ordered by start or end time.
TEST_F(ResponsivenessCalculatorTest, UnorderedEvents) {
// We add the following events:
// [100, 250]
// [150, 300]
// [50, 200]
// [50, 390]
// The event [50, 400] subsumes all previous events.
AddEventUI(kJankThresholdInMs, 2.5 * kJankThresholdInMs);
AddEventUI(1.5 * kJankThresholdInMs, 3 * kJankThresholdInMs);
AddEventUI(0.5 * kJankThresholdInMs, 2 * kJankThresholdInMs);
AddEventUI(0.5 * kJankThresholdInMs, 3.9 * kJankThresholdInMs);
TriggerCalculation();
ASSERT_EQ(1u, calculator_->Emissions().size());
EXPECT_EQ(3, calculator_->Emissions()[0]);
}
} // namespace responsiveness
} // namespace content