Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / media / capture / smooth_event_sampler_unittest.cc
blobfbf81df3eb07369cbe49d637a8e0c6301efc6cd3
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/capture/smooth_event_sampler.h"
7 #include "base/strings/stringprintf.h"
8 #include "testing/gtest/include/gtest/gtest.h"
10 namespace content {
12 namespace {
14 bool AddEventAndConsiderSampling(SmoothEventSampler* sampler,
15 base::TimeTicks event_time) {
16 sampler->ConsiderPresentationEvent(event_time);
17 return sampler->ShouldSample();
20 void SteadyStateSampleAndAdvance(base::TimeDelta vsync,
21 SmoothEventSampler* sampler,
22 base::TimeTicks* t) {
23 ASSERT_TRUE(AddEventAndConsiderSampling(sampler, *t));
24 ASSERT_TRUE(sampler->HasUnrecordedEvent());
25 sampler->RecordSample();
26 ASSERT_FALSE(sampler->HasUnrecordedEvent());
27 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
28 *t += vsync;
29 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
32 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync,
33 SmoothEventSampler* sampler,
34 base::TimeTicks* t) {
35 ASSERT_FALSE(AddEventAndConsiderSampling(sampler, *t));
36 ASSERT_TRUE(sampler->HasUnrecordedEvent());
37 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
38 *t += vsync;
39 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
42 base::TimeTicks InitialTestTimeTicks() {
43 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
46 void TestRedundantCaptureStrategy(base::TimeDelta capture_period,
47 int redundant_capture_goal,
48 SmoothEventSampler* sampler,
49 base::TimeTicks* t) {
50 // Before any events have been considered, we're overdue for sampling.
51 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t));
53 // Consider the first event. We want to sample that.
54 ASSERT_FALSE(sampler->HasUnrecordedEvent());
55 ASSERT_TRUE(AddEventAndConsiderSampling(sampler, *t));
56 ASSERT_TRUE(sampler->HasUnrecordedEvent());
57 sampler->RecordSample();
58 ASSERT_FALSE(sampler->HasUnrecordedEvent());
60 // After more than 250 ms has passed without considering an event, we should
61 // repeatedly be overdue for sampling. However, once the redundant capture
62 // goal is achieved, we should no longer be overdue for sampling.
63 *t += base::TimeDelta::FromMilliseconds(250);
64 for (int i = 0; i < redundant_capture_goal; i++) {
65 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
66 ASSERT_FALSE(sampler->HasUnrecordedEvent());
67 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t))
68 << "Should sample until redundant capture goal is hit";
69 sampler->RecordSample();
70 *t += capture_period; // Timer fires once every capture period.
72 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t))
73 << "Should not be overdue once redundant capture goal achieved.";
76 } // namespace
78 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains
79 // much more comprehensive before/after/edge-case scenarios than the others.
80 TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) {
81 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
82 const int redundant_capture_goal = 200;
83 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60;
85 SmoothEventSampler sampler(capture_period, redundant_capture_goal);
86 base::TimeTicks t = InitialTestTimeTicks();
88 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
89 &sampler, &t);
91 // Steady state, we should capture every other vsync, indefinitely.
92 for (int i = 0; i < 100; i++) {
93 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
94 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
95 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
98 // Now pretend we're limited by backpressure in the pipeline. In this scenario
99 // case we are adding events but not sampling them.
100 for (int i = 0; i < 20; i++) {
101 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
102 ASSERT_EQ(i >= 14, sampler.IsOverdueForSamplingAt(t));
103 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
104 ASSERT_TRUE(sampler.HasUnrecordedEvent());
105 t += vsync;
108 // Now suppose we can sample again. We should be back in the steady state,
109 // but at a different phase.
110 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
111 for (int i = 0; i < 100; i++) {
112 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
113 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
114 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
118 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped.
119 TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) {
120 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
121 const int redundant_capture_goal = 2;
122 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50;
124 SmoothEventSampler sampler(capture_period, redundant_capture_goal);
125 base::TimeTicks t = InitialTestTimeTicks();
127 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
128 &sampler, &t);
130 // Steady state, we should capture 1st, 2nd and 4th frames out of every five
131 // frames, indefinitely.
132 for (int i = 0; i < 100; i++) {
133 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
134 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
135 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
136 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
137 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
138 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
141 // Now pretend we're limited by backpressure in the pipeline. In this scenario
142 // case we are adding events but not sampling them.
143 for (int i = 0; i < 20; i++) {
144 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
145 ASSERT_EQ(i >= 11, sampler.IsOverdueForSamplingAt(t));
146 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
147 t += vsync;
150 // Now suppose we can sample again. We should be back in the steady state
151 // again.
152 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
153 for (int i = 0; i < 100; i++) {
154 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
155 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
156 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
157 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
158 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
159 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
163 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped.
164 TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) {
165 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
166 const int redundant_capture_goal = 32;
167 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75;
169 SmoothEventSampler sampler(capture_period, redundant_capture_goal);
170 base::TimeTicks t = InitialTestTimeTicks();
172 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
173 &sampler, &t);
175 // Steady state, we should capture 1st and 3rd frames out of every five
176 // frames, indefinitely.
177 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
178 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
179 for (int i = 0; i < 100; i++) {
180 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
181 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
182 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
183 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
184 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
185 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
188 // Now pretend we're limited by backpressure in the pipeline. In this scenario
189 // case we are adding events but not sampling them.
190 for (int i = 0; i < 20; i++) {
191 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
192 ASSERT_EQ(i >= 16, sampler.IsOverdueForSamplingAt(t));
193 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
194 t += vsync;
197 // Now suppose we can sample again. We capture the next frame, and not the one
198 // after that, and then we're back in the steady state again.
199 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
200 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
201 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
202 for (int i = 0; i < 100; i++) {
203 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
204 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
205 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
206 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
207 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
208 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
212 // 30Hz sampled at 30Hz should produce 30Hz.
213 TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) {
214 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
215 const int redundant_capture_goal = 1;
216 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30;
218 SmoothEventSampler sampler(capture_period, redundant_capture_goal);
219 base::TimeTicks t = InitialTestTimeTicks();
221 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
222 &sampler, &t);
224 // Steady state, we should capture every vsync, indefinitely.
225 for (int i = 0; i < 200; i++) {
226 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
227 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
230 // Now pretend we're limited by backpressure in the pipeline. In this scenario
231 // case we are adding events but not sampling them.
232 for (int i = 0; i < 10; i++) {
233 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
234 ASSERT_EQ(i >= 7, sampler.IsOverdueForSamplingAt(t));
235 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
236 t += vsync;
239 // Now suppose we can sample again. We should be back in the steady state.
240 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
241 for (int i = 0; i < 100; i++) {
242 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
243 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
247 // 24Hz sampled at 30Hz should produce 24Hz.
248 TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) {
249 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
250 const int redundant_capture_goal = 333;
251 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24;
253 SmoothEventSampler sampler(capture_period, redundant_capture_goal);
254 base::TimeTicks t = InitialTestTimeTicks();
256 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
257 &sampler, &t);
259 // Steady state, we should capture every vsync, indefinitely.
260 for (int i = 0; i < 200; i++) {
261 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
262 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
265 // Now pretend we're limited by backpressure in the pipeline. In this scenario
266 // case we are adding events but not sampling them.
267 for (int i = 0; i < 10; i++) {
268 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
269 ASSERT_EQ(i >= 6, sampler.IsOverdueForSamplingAt(t));
270 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
271 t += vsync;
274 // Now suppose we can sample again. We should be back in the steady state.
275 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
276 for (int i = 0; i < 100; i++) {
277 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
278 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
282 TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) {
283 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
284 const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1);
286 SmoothEventSampler sampler(capture_period, 1);
287 base::TimeTicks t = InitialTestTimeTicks();
289 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
290 sampler.RecordSample();
291 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t))
292 << "Sampled last event; should not be dirty.";
293 t += overdue_period;
295 // Now simulate 2 events with the same clock value.
296 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
297 sampler.RecordSample();
298 ASSERT_FALSE(AddEventAndConsiderSampling(&sampler, t))
299 << "Two events at same time -- expected second not to be sampled.";
300 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t + overdue_period))
301 << "Second event should dirty the capture state.";
302 sampler.RecordSample();
303 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t + overdue_period));
306 namespace {
308 struct DataPoint {
309 bool should_capture;
310 double increment_ms;
313 void ReplayCheckingSamplerDecisions(const DataPoint* data_points,
314 size_t num_data_points,
315 SmoothEventSampler* sampler) {
316 base::TimeTicks t = InitialTestTimeTicks();
317 for (size_t i = 0; i < num_data_points; ++i) {
318 t += base::TimeDelta::FromMicroseconds(
319 static_cast<int64>(data_points[i].increment_ms * 1000));
320 ASSERT_EQ(data_points[i].should_capture,
321 AddEventAndConsiderSampling(sampler, t))
322 << "at data_points[" << i << ']';
323 if (data_points[i].should_capture)
324 sampler->RecordSample();
328 } // namespace
330 TEST(SmoothEventSamplerTest, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz) {
331 // Actual capturing of timing data: Initial instability as a 24 FPS video was
332 // started from a still screen, then clearly followed by steady-state.
333 static const DataPoint data_points[] = {
334 { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 },
335 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 },
336 { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 },
337 { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 },
338 { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 },
339 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 },
340 { false, 0 }, { true, 50.161 }, { false, 0 }, { true, 33.44 },
341 { true, 16.72 }, { false, 16.721 }, { true, 66.881 }, { false, 0 },
342 { true, 33.441 }, { true, 16.72 }, { true, 50.16 }, { true, 16.72 },
343 { false, 16.721 }, { true, 50.161 }, { true, 50.16 }, { false, 0 },
344 { true, 33.441 }, { true, 50.337 }, { true, 50.183 }, { true, 16.722 },
345 { true, 50.161 }, { true, 33.441 }, { true, 50.16 }, { true, 33.441 },
346 { true, 50.16 }, { true, 33.441 }, { true, 50.16 }, { true, 33.44 },
347 { true, 50.161 }, { true, 50.16 }, { true, 33.44 }, { true, 33.441 },
348 { true, 50.16 }, { true, 50.161 }, { true, 33.44 }, { true, 33.441 },
349 { true, 50.16 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
350 { true, 50.161 }, { true, 33.44 }, { true, 50.161 }, { true, 33.44 },
351 { true, 83.601 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }
354 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, 3);
355 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
358 TEST(SmoothEventSamplerTest, DrawingAt30FpsWith60HzVsyncSampledAt30Hertz) {
359 // Actual capturing of timing data: Initial instability as a 30 FPS video was
360 // started from a still screen, then followed by steady-state. Drawing
361 // framerate from the video rendering was a bit volatile, but averaged 30 FPS.
362 static const DataPoint data_points[] = {
363 { true, 2407.69 }, { true, 16.733 }, { true, 217.362 }, { true, 33.441 },
364 { true, 33.44 }, { true, 33.44 }, { true, 33.441 }, { true, 33.44 },
365 { true, 33.44 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 },
366 { true, 16.721 }, { true, 33.44 }, { false, 0 }, { true, 50.161 },
367 { true, 50.16 }, { false, 0 }, { true, 50.161 }, { true, 33.44 },
368 { true, 16.72 }, { false, 0 }, { false, 16.72 }, { true, 66.881 },
369 { false, 0 }, { true, 33.44 }, { true, 16.72 }, { true, 50.161 },
370 { false, 0 }, { true, 33.538 }, { true, 33.526 }, { true, 33.447 },
371 { true, 33.445 }, { true, 33.441 }, { true, 16.721 }, { true, 33.44 },
372 { true, 33.44 }, { true, 50.161 }, { true, 16.72 }, { true, 33.44 },
373 { true, 33.441 }, { true, 33.44 }, { false, 0 }, { false, 16.72 },
374 { true, 66.881 }, { true, 16.72 }, { false, 16.72 }, { true, 50.16 },
375 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 33.44 },
376 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { false, 0 },
377 { true, 33.44 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
378 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 66.88 },
379 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
380 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { false, 0 },
381 { true, 16.72 }, { true, 50.161 }, { false, 0 }, { true, 50.16 },
382 { false, 0.001 }, { true, 16.721 }, { true, 66.88 }, { true, 33.44 },
383 { true, 33.441 }, { true, 33.44 }, { true, 50.161 }, { true, 16.72 },
384 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 66.881 },
385 { true, 33.44 }, { true, 16.72 }, { true, 33.441 }, { false, 16.72 },
386 { true, 66.88 }, { true, 16.721 }, { true, 50.16 }, { true, 33.44 },
387 { true, 16.72 }, { true, 33.441 }, { true, 33.44 }, { true, 33.44 }
390 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, 3);
391 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
394 TEST(SmoothEventSamplerTest, DrawingAt60FpsWith60HzVsyncSampledAt30Hertz) {
395 // Actual capturing of timing data: WebGL Acquarium demo
396 // (http://webglsamples.googlecode.com/hg/aquarium/aquarium.html) which ran
397 // between 55-60 FPS in the steady-state.
398 static const DataPoint data_points[] = {
399 { true, 16.72 }, { true, 16.72 }, { true, 4163.29 }, { true, 50.193 },
400 { true, 117.041 }, { true, 50.161 }, { true, 50.16 }, { true, 33.441 },
401 { true, 50.16 }, { true, 33.44 }, { false, 0 }, { false, 0 },
402 { true, 50.161 }, { true, 83.601 }, { true, 50.16 }, { true, 16.72 },
403 { true, 33.441 }, { false, 16.72 }, { true, 50.16 }, { true, 16.72 },
404 { false, 0.001 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
405 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
406 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
407 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
408 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
409 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
410 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 33.44 },
411 { false, 0 }, { true, 16.721 }, { true, 50.161 }, { false, 0 },
412 { true, 33.44 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
413 { false, 0 }, { true, 33.44 }, { false, 16.72 }, { true, 16.72 },
414 { true, 50.16 }, { false, 0 }, { true, 16.721 }, { true, 33.44 },
415 { false, 0 }, { true, 33.44 }, { false, 16.721 }, { true, 16.721 },
416 { true, 50.161 }, { false, 0 }, { true, 16.72 }, { true, 33.44 },
417 { false, 0 }, { true, 33.441 }, { false, 16.72 }, { true, 16.72 },
418 { true, 50.16 }, { false, 0 }, { true, 16.72 }, { true, 33.441 },
419 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { true, 33.441 },
420 { false, 0 }, { true, 33.44 }, { true, 33.441 }, { false, 0 },
421 { true, 33.44 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
422 { true, 16.721 }, { true, 50.161 }, { false, 0 }, { true, 16.72 },
423 { true, 33.44 }, { true, 33.441 }, { false, 0 }, { true, 33.44 },
424 { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 },
425 { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 },
426 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
427 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
428 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
429 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
432 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, 3);
433 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
436 } // namespace content