blob: 17c70c8a6cbe32e577a10c035d87d74fb9e5aadf [file] [log] [blame]
// 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