Index: base/message_loop/message_pump_mac_unittest.cc |
diff --git a/base/message_loop/message_pump_mac_unittest.cc b/base/message_loop/message_pump_mac_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..17c70c8a6cbe32e577a10c035d87d74fb9e5aadf |
--- /dev/null |
+++ b/base/message_loop/message_pump_mac_unittest.cc |
@@ -0,0 +1,108 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/message_loop/message_pump_mac.h" |
+ |
+#include "base/mac/scoped_cftyperef.h" |
+#include "base/macros.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace base { |
+ |
+class TestMessagePumpCFRunLoopBase { |
+ public: |
+ bool TestCanInvalidateTimers() { |
+ return MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers(); |
+ } |
+ static void SetTimerValid(CFRunLoopTimerRef timer, bool valid) { |
+ MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(timer, valid); |
+ } |
+ |
+ static void PerformTimerCallback(CFRunLoopTimerRef timer, void* info) { |
+ TestMessagePumpCFRunLoopBase* self = |
+ static_cast<TestMessagePumpCFRunLoopBase*>(info); |
+ self->timer_callback_called_ = true; |
+ |
+ if (self->invalidate_timer_in_callback_) { |
+ SetTimerValid(timer, false); |
+ } |
+ } |
+ |
+ bool invalidate_timer_in_callback_; |
+ |
+ bool timer_callback_called_; |
+}; |
+ |
+TEST(MessagePumpMacTest, TestCanInvalidateTimers) { |
+ TestMessagePumpCFRunLoopBase message_pump_test; |
+ |
+ // Catch whether or not the use of private API ever starts failing. |
+ EXPECT_TRUE(message_pump_test.TestCanInvalidateTimers()); |
+} |
+ |
+TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) { |
+ TestMessagePumpCFRunLoopBase message_pump_test; |
+ |
+ CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); |
+ timer_context.info = &message_pump_test; |
+ const CFTimeInterval kCFTimeIntervalMax = |
+ std::numeric_limits<CFTimeInterval>::max(); |
+ ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(CFRunLoopTimerCreate( |
+ NULL, // allocator |
+ kCFTimeIntervalMax, // fire time |
+ kCFTimeIntervalMax, // interval |
+ 0, // flags |
+ 0, // priority |
+ TestMessagePumpCFRunLoopBase::PerformTimerCallback, &timer_context)); |
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), test_timer, |
+ kMessageLoopExclusiveRunLoopMode); |
+ |
+ // Sanity check. |
+ EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); |
+ |
+ // Confirm that the timer fires as expected, and that it's not a one-time-use |
+ // timer (those timers are invalidated after they fire). |
+ CFAbsoluteTime next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01; |
+ CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time); |
+ message_pump_test.timer_callback_called_ = false; |
+ message_pump_test.invalidate_timer_in_callback_ = false; |
+ CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true); |
+ EXPECT_TRUE(message_pump_test.timer_callback_called_); |
+ EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); |
+ |
+ // As a repeating timer, the timer should have a new fire date set in the |
+ // future. |
+ EXPECT_GT(CFRunLoopTimerGetNextFireDate(test_timer), next_fire_time); |
+ |
+ // Try firing the timer, and invalidating it within its callback. |
+ next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01; |
+ CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time); |
+ message_pump_test.timer_callback_called_ = false; |
+ message_pump_test.invalidate_timer_in_callback_ = true; |
+ CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true); |
+ EXPECT_TRUE(message_pump_test.timer_callback_called_); |
+ EXPECT_FALSE(CFRunLoopTimerIsValid(test_timer)); |
+ |
+ // The CFRunLoop believes the timer is invalid, so it should not have a |
+ // fire date. |
+ EXPECT_EQ(0, CFRunLoopTimerGetNextFireDate(test_timer)); |
+ |
+ // Now mark the timer as valid and confirm that it still fires correctly. |
+ TestMessagePumpCFRunLoopBase::SetTimerValid(test_timer, true); |
+ EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); |
+ next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01; |
+ CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time); |
+ message_pump_test.timer_callback_called_ = false; |
+ message_pump_test.invalidate_timer_in_callback_ = false; |
+ CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true); |
+ EXPECT_TRUE(message_pump_test.timer_callback_called_); |
+ EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); |
+ |
+ // Confirm that the run loop again gave it a new fire date in the future. |
+ EXPECT_GT(CFRunLoopTimerGetNextFireDate(test_timer), next_fire_time); |
+ |
+ CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer, |
+ kMessageLoopExclusiveRunLoopMode); |
+} |
+} // namespace base |