OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 #include "platform/Timer.h" | 6 #include "platform/Timer.h" |
7 | 7 |
8 #include "public/platform/Platform.h" | 8 #include "public/platform/Platform.h" |
9 #include "public/platform/WebScheduler.h" | 9 #include "public/platform/WebScheduler.h" |
10 #include "public/platform/WebThread.h" | 10 #include "public/platform/WebThread.h" |
11 #include <gmock/gmock.h> | 11 #include <gmock/gmock.h> |
12 #include <gtest/gtest.h> | 12 #include <gtest/gtest.h> |
13 #include <queue> | 13 #include <queue> |
14 | 14 |
15 using testing::ElementsAre; | 15 using testing::ElementsAre; |
16 | 16 |
17 namespace blink { | 17 namespace blink { |
18 namespace { | 18 namespace { |
19 double gCurrentTimeSecs = 0.0; | 19 double gCurrentTimeSecs = 0.0; |
20 | 20 |
21 double currentTime() | 21 double currentTime() |
22 { | 22 { |
23 return gCurrentTimeSecs; | 23 return gCurrentTimeSecs; |
24 } | 24 } |
25 | 25 |
26 // This class exists because gcc doesn't know how to move an OwnPtr. | |
27 class RefCountedTaskContainer : public RefCounted<RefCountedTaskContainer> { | |
28 public: | |
29 explicit RefCountedTaskContainer(WebThread::Task* task) : m_task(adoptPtr(ta
sk)) { } | |
30 | |
31 ~RefCountedTaskContainer() { } | |
32 | |
33 void run() | |
34 { | |
35 m_task->run(); | |
36 } | |
37 | |
38 private: | |
39 OwnPtr<WebThread::Task> m_task; | |
40 }; | |
41 | |
42 class DelayedTask { | |
43 public: | |
44 DelayedTask(WebThread::Task* task, long long delayMs) | |
45 : m_task(adoptRef(new RefCountedTaskContainer(task))) | |
46 , m_runTimeSecs(monotonicallyIncreasingTime() + 0.001 * static_cast<doub
le>(delayMs)) | |
47 , m_delayMs(delayMs) { } | |
48 | |
49 bool operator<(const DelayedTask& other) const | |
50 { | |
51 return m_runTimeSecs > other.m_runTimeSecs; | |
52 } | |
53 | |
54 void run() const | |
55 { | |
56 m_task->run(); | |
57 } | |
58 | |
59 double runTimeSecs() const | |
60 { | |
61 return m_runTimeSecs; | |
62 } | |
63 | |
64 long long delayMs() const | |
65 { | |
66 return m_delayMs; | |
67 } | |
68 | |
69 private: | |
70 RefPtr<RefCountedTaskContainer> m_task; | |
71 double m_runTimeSecs; | |
72 long long m_delayMs; | |
73 }; | |
74 | |
75 class MockWebScheduler : public WebScheduler { | 26 class MockWebScheduler : public WebScheduler { |
76 public: | 27 public: |
77 MockWebScheduler() { } | 28 explicit MockWebScheduler() { } |
78 ~MockWebScheduler() override { } | 29 ~MockWebScheduler() override { } |
79 | 30 |
80 bool shouldYieldForHighPriorityWork() override | 31 bool shouldYieldForHighPriorityWork() override |
81 { | 32 { |
82 return false; | 33 return false; |
83 } | 34 } |
84 | 35 |
85 bool canExceedIdleDeadlineIfRequired() override | 36 bool canExceedIdleDeadlineIfRequired() override |
86 { | 37 { |
87 return false; | 38 return false; |
(...skipping 10 matching lines...) Expand all Loading... |
98 void postIdleTaskAfterWakeup(const WebTraceLocation&, WebThread::IdleTask*)
override | 49 void postIdleTaskAfterWakeup(const WebTraceLocation&, WebThread::IdleTask*)
override |
99 { | 50 { |
100 } | 51 } |
101 | 52 |
102 void postLoadingTask(const WebTraceLocation&, WebThread::Task*) override | 53 void postLoadingTask(const WebTraceLocation&, WebThread::Task*) override |
103 { | 54 { |
104 } | 55 } |
105 | 56 |
106 void postTimerTask(const WebTraceLocation&, WebThread::Task* task, long long
delayMs) override | 57 void postTimerTask(const WebTraceLocation&, WebThread::Task* task, long long
delayMs) override |
107 { | 58 { |
108 m_timerTasks.push(DelayedTask(task, delayMs)); | |
109 } | 59 } |
110 | |
111 void runUntilIdle() | |
112 { | |
113 while (!m_timerTasks.empty()) { | |
114 gCurrentTimeSecs = m_timerTasks.top().runTimeSecs(); | |
115 m_timerTasks.top().run(); | |
116 m_timerTasks.pop(); | |
117 } | |
118 } | |
119 | |
120 void runUntilIdleOrDeadlinePassed(double deadline) | |
121 { | |
122 while (!m_timerTasks.empty()) { | |
123 if (m_timerTasks.top().runTimeSecs() > deadline) { | |
124 gCurrentTimeSecs = deadline; | |
125 break; | |
126 } | |
127 gCurrentTimeSecs = m_timerTasks.top().runTimeSecs(); | |
128 m_timerTasks.top().run(); | |
129 m_timerTasks.pop(); | |
130 } | |
131 } | |
132 | |
133 bool hasOneTimerTask() const | |
134 { | |
135 return m_timerTasks.size() == 1; | |
136 } | |
137 | |
138 long nextTimerTaskDelayMillis() const | |
139 { | |
140 ASSERT(hasOneTimerTask()); | |
141 return m_timerTasks.top().delayMs(); | |
142 } | |
143 | |
144 private: | |
145 std::priority_queue<DelayedTask> m_timerTasks; | |
146 }; | 60 }; |
147 | 61 |
148 class FakeWebThread : public WebThread { | 62 class FakeWebThread : public WebThread { |
149 public: | 63 public: |
150 FakeWebThread() : m_webScheduler(adoptPtr(new MockWebScheduler())) { } | 64 explicit FakeWebThread(WebScheduler* webScheduler) : m_webScheduler(webSched
uler) { } |
151 ~FakeWebThread() override { } | 65 ~FakeWebThread() override { } |
152 | 66 |
153 // WebThread implementation: | 67 // WebThread implementation: |
154 void postTask(const WebTraceLocation&, Task*) | 68 void postTask(const WebTraceLocation&, Task*) |
155 { | 69 { |
156 ASSERT_NOT_REACHED(); | 70 ASSERT_NOT_REACHED(); |
157 } | 71 } |
158 | 72 |
159 virtual void postDelayedTask(const WebTraceLocation&, Task*, long long) | 73 virtual void postDelayedTask(const WebTraceLocation&, Task*, long long) |
160 { | 74 { |
161 ASSERT_NOT_REACHED(); | 75 ASSERT_NOT_REACHED(); |
162 } | 76 } |
163 | 77 |
164 virtual bool isCurrentThread() const | 78 virtual bool isCurrentThread() const |
165 { | 79 { |
166 ASSERT_NOT_REACHED(); | 80 ASSERT_NOT_REACHED(); |
167 return true; | 81 return true; |
168 } | 82 } |
169 | 83 |
170 virtual PlatformThreadId threadId() const | 84 virtual PlatformThreadId threadId() const |
171 { | 85 { |
172 ASSERT_NOT_REACHED(); | 86 ASSERT_NOT_REACHED(); |
173 return 0; | 87 return 0; |
174 } | 88 } |
175 | 89 |
176 WebScheduler* scheduler() const override | 90 WebScheduler* scheduler() const override |
177 { | 91 { |
178 return m_webScheduler.get(); | 92 return m_webScheduler; |
179 } | 93 } |
180 | 94 |
181 virtual void enterRunLoop() | 95 virtual void enterRunLoop() |
182 { | 96 { |
183 ASSERT_NOT_REACHED(); | 97 ASSERT_NOT_REACHED(); |
184 } | 98 } |
185 | 99 |
186 virtual void exitRunLoop() | 100 virtual void exitRunLoop() |
187 { | 101 { |
188 ASSERT_NOT_REACHED(); | 102 ASSERT_NOT_REACHED(); |
189 } | 103 } |
190 | 104 |
191 private: | 105 private: |
192 OwnPtr<MockWebScheduler> m_webScheduler; | 106 WebScheduler* m_webScheduler; |
193 }; | 107 }; |
194 | 108 |
195 class TimerTestPlatform : public Platform { | 109 class TimerTestPlatform : public Platform { |
196 public: | 110 public: |
197 TimerTestPlatform() | 111 explicit TimerTestPlatform(WebThread* webThread) |
198 : m_webThread(adoptPtr(new FakeWebThread())) { } | 112 : m_webThread(webThread) |
| 113 , m_timerInterval(-1) { } |
199 ~TimerTestPlatform() override { } | 114 ~TimerTestPlatform() override { } |
200 | 115 |
201 WebThread* currentThread() override | 116 WebThread* currentThread() override |
202 { | 117 { |
203 return m_webThread.get(); | 118 return m_webThread; |
204 } | 119 } |
205 | 120 |
206 void cryptographicallyRandomValues(unsigned char*, size_t) override | 121 void cryptographicallyRandomValues(unsigned char*, size_t) override |
207 { | 122 { |
208 ASSERT_NOT_REACHED(); | 123 ASSERT_NOT_REACHED(); |
209 } | 124 } |
210 | 125 |
211 const unsigned char* getTraceCategoryEnabledFlag(const char* categoryName) o
verride | 126 const unsigned char* getTraceCategoryEnabledFlag(const char* categoryName) o
verride |
212 { | 127 { |
213 static const unsigned char enabled[] = {0}; | 128 static const unsigned char enabled[] = {0}; |
214 return enabled; | 129 return enabled; |
215 } | 130 } |
216 | 131 |
| 132 void setSharedTimerFiredFunction(SharedTimerFunction timerFunction) override |
| 133 { |
| 134 s_timerFunction = timerFunction; |
| 135 } |
| 136 |
| 137 void setSharedTimerFireInterval(double interval) override |
| 138 { |
| 139 m_timerInterval = interval; |
| 140 } |
| 141 |
| 142 virtual void stopSharedTimer() override |
| 143 { |
| 144 m_timerInterval = -1; |
| 145 } |
| 146 |
217 void runUntilIdle() | 147 void runUntilIdle() |
218 { | 148 { |
219 mockScheduler()->runUntilIdle(); | 149 while (hasOneTimerTask()) { |
| 150 gCurrentTimeSecs += m_timerInterval; |
| 151 s_timerFunction(); |
| 152 } |
220 } | 153 } |
221 | 154 |
222 void runUntilIdleOrDeadlinePassed(double deadline) | 155 void runUntilIdleOrDeadlinePassed(double deadline) |
223 { | 156 { |
224 mockScheduler()->runUntilIdleOrDeadlinePassed(deadline); | 157 while (hasOneTimerTask()) { |
| 158 double newTime = gCurrentTimeSecs + m_timerInterval; |
| 159 if (newTime >= deadline) { |
| 160 gCurrentTimeSecs = deadline; |
| 161 break; |
| 162 } |
| 163 gCurrentTimeSecs = newTime; |
| 164 s_timerFunction(); |
| 165 } |
225 } | 166 } |
226 | 167 |
227 bool hasOneTimerTask() const | 168 bool hasOneTimerTask() const |
228 { | 169 { |
229 return mockScheduler()->hasOneTimerTask(); | 170 return s_timerFunction && m_timerInterval >= 0; |
230 } | 171 } |
231 | 172 |
232 long nextTimerTaskDelayMillis() const | 173 long nextTimerTaskDelayMillis() const |
233 { | 174 { |
234 return mockScheduler()->nextTimerTaskDelayMillis(); | 175 ASSERT(hasOneTimerTask()); |
| 176 return static_cast<long>(m_timerInterval * 1000); |
235 } | 177 } |
236 | 178 |
237 private: | 179 private: |
238 MockWebScheduler* mockScheduler() const | 180 WebThread* m_webThread; |
239 { | 181 double m_timerInterval; |
240 return static_cast<MockWebScheduler*>(m_webThread->scheduler()); | |
241 } | |
242 | 182 |
243 OwnPtr<FakeWebThread> m_webThread; | 183 // This needs to be static because the callback is registered only once by |
| 184 // PlatformThreadData. |
| 185 static SharedTimerFunction s_timerFunction; |
244 }; | 186 }; |
245 | 187 |
| 188 Platform::SharedTimerFunction TimerTestPlatform::s_timerFunction; |
| 189 |
246 class TimerTest : public testing::Test { | 190 class TimerTest : public testing::Test { |
247 public: | 191 public: |
248 void SetUp() override | 192 void SetUp() override |
249 { | 193 { |
250 m_platform = adoptPtr(new TimerTestPlatform()); | 194 m_mockWebScheduler = adoptPtr(new MockWebScheduler()); |
| 195 m_fakeWebThread = adoptPtr(new FakeWebThread(m_mockWebScheduler.get())); |
| 196 m_platform = adoptPtr(new TimerTestPlatform(m_fakeWebThread.get())); |
251 m_oldPlatform = Platform::current(); | 197 m_oldPlatform = Platform::current(); |
252 Platform::initialize(m_platform.get()); | 198 Platform::initialize(m_platform.get()); |
253 WTF::setMonotonicallyIncreasingTimeFunction(currentTime); | 199 WTF::setMonotonicallyIncreasingTimeFunction(currentTime); |
254 | 200 |
255 m_runTimes.clear(); | 201 m_runTimes.clear(); |
256 gCurrentTimeSecs = 10.0; | 202 gCurrentTimeSecs = 10.0; |
257 m_startTime = gCurrentTimeSecs; | 203 m_startTime = gCurrentTimeSecs; |
258 } | 204 } |
259 | 205 |
260 void TearDown() override | 206 void TearDown() override |
(...skipping 29 matching lines...) Expand all Loading... |
290 long nextTimerTaskDelayMillis() const | 236 long nextTimerTaskDelayMillis() const |
291 { | 237 { |
292 return m_platform->nextTimerTaskDelayMillis(); | 238 return m_platform->nextTimerTaskDelayMillis(); |
293 } | 239 } |
294 | 240 |
295 protected: | 241 protected: |
296 double m_startTime; | 242 double m_startTime; |
297 std::vector<double> m_runTimes; | 243 std::vector<double> m_runTimes; |
298 | 244 |
299 private: | 245 private: |
| 246 OwnPtr<MockWebScheduler> m_mockWebScheduler; |
| 247 OwnPtr<FakeWebThread> m_fakeWebThread; |
300 OwnPtr<TimerTestPlatform> m_platform; | 248 OwnPtr<TimerTestPlatform> m_platform; |
301 Platform* m_oldPlatform; | 249 Platform* m_oldPlatform; |
302 }; | 250 }; |
303 | 251 |
304 TEST_F(TimerTest, StartOneShot_Zero) | 252 TEST_F(TimerTest, StartOneShot_Zero) |
305 { | 253 { |
306 Timer<TimerTest> timer(this, &TimerTest::countingTask); | 254 Timer<TimerTest> timer(this, &TimerTest::countingTask); |
307 timer.startOneShot(0, FROM_HERE); | 255 timer.startOneShot(0, FROM_HERE); |
308 | 256 |
309 ASSERT(hasOneTimerTask()); | 257 ASSERT(hasOneTimerTask()); |
(...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 MockTimerWithAlignment timer; | 665 MockTimerWithAlignment timer; |
718 timer.setAlignedFireTime(m_startTime + 1.0); | 666 timer.setAlignedFireTime(m_startTime + 1.0); |
719 | 667 |
720 timer.start(0.0, 0.0, FROM_HERE); | 668 timer.start(0.0, 0.0, FROM_HERE); |
721 | 669 |
722 EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval()); | 670 EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval()); |
723 EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); | 671 EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); |
724 EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); | 672 EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); |
725 | 673 |
726 timer.setAlignedFireTime(m_startTime); | 674 timer.setAlignedFireTime(m_startTime); |
727 timer.didChangeAlignmentInterval(monotonicallyIncreasingTime()); | 675 timer.didChangeAlignmentInterval(); |
728 | 676 |
729 EXPECT_FLOAT_EQ(0.0, timer.nextFireInterval()); | 677 EXPECT_FLOAT_EQ(0.0, timer.nextFireInterval()); |
730 EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); | 678 EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval()); |
731 EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); | 679 EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime()); |
732 } | 680 } |
733 | 681 |
734 | 682 |
735 } // namespace | 683 } // namespace |
736 } // namespace blink | 684 } // namespace blink |
OLD | NEW |