[go: nahoru, domu]

blob: 018e78b4ce95518782fbe5834a813ea20d113431 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/metrics/frame_sorter.h"
#include <string>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/strings/string_number_conversions.h"
#include "cc/metrics/frame_info.h"
#include "cc/test/fake_frame_info.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
// Test class for FrameSorter
class FrameSorterTest : public testing::Test {
public:
FrameSorterTest()
: frame_sorter_(base::BindRepeating(&FrameSorterTest::FlushFrame,
base::Unretained(this))) {
IncreaseSourceId();
}
~FrameSorterTest() override = default;
const viz::BeginFrameArgs GetNextFrameArgs() {
uint64_t sequence_number = next_frame_sequence_number_++;
return viz::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, next_frame_source_id_, sequence_number,
last_begin_frame_time_,
last_begin_frame_time_ + viz::BeginFrameArgs::DefaultInterval(),
viz::BeginFrameArgs::DefaultInterval(), viz::BeginFrameArgs::NORMAL);
}
void IncreaseSourceId() {
next_frame_source_id_++;
current_frame_id_ = -1;
args_.clear();
next_frame_sequence_number_ = viz::BeginFrameArgs::kStartingFrameNumber;
}
// Simulates a sequence of queries
// Possible queries:
// "S{frame_number}": Start frame.
// "D{frame_number}": End frame as dropped.
// "P{frame_number}": End frame as none-dropped (presented).
// "E{frame_number}": Frame expects twp acks.
// "I": Increase source_id (simulating a crash).
// "R": Reset the frame sorter.
// Method expects the start of frames to be in order starting with 1.
void SimulateQueries(std::vector<std::string> queries) {
// Keeps track of how many times a frame is terminated.
std::map<int, int> end_counters;
for (auto& query : queries) {
int id;
base::StringToInt(query.substr(1), &id);
if (id > current_frame_id_) {
current_frame_id_++;
args_.push_back(GetNextFrameArgs());
}
switch (query[0]) {
case 'S':
frame_sorter_.AddNewFrame(args_[id]);
break;
case 'D': {
++end_counters[id];
FrameInfo info =
CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped);
if (end_counters[id] == 1) {
// For the first response to the frame, mark it as not including
// update from the main-thread.
info.main_thread_response = FrameInfo::MainThreadResponse::kMissing;
} else {
DCHECK_EQ(2, end_counters[id]);
info.main_thread_response =
FrameInfo::MainThreadResponse::kIncluded;
}
frame_sorter_.AddFrameResult(args_[id], info);
break;
}
case 'P': {
++end_counters[id];
FrameInfo info =
CreateFakeFrameInfo(FrameInfo::FrameFinalState::kPresentedAll);
if (end_counters[id] == 1) {
// For the first response to the frame, mark it as not including
// update from the main-thread.
info.main_thread_response = FrameInfo::MainThreadResponse::kMissing;
} else {
DCHECK_EQ(2, end_counters[id]);
info.main_thread_response =
FrameInfo::MainThreadResponse::kIncluded;
}
frame_sorter_.AddFrameResult(args_[id], info);
break;
}
case 'I':
IncreaseSourceId();
break;
case 'R':
frame_sorter_.Reset();
break;
}
}
}
void ValidateResults(
std::vector<std::pair<uint64_t, bool>> expected_results) {
EXPECT_EQ(sorted_frames_.size(), expected_results.size());
int result_index = 0;
for (auto result : expected_results) {
auto& sorted_frame = sorted_frames_[result_index];
EXPECT_EQ(sorted_frame.first.frame_id.sequence_number, result.first + 1);
EXPECT_EQ(sorted_frame.second, result.second);
result_index++;
}
}
private:
void FlushFrame(const viz::BeginFrameArgs& args, const FrameInfo& frame) {
sorted_frames_.emplace_back(args, frame.IsDroppedAffectingSmoothness());
}
FrameSorter frame_sorter_;
std::vector<std::pair<const viz::BeginFrameArgs, bool>> sorted_frames_;
base::TimeTicks last_begin_frame_time_ = base::TimeTicks::Now();
uint64_t next_frame_source_id_ = 0;
uint64_t next_frame_sequence_number_ =
viz::BeginFrameArgs::kStartingFrameNumber;
std::vector<const viz::BeginFrameArgs> args_ = {};
int current_frame_id_ = -1;
};
TEST_F(FrameSorterTest, TestSortingFrames) {
// Frame end in order of F0, F2, F1, F3, but they should be flushed in the
// order they began (eg. F0, F1, F2, F3).
std::vector<std::string> queries = {"S0", "S1", "P0", "S2",
"P2", "S3", "P1", "D3"};
std::vector<std::pair<uint64_t, bool>> expected_results = {
{0, false}, {1, false}, {2, false}, {3, true}};
SimulateQueries(queries);
ValidateResults(expected_results);
}
TEST_F(FrameSorterTest, ResetInMiddleOfAcks) {
// Reset occur in between the start and ack of F1 & F3, so except these two
// all other frames should be flushed in order in the sorted_frames_.
std::vector<std::string> queries = {"S0", "S1", "P0", "S2", "P2", "S3",
"R", "S4", "P1", "D4", "D3"};
std::vector<std::pair<uint64_t, bool>> expected_results = {
{0, false}, {2, false}, {4, true}};
SimulateQueries(queries);
ValidateResults(expected_results);
}
TEST_F(FrameSorterTest, ExpectingMultipleAcks) {
// F1 has multiple acks and the final ack is received at the end. Which makes
// other frames to wait in pending tree before being flushed, all in order.
// Also in any of the duplicated acks report a dropped frame, the overall
// status for that frame should be dropped.
std::vector<std::string> queries = {"S0", "S1", "P0", "S1", "S2", "P2",
"S3", "S4", "P1", "D3", "D4", "D1"};
std::vector<std::pair<uint64_t, bool>> expected_results = {
{0, false}, {1, true}, {2, false}, {3, true}, {4, true}};
SimulateQueries(queries);
ValidateResults(expected_results);
}
TEST_F(FrameSorterTest, ExpectingMultipleAcksWithReset) {
// Combination of last two tests. Reset occurs in middle of start and ack of
// F1 and F3. Also F1 expects two acks, which both are received after reset.
std::vector<std::string> queries = {"S0", "S1", "P0", "S1", "S2", "P2", "S3",
"R", "S4", "P1", "D1", "D4", "D3"};
std::vector<std::pair<uint64_t, bool>> expected_results = {
{0, false}, {2, false}, {4, true}};
SimulateQueries(queries);
ValidateResults(expected_results);
}
TEST_F(FrameSorterTest, ExpectingMultipleAcksWithReset2) {
// Reset occurs in middle of the two starts of F0.
// This is to test if F1 will be flushed properly while being in between the
// two acks of F0, which should be ignored as a result of reset.
std::vector<std::string> queries = {"S0", "R", "S0", "P0", "S1", "D0", "D1"};
std::vector<std::pair<uint64_t, bool>> expected_results = {{1, true}};
SimulateQueries(queries);
ValidateResults(expected_results);
}
TEST_F(FrameSorterTest, ExpectingMultipleAcksWithSourceIdIncrease) {
// Reset and increase in source_id occurs in middle start and ack of F1.
// This is to simulate a navigation. So the initial F0 has source_id of 0
// while the F0 after reset has source_id of 1, and because of that the
// start and ack for F0 after the reset is not ignored (The ignore is only
// needed when frame has the same source_id and sequence_number).
std::vector<std::string> queries = {"S0", "S0", "S1", "S1", "I", "R",
"S0", "S0", "P0", "S1", "P1", "D0"};
std::vector<std::pair<uint64_t, bool>> expected_results = {{0, true},
{1, false}};
SimulateQueries(queries);
ValidateResults(expected_results);
}
} // namespace cc