| // Copyright 2008, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "base/message_loop.h" |
| #include "base/task.h" |
| #include "base/timer.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| class TimerTest : public testing::Test { |
| }; |
| }; |
| |
| // A base class timer task that sanity-checks timer functionality and counts |
| // the number of times it has run. Handles all message loop and memory |
| // management issues. |
| class TimerTask : public Task { |
| public: |
| // Runs all timers to completion. This returns only after all timers have |
| // finished firing. |
| static void RunTimers(); |
| |
| // Creates a new timer. If |repeating| is true, the timer will repeat 10 |
| // times before terminating. |
| // |
| // All timers are managed on the message loop of the thread that calls this |
| // function the first time. |
| TimerTask(int delay, bool repeating); |
| |
| virtual ~TimerTask(); |
| |
| int iterations() const { return iterations_; } |
| const Timer* timer() const { return timer_; } |
| |
| // Resets the timer, if it exists. |
| void Reset(); |
| |
| // Task |
| virtual void Run(); |
| |
| protected: |
| // Shuts down the message loop if necessary. |
| static void QuitMessageLoop(); |
| |
| private: |
| static MessageLoop* message_loop() { |
| static MessageLoop* loop = MessageLoop::current(); |
| return loop; |
| } |
| |
| static int timer_count_; |
| static bool loop_running_; |
| |
| bool timer_running_; |
| int delay_; |
| TimeTicks start_ticks_; |
| int iterations_; |
| Timer* timer_; |
| }; |
| |
| // static |
| void TimerTask::RunTimers() { |
| if (timer_count_ && !loop_running_) { |
| loop_running_ = true; |
| message_loop()->Run(); |
| } |
| } |
| |
| TimerTask::TimerTask(int delay, bool repeating) |
| : timer_running_(false), |
| delay_(delay), |
| start_ticks_(TimeTicks::Now()), |
| iterations_(0), |
| timer_(NULL) { |
| Reset(); // This will just set up the variables to indicate we have a |
| // running timer. |
| timer_ = message_loop()->timer_manager()->StartTimer(delay, this, repeating); |
| } |
| |
| TimerTask::~TimerTask() { |
| if (timer_) { |
| message_loop()->timer_manager()->StopTimer(timer_); |
| delete timer_; |
| } |
| if (timer_running_) { |
| timer_running_ = false; |
| if (--timer_count_ <= 0) |
| QuitMessageLoop(); |
| } |
| } |
| |
| void TimerTask::Reset() { |
| if (!timer_running_) { |
| timer_running_ = true; |
| ++timer_count_; |
| } |
| if (timer_) { |
| start_ticks_ = TimeTicks::Now(); |
| message_loop()->timer_manager()->ResetTimer(timer_); |
| } |
| } |
| |
| void TimerTask::Run() { |
| ++iterations_; |
| |
| // Test that we fired on or after the delay, not before. |
| const TimeTicks ticks = TimeTicks::Now(); |
| EXPECT_LE(delay_, (ticks - start_ticks_).InMilliseconds()); |
| // Note: Add the delay rather than using the ticks recorded. |
| // Repeating timers have already started ticking before |
| // this callback; we pretend they started *now*, then |
| // it might seem like they fire early, when they do not. |
| start_ticks_ += TimeDelta::FromMilliseconds(delay_); |
| |
| // If we're done running, shut down the message loop. |
| if (timer_->repeating() && (iterations_ < 10)) |
| return; // Iterate 10 times before terminating. |
| message_loop()->timer_manager()->StopTimer(timer_); |
| timer_running_ = false; |
| if (--timer_count_ <= 0) |
| QuitMessageLoop(); |
| } |
| |
| // static |
| void TimerTask::QuitMessageLoop() { |
| if (loop_running_) { |
| message_loop()->Quit(); |
| loop_running_ = false; |
| } |
| } |
| |
| int TimerTask::timer_count_ = 0; |
| bool TimerTask::loop_running_ = false; |
| |
| // A task that deletes itself when run. |
| class DeletingTask : public TimerTask { |
| public: |
| DeletingTask(int delay, bool repeating) : TimerTask(delay, repeating) { } |
| |
| // Task |
| virtual void Run(); |
| }; |
| |
| void DeletingTask::Run() { |
| delete this; |
| |
| // Can't call TimerTask::Run() here, we've destroyed ourselves. |
| } |
| |
| // A class that resets another TimerTask when run. |
| class ResettingTask : public TimerTask { |
| public: |
| ResettingTask(int delay, bool repeating, TimerTask* task) |
| : TimerTask(delay, repeating), |
| task_(task) { |
| } |
| |
| virtual void Run(); |
| |
| private: |
| TimerTask* task_; |
| }; |
| |
| void ResettingTask::Run() { |
| task_->Reset(); |
| |
| TimerTask::Run(); |
| } |
| |
| // A class that quits the message loop when run. |
| class QuittingTask : public TimerTask { |
| public: |
| QuittingTask(int delay, bool repeating) : TimerTask(delay, repeating) { } |
| |
| virtual void Run(); |
| }; |
| |
| void QuittingTask::Run() { |
| QuitMessageLoop(); |
| |
| TimerTask::Run(); |
| } |
| |
| void RunTimerTest() { |
| // Make sure oneshot timers work correctly. |
| TimerTask task1(100, false); |
| TimerTask::RunTimers(); |
| EXPECT_EQ(1, task1.iterations()); |
| |
| // Make sure repeating timers work correctly. |
| TimerTask task2(10, true); |
| TimerTask task3(100, true); |
| TimerTask::RunTimers(); |
| EXPECT_EQ(10, task2.iterations()); |
| EXPECT_EQ(10, task3.iterations()); |
| } |
| |
| TEST(TimerTest, TimerComparison) { |
| // Make sure TimerComparison sorts correctly. |
| const TimerTask task1(10, false); |
| const Timer* timer1 = task1.timer(); |
| const TimerTask task2(200, false); |
| const Timer* timer2 = task2.timer(); |
| TimerComparison comparison; |
| EXPECT_FALSE(comparison(timer1, timer2)); |
| EXPECT_TRUE(comparison(timer2, timer1)); |
| } |
| |
| TEST(TimerTest, TimerCase) { |
| RunTimerTest(); |
| } |
| |
| TEST(TimerTest, BrokenTimerCase) { |
| // Simulate faulty early-firing timers. The tasks in RunTimerTest should |
| // nevertheless be invoked after their specified delays, regardless of when |
| // WM_TIMER fires. |
| TimerManager* manager = MessageLoop::current()->timer_manager(); |
| manager->set_use_broken_delay(true); |
| RunTimerTest(); |
| manager->set_use_broken_delay(false); |
| } |
| |
| TEST(TimerTest, DeleteFromRun) { |
| // Make sure TimerManager correctly handles a Task that deletes itself when |
| // run. |
| DeletingTask* deleting_task1 = new DeletingTask(50, true); |
| TimerTask timer_task(150, false); |
| DeletingTask* deleting_task2 = new DeletingTask(250, true); |
| TimerTask::RunTimers(); |
| EXPECT_EQ(1, timer_task.iterations()); |
| } |
| |
| TEST(TimerTest, Reset) { |
| // Make sure resetting a timer after it has fired works. |
| TimerTask timer_task1(250, false); |
| TimerTask timer_task2(100, true); |
| ResettingTask resetting_task1(600, false, &timer_task1); |
| TimerTask::RunTimers(); |
| EXPECT_EQ(2, timer_task1.iterations()); |
| EXPECT_EQ(10, timer_task2.iterations()); |
| |
| // Make sure resetting a timer before it has fired works. This will reset |
| // two timers, then stop the message loop between when they should have |
| // finally fired. |
| TimerTask timer_task3(100, false); |
| TimerTask timer_task4(600, false); |
| ResettingTask resetting_task3(50, false, &timer_task3); |
| ResettingTask resetting_task4(50, false, &timer_task4); |
| QuittingTask quitting_task(300, false); |
| TimerTask::RunTimers(); |
| EXPECT_EQ(1, timer_task3.iterations()); |
| EXPECT_EQ(0, timer_task4.iterations()); |
| } |
| |
| TEST(TimerTest, FifoOrder) { |
| // Creating timers with the same timeout should |
| // always compare to result in FIFO ordering. |
| |
| // Derive from the timer so that we can set it's fire time. |
| // We have to do this, because otherwise, it's possible for |
| // two timers, created back to back, to have different times, |
| // and in that case, we aren't really testing what we want |
| // to test! |
| class MockTimer : public Timer { |
| public: |
| MockTimer(int delay) : Timer(delay, NULL, false) {} |
| void set_fire_time(const Time& t) { fire_time_ = t; } |
| }; |
| |
| class MockTimerManager : public TimerManager { |
| public: |
| MockTimerManager() : TimerManager(MessageLoop::current()) { |
| } |
| |
| // Pops the most-recent to fire timer and returns its timer id. |
| // Returns -1 if there are no timers in the list. |
| int pop() { |
| int rv = -1; |
| Timer* top = PeekTopTimer(); |
| if (top) { |
| rv = top->id(); |
| StopTimer(top); |
| delete top; |
| } |
| return rv; |
| } |
| }; |
| |
| MockTimer t1(0); |
| MockTimer t2(0); |
| t2.set_fire_time(t1.fire_time()); |
| TimerComparison comparison; |
| EXPECT_TRUE(comparison(&t2, &t1)); |
| |
| // Issue a tight loop of timers; most will have the |
| // same timestamp; some will not. Either way, since |
| // all are created with delay(0), the second timer |
| // must always be greater than the first. Then, pop |
| // all the timers and verify that it's a FIFO list. |
| MockTimerManager manager; |
| const int kNumTimers = 1024; |
| for (int i=0; i < kNumTimers; i++) |
| Timer* timer = manager.StartTimer(0, NULL, false); |
| |
| int last_id = -1; |
| int new_id = 0; |
| while((new_id = manager.pop()) > 0) |
| EXPECT_GT(new_id, last_id); |
| } |