1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/metrics/frame_sorter.h"
11 #include "base/bind.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "cc/metrics/frame_info.h"
14 #include "cc/test/fake_frame_info.h"
15 #include "testing/gtest/include/gtest/gtest.h"
19 // Test class for FrameSorter
20 class FrameSorterTest : public testing::Test {
23 : frame_sorter_(base::BindRepeating(&FrameSorterTest::FlushFrame,
24 base::Unretained(this))) {
27 ~FrameSorterTest() override = default;
29 const viz::BeginFrameArgs GetNextFrameArgs() {
30 uint64_t sequence_number = next_frame_sequence_number_++;
31 return viz::BeginFrameArgs::Create(
32 BEGINFRAME_FROM_HERE, next_frame_source_id_, sequence_number,
33 last_begin_frame_time_,
34 last_begin_frame_time_ + viz::BeginFrameArgs::DefaultInterval(),
35 viz::BeginFrameArgs::DefaultInterval(), viz::BeginFrameArgs::NORMAL);
38 void IncreaseSourceId() {
39 next_frame_source_id_++;
40 current_frame_id_ = -1;
42 next_frame_sequence_number_ = viz::BeginFrameArgs::kStartingFrameNumber;
45 // Simulates a sequence of queries
47 // "S{frame_number}": Start frame.
48 // "D{frame_number}": End frame as dropped.
49 // "P{frame_number}": End frame as none-dropped (presented).
50 // "E{frame_number}": Frame expects twp acks.
51 // "I": Increase source_id (simulating a crash).
52 // "R": Reset the frame sorter.
53 // Method expects the start of frames to be in order starting with 1.
54 void SimulateQueries(std::vector<std::string> queries) {
55 // Keeps track of how many times a frame is terminated.
56 std::map<int, int> end_counters;
58 for (auto& query : queries) {
60 base::StringToInt(query.substr(1), &id);
61 if (id > current_frame_id_) {
63 args_.push_back(GetNextFrameArgs());
67 frame_sorter_.AddNewFrame(args_[id]);
72 CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped);
73 if (end_counters[id] == 1) {
74 // For the first response to the frame, mark it as not including
75 // update from the main-thread.
76 info.main_thread_response = FrameInfo::MainThreadResponse::kMissing;
78 DCHECK_EQ(2, end_counters[id]);
79 info.main_thread_response =
80 FrameInfo::MainThreadResponse::kIncluded;
82 frame_sorter_.AddFrameResult(args_[id], info);
88 CreateFakeFrameInfo(FrameInfo::FrameFinalState::kPresentedAll);
89 if (end_counters[id] == 1) {
90 // For the first response to the frame, mark it as not including
91 // update from the main-thread.
92 info.main_thread_response = FrameInfo::MainThreadResponse::kMissing;
94 DCHECK_EQ(2, end_counters[id]);
95 info.main_thread_response =
96 FrameInfo::MainThreadResponse::kIncluded;
98 frame_sorter_.AddFrameResult(args_[id], info);
105 frame_sorter_.Reset();
111 void ValidateResults(
112 std::vector<std::pair<uint64_t, bool>> expected_results) {
113 EXPECT_EQ(sorted_frames_.size(), expected_results.size());
114 int result_index = 0;
115 for (auto result : expected_results) {
116 auto& sorted_frame = sorted_frames_[result_index];
117 EXPECT_EQ(sorted_frame.first.frame_id.sequence_number, result.first + 1);
118 EXPECT_EQ(sorted_frame.second, result.second);
124 void FlushFrame(const viz::BeginFrameArgs& args, const FrameInfo& frame) {
125 sorted_frames_.emplace_back(args, frame.IsDroppedAffectingSmoothness());
128 FrameSorter frame_sorter_;
129 std::vector<std::pair<const viz::BeginFrameArgs, bool>> sorted_frames_;
130 base::TimeTicks last_begin_frame_time_ = base::TimeTicks::Now();
131 uint64_t next_frame_source_id_ = 0;
132 uint64_t next_frame_sequence_number_ =
133 viz::BeginFrameArgs::kStartingFrameNumber;
134 std::vector<const viz::BeginFrameArgs> args_ = {};
135 int current_frame_id_ = -1;
138 TEST_F(FrameSorterTest, TestSortingFrames) {
139 // Frame end in order of F0, F2, F1, F3, but they should be flushed in the
140 // order they began (eg. F0, F1, F2, F3).
141 std::vector<std::string> queries = {"S0", "S1", "P0", "S2",
142 "P2", "S3", "P1", "D3"};
143 std::vector<std::pair<uint64_t, bool>> expected_results = {
144 {0, false}, {1, false}, {2, false}, {3, true}};
146 SimulateQueries(queries);
147 ValidateResults(expected_results);
150 TEST_F(FrameSorterTest, ResetInMiddleOfAcks) {
151 // Reset occur in between the start and ack of F1 & F3, so except these two
152 // all other frames should be flushed in order in the sorted_frames_.
153 std::vector<std::string> queries = {"S0", "S1", "P0", "S2", "P2", "S3",
154 "R", "S4", "P1", "D4", "D3"};
155 std::vector<std::pair<uint64_t, bool>> expected_results = {
156 {0, false}, {2, false}, {4, true}};
158 SimulateQueries(queries);
159 ValidateResults(expected_results);
162 TEST_F(FrameSorterTest, ExpectingMultipleAcks) {
163 // F1 has multiple acks and the final ack is received at the end. Which makes
164 // other frames to wait in pending tree before being flushed, all in order.
165 // Also in any of the duplicated acks report a dropped frame, the overall
166 // status for that frame should be dropped.
167 std::vector<std::string> queries = {"S0", "S1", "P0", "S1", "S2", "P2",
168 "S3", "S4", "P1", "D3", "D4", "D1"};
169 std::vector<std::pair<uint64_t, bool>> expected_results = {
170 {0, false}, {1, true}, {2, false}, {3, true}, {4, true}};
172 SimulateQueries(queries);
173 ValidateResults(expected_results);
176 TEST_F(FrameSorterTest, ExpectingMultipleAcksWithReset) {
177 // Combination of last two tests. Reset occurs in middle of start and ack of
178 // F1 and F3. Also F1 expects two acks, which both are received after reset.
179 std::vector<std::string> queries = {"S0", "S1", "P0", "S1", "S2", "P2", "S3",
180 "R", "S4", "P1", "D1", "D4", "D3"};
181 std::vector<std::pair<uint64_t, bool>> expected_results = {
182 {0, false}, {2, false}, {4, true}};
184 SimulateQueries(queries);
185 ValidateResults(expected_results);
188 TEST_F(FrameSorterTest, ExpectingMultipleAcksWithReset2) {
189 // Reset occurs in middle of the two starts of F0.
190 // This is to test if F1 will be flushed properly while being in between the
191 // two acks of F0, which should be ignored as a result of reset.
192 std::vector<std::string> queries = {"S0", "R", "S0", "P0", "S1", "D0", "D1"};
193 std::vector<std::pair<uint64_t, bool>> expected_results = {{1, true}};
195 SimulateQueries(queries);
196 ValidateResults(expected_results);
199 TEST_F(FrameSorterTest, ExpectingMultipleAcksWithSourceIdIncrease) {
200 // Reset and increase in source_id occurs in middle start and ack of F1.
201 // This is to simulate a navigation. So the initial F0 has source_id of 0
202 // while the F0 after reset has source_id of 1, and because of that the
203 // start and ack for F0 after the reset is not ignored (The ignore is only
204 // needed when frame has the same source_id and sequence_number).
205 std::vector<std::string> queries = {"S0", "S0", "S1", "S1", "I", "R",
206 "S0", "S0", "P0", "S1", "P1", "D0"};
207 std::vector<std::pair<uint64_t, bool>> expected_results = {{0, true},
210 SimulateQueries(queries);
211 ValidateResults(expected_results);