File: frame_sorter_unittest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (267 lines) | stat: -rw-r--r-- 9,920 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// 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/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "cc/metrics/custom_metrics_recorder.h"
#include "cc/metrics/frame_info.h"
#include "cc/test/fake_frame_info.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {

class TestCustomMetricsRecorder : public CustomMetricRecorder {
 public:
  TestCustomMetricsRecorder() = default;
  ~TestCustomMetricsRecorder() override = default;

  // CustomMetricRecorder:
  void ReportPercentDroppedFramesInOneSecondWindow2(double percent) override {
    ++report_count_;
    last_percent_dropped_frames_ = percent;
  }
  void ReportEventLatency(
      std::vector<EventLatencyTracker::LatencyData> latencies) override {}

  void Reset() {
    report_count_ = 0u;
    last_percent_dropped_frames_ = 0;
  }

  int report_count() const { return report_count_; }

  double last_percent_dropped_frames() const {
    return last_percent_dropped_frames_;
  }

 private:
  int report_count_ = 0u;
  double last_percent_dropped_frames_ = 0;
};

// Test class for FrameSorter
class FrameSorterTest : public testing::Test, FrameSorterObserver {
 public:
  FrameSorterTest() {
    frame_sorter_.AddObserver(this);
    IncreaseSourceId();
  }
  ~FrameSorterTest() override = default;

  viz::BeginFrameArgs GetNextFrameArgs() {
    uint64_t sequence_number = next_frame_sequence_number_++;
    last_begin_frame_time_ += base::Milliseconds(250);
    return viz::BeginFrameArgs::Create(
        BEGINFRAME_FROM_HERE, next_frame_source_id_, sequence_number,
        last_begin_frame_time_,
        last_begin_frame_time_ + base::Milliseconds(250),
        base::Milliseconds(250), 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(/*reset_fcp=*/true);
          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++;
    }
  }
  FrameSorter frame_sorter_;

 private:
  void AddSortedFrame(const viz::BeginFrameArgs& args,
                      const FrameInfo& frame) override {
    sorted_frames_.emplace_back(args, frame.IsDroppedAffectingSmoothness());
  }

  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<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);
}

TEST_F(FrameSorterTest, ReportOnEveryFrameForUI) {
  // 1 dropped frame in the first window of 4 frames, which will be accounted
  // for over the first second of monitoring.
  // We need to present two additional frames in order to (1) assign the
  // sliding_window_current_percent_dropped_ value and (2) print it in the
  // recorder.
  std::vector<std::string> queries = {"S0", "S1", "P0", "S2", "P2", "S3",
                                      "P1", "D3", "S4", "P4", "S5", "D5"};

  frame_sorter_.EnableReportForUI();
  TestCustomMetricsRecorder recorder;

  SimulateQueries(queries);
  // Should report 1 report count because we only had one window > the default
  // interval of 1s.
  EXPECT_EQ(recorder.report_count(), 1);
  EXPECT_EQ(recorder.last_percent_dropped_frames(),
            25.0f);  // 1 of 4 frames dropped in the first window.
}

}  // namespace cc