1 // Copyright 2017 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.
4
5 #include "base/trace_event/memory_dump_scheduler.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using ::testing::AtMost;
17 using ::testing::Invoke;
18 using ::testing::_;
19
20 namespace base {
21 namespace trace_event {
22
23 namespace {
24
25 // Wrapper to use gmock on a callback.
26 struct CallbackWrapper {
27 MOCK_METHOD1(OnTick, void(MemoryDumpLevelOfDetail));
28 };
29
30 } // namespace
31
32 class MemoryDumpSchedulerTest : public testing::Test {
33 public:
MemoryDumpSchedulerTest()34 MemoryDumpSchedulerTest()
35 : testing::Test(),
36 evt_(WaitableEvent::ResetPolicy::MANUAL,
37 WaitableEvent::InitialState::NOT_SIGNALED),
38 bg_thread_("MemoryDumpSchedulerTest Thread") {
39 bg_thread_.Start();
40 }
41
42 protected:
43 MemoryDumpScheduler scheduler_;
44 WaitableEvent evt_;
45 CallbackWrapper on_tick_;
46 Thread bg_thread_;
47 };
48
TEST_F(MemoryDumpSchedulerTest,SingleTrigger)49 TEST_F(MemoryDumpSchedulerTest, SingleTrigger) {
50 const uint32_t kPeriodMs = 1;
51 const auto kLevelOfDetail = MemoryDumpLevelOfDetail::DETAILED;
52 const uint32_t kTicks = 5;
53 MemoryDumpScheduler::Config config;
54 config.triggers.push_back({kLevelOfDetail, kPeriodMs});
55 config.callback =
56 BindRepeating(&CallbackWrapper::OnTick, Unretained(&on_tick_));
57
58 testing::InSequence sequence;
59 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
60 EXPECT_CALL(on_tick_, OnTick(_))
61 .WillRepeatedly(Invoke(
62 [this, kLevelOfDetail](MemoryDumpLevelOfDetail level_of_detail) {
63 EXPECT_EQ(kLevelOfDetail, level_of_detail);
64 this->evt_.Signal();
65 }));
66
67 // Check that Stop() before Start() doesn't cause any error.
68 scheduler_.Stop();
69
70 const TimeTicks tstart = TimeTicks::Now();
71 scheduler_.Start(config, bg_thread_.task_runner());
72 evt_.Wait();
73 const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
74
75 // It takes N-1 ms to perform N ticks of 1ms each.
76 EXPECT_GE(time_ms, kPeriodMs * (kTicks - 1));
77
78 // Check that stopping twice doesn't cause any problems.
79 scheduler_.Stop();
80 scheduler_.Stop();
81 }
82
TEST_F(MemoryDumpSchedulerTest,MultipleTriggers)83 TEST_F(MemoryDumpSchedulerTest, MultipleTriggers) {
84 const uint32_t kPeriodLightMs = 3;
85 const uint32_t kPeriodDetailedMs = 9;
86 MemoryDumpScheduler::Config config;
87 const MemoryDumpLevelOfDetail kLight = MemoryDumpLevelOfDetail::LIGHT;
88 const MemoryDumpLevelOfDetail kDetailed = MemoryDumpLevelOfDetail::DETAILED;
89 config.triggers.push_back({kLight, kPeriodLightMs});
90 config.triggers.push_back({kDetailed, kPeriodDetailedMs});
91 config.callback =
92 BindRepeating(&CallbackWrapper::OnTick, Unretained(&on_tick_));
93
94 TimeTicks t1, t2, t3;
95
96 testing::InSequence sequence;
97 EXPECT_CALL(on_tick_, OnTick(kDetailed))
98 .WillOnce(
99 Invoke([&t1](MemoryDumpLevelOfDetail) { t1 = TimeTicks::Now(); }));
100 EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
101 EXPECT_CALL(on_tick_, OnTick(kLight)).Times(1);
102 EXPECT_CALL(on_tick_, OnTick(kDetailed))
103 .WillOnce(
104 Invoke([&t2](MemoryDumpLevelOfDetail) { t2 = TimeTicks::Now(); }));
105 EXPECT_CALL(on_tick_, OnTick(kLight))
106 .WillOnce(
107 Invoke([&t3](MemoryDumpLevelOfDetail) { t3 = TimeTicks::Now(); }));
108
109 // Rationale for WillRepeatedly and not just WillOnce: Extra ticks might
110 // happen if the Stop() takes time. Not an interesting case, but we need to
111 // avoid gmock to shout in that case.
112 EXPECT_CALL(on_tick_, OnTick(_))
113 .WillRepeatedly(
114 Invoke([this](MemoryDumpLevelOfDetail) { this->evt_.Signal(); }));
115
116 scheduler_.Start(config, bg_thread_.task_runner());
117 evt_.Wait();
118 scheduler_.Stop();
119 EXPECT_GE((t2 - t1).InMillisecondsF(), kPeriodDetailedMs);
120 EXPECT_GE((t3 - t2).InMillisecondsF(), kPeriodLightMs);
121 }
122
TEST_F(MemoryDumpSchedulerTest,StartStopQuickly)123 TEST_F(MemoryDumpSchedulerTest, StartStopQuickly) {
124 const uint32_t kPeriodMs = 3;
125 const uint32_t kQuickIterations = 5;
126 const uint32_t kDetailedTicks = 10;
127
128 MemoryDumpScheduler::Config light_config;
129 light_config.triggers.push_back({MemoryDumpLevelOfDetail::LIGHT, kPeriodMs});
130 light_config.callback =
131 BindRepeating(&CallbackWrapper::OnTick, Unretained(&on_tick_));
132
133 MemoryDumpScheduler::Config detailed_config;
134 detailed_config.triggers.push_back(
135 {MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
136 detailed_config.callback =
137 BindRepeating(&CallbackWrapper::OnTick, Unretained(&on_tick_));
138
139 testing::InSequence sequence;
140 EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::LIGHT))
141 .Times(AtMost(kQuickIterations));
142 EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::DETAILED))
143 .Times(kDetailedTicks - 1);
144 EXPECT_CALL(on_tick_, OnTick(MemoryDumpLevelOfDetail::DETAILED))
145 .WillRepeatedly(
146 Invoke([this](MemoryDumpLevelOfDetail) { this->evt_.Signal(); }));
147
148 const TimeTicks tstart = TimeTicks::Now();
149 for (unsigned int i = 0; i < kQuickIterations; i++) {
150 scheduler_.Start(light_config, bg_thread_.task_runner());
151 scheduler_.Stop();
152 }
153
154 scheduler_.Start(detailed_config, bg_thread_.task_runner());
155
156 evt_.Wait();
157 const double time_ms = (TimeTicks::Now() - tstart).InMillisecondsF();
158 scheduler_.Stop();
159
160 // It takes N-1 ms to perform N ticks of 1ms each.
161 EXPECT_GE(time_ms, kPeriodMs * (kDetailedTicks - 1));
162 }
163
TEST_F(MemoryDumpSchedulerTest,StopAndStartOnAnotherThread)164 TEST_F(MemoryDumpSchedulerTest, StopAndStartOnAnotherThread) {
165 const uint32_t kPeriodMs = 1;
166 const uint32_t kTicks = 3;
167 MemoryDumpScheduler::Config config;
168 config.triggers.push_back({MemoryDumpLevelOfDetail::DETAILED, kPeriodMs});
169 config.callback =
170 BindRepeating(&CallbackWrapper::OnTick, Unretained(&on_tick_));
171
172 auto expected_task_runner = bg_thread_.task_runner();
173 testing::InSequence sequence;
174 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
175 EXPECT_CALL(on_tick_, OnTick(_))
176 .WillRepeatedly(
177 Invoke([this, expected_task_runner](MemoryDumpLevelOfDetail) {
178 EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
179 this->evt_.Signal();
180 }));
181
182 scheduler_.Start(config, bg_thread_.task_runner());
183 evt_.Wait();
184 scheduler_.Stop();
185 bg_thread_.Stop();
186
187 Thread bg_thread_2("MemoryDumpSchedulerTest Thread 2");
188 bg_thread_2.Start();
189 evt_.Reset();
190 expected_task_runner = bg_thread_2.task_runner();
191 EXPECT_CALL(on_tick_, OnTick(_)).Times(kTicks - 1);
192 EXPECT_CALL(on_tick_, OnTick(_))
193 .WillRepeatedly(
194 Invoke([this, expected_task_runner](MemoryDumpLevelOfDetail) {
195 EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
196 this->evt_.Signal();
197 }));
198 scheduler_.Start(config, bg_thread_2.task_runner());
199 evt_.Wait();
200 scheduler_.Stop();
201 }
202
203 } // namespace trace_event
204 } // namespace base
205