blob: ddffbde1f3b2f0c2f8247f09065c639becec27fc [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/async/Looper.h"
#include "android/base/Log.h"
#include "android/base/sockets/SocketUtils.h"
#include "android/base/sockets/ScopedSocket.h"
#include <gtest/gtest.h>
#include <memory>
#include <errno.h>
namespace android {
namespace base {
static bool createScopedSocketPair(ScopedSocket* s0, ScopedSocket* s1) {
int fd0 = -1, fd1 = -1;
if (socketCreatePair(&fd0, &fd1) < 0) {
PLOG(ERROR) << "Could not create socket pair";
return false;
}
s0->reset(fd0);
s1->reset(fd1);
return true;
}
using Duration = Looper::Duration;
TEST(GenericLooper, CreationAndDestruction) {
std::unique_ptr<Looper> looper(Looper::create());
ASSERT_TRUE(looper.get());
}
TEST(GenericLooper, RunWithoutHandlers) {
std::unique_ptr<Looper> looper(Looper::create());
// A simple run() should return immediately. Accept 5 ms max, which is
// huge but might account for overloaded machines when the tests are run.
const Duration kMaxDelayMs = 5;
Duration now0 = looper->nowMs();
looper->run();
Duration now1 = looper->nowMs();
EXPECT_LE(now1 - now0, kMaxDelayMs);
// Similarly, runWithDeadlineMs(kInfiniteDuration) should return
// EWOULDBLOCK.
now0 = looper->nowMs();
EXPECT_EQ(EWOULDBLOCK, looper->runWithDeadlineMs(
Looper::kDurationInfinite));
now1 = looper->nowMs();
EXPECT_LE(now1 - now0, kMaxDelayMs);
// Same with an arbitrary fixed deadline.
now0 = looper->nowMs();
EXPECT_EQ(EWOULDBLOCK, looper->runWithDeadlineMs(now0 + 1000));
now1 = looper->nowMs();
EXPECT_LE(now1 - now0, kMaxDelayMs);
}
static void myTimerCallbackNoop(void* opaque, Looper::Timer* timer) {}
static void myTimerCallbackForceQuit(void* opaque, Looper::Timer* timer) {
auto looper = static_cast<Looper*>(opaque);
looper->forceQuit();
}
static void myTimerCallbackSelfDelete(void* opaque, Looper::Timer* timer) {
delete timer;
}
namespace {
struct TimerGroup {
Looper* looper;
Looper::Timer* timer1;
Looper::Timer* timer2;
};
} // namespace
static void myTimerCallbackStopSecondTimer(void* opaque,
Looper::Timer* timer) {
auto group = static_cast<TimerGroup*>(opaque);
ASSERT_EQ(timer, group->timer1);
group->timer2->stop();
}
static void myTimerCallbackShouldNeverBeCalled(void* opaque,
Looper::Timer* timer) {
GTEST_FAIL() << "This timer callback should never be called!!";
}
TEST(GenericLooper, RunWithSingleTimer) {
std::unique_ptr<Looper> looper(Looper::create());
// Create a timer that runs after 10ms then calls Looper::forceQuit().
std::unique_ptr<Looper::Timer> timer1(looper->createTimer(
myTimerCallbackNoop, looper.get()));
ASSERT_TRUE(timer1.get());
ASSERT_EQ(looper.get(), timer1->parentLooper());
EXPECT_FALSE(timer1->isActive());
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
timer1->startAbsolute(now0 + kDelayMs);
EXPECT_TRUE(timer1->isActive());
int ret = looper->runWithDeadlineMs(Looper::kDurationInfinite);
EXPECT_EQ(EWOULDBLOCK, ret);
Duration now1 = looper->nowMs();
EXPECT_GE(now1 - now0, kDelayMs);
EXPECT_FALSE(timer1->isActive());
}
TEST(GenericLooper, RunWithSingleTimerTimingOut) {
std::unique_ptr<Looper> looper(Looper::create());
// Create a timer that runs after 10ms then calls Looper::forceQuit().
std::unique_ptr<Looper::Timer> timer1(looper->createTimer(
myTimerCallbackNoop, looper.get()));
ASSERT_TRUE(timer1.get());
ASSERT_EQ(looper.get(), timer1->parentLooper());
EXPECT_FALSE(timer1->isActive());
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
timer1->startAbsolute(now0 + kDelayMs);
EXPECT_TRUE(timer1->isActive());
int ret = looper->runWithDeadlineMs(now0 + kDelayMs / 2);
EXPECT_EQ(ETIMEDOUT, ret);
Duration now1 = looper->nowMs();
EXPECT_GE(now1 - now0, kDelayMs / 2);
EXPECT_LT(now1 - now0, kDelayMs);
EXPECT_TRUE(timer1->isActive());
}
TEST(GenericLooper, RunWithSingleTimerForceQuitting) {
std::unique_ptr<Looper> looper(Looper::create());
// Create a timer that runs after 10ms then calls Looper::forceQuit().
std::unique_ptr<Looper::Timer> timer1(looper->createTimer(
myTimerCallbackForceQuit, looper.get()));
ASSERT_TRUE(timer1.get());
ASSERT_EQ(looper.get(), timer1->parentLooper());
EXPECT_FALSE(timer1->isActive());
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
timer1->startAbsolute(now0 + kDelayMs);
EXPECT_TRUE(timer1->isActive());
looper->run();
Duration now1 = looper->nowMs();
EXPECT_GE(now1 - now0, kDelayMs);
EXPECT_FALSE(timer1->isActive());
}
TEST(GenericLooper, RunWithSingleTimerSelfDeleting) {
std::unique_ptr<Looper> looper(Looper::create());
// Create a timer that runs after 10ms then self deletes itself, which
// will force run() to exit.
Looper::Timer* timer1 = looper->createTimer(
myTimerCallbackSelfDelete, looper.get());
ASSERT_TRUE(timer1);
ASSERT_EQ(looper.get(), timer1->parentLooper());
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
timer1->startAbsolute(now0 + kDelayMs);
EXPECT_TRUE(timer1->isActive());
looper->run();
Duration now1 = looper->nowMs();
EXPECT_GE(now1 - now0, kDelayMs);
// TIMER WAS DELETED: EXPECT_FALSE(timer1->isActive());
}
TEST(GenericLooper, RunWithTwoTimersOneStoppingTheOther) {
// Create two timers, one that runs after 10ms, and the other after 20ms
// The first timer callback will stop the second one, which will force
// Looper::run() to exit after 10ms.
std::unique_ptr<Looper> looper(Looper::create());
TimerGroup group = { .looper = looper.get(), };
// Create a timer that runs after 10ms then calls Looper::forceQuit().
std::unique_ptr<Looper::Timer> timer1(looper->createTimer(
myTimerCallbackStopSecondTimer, &group));
ASSERT_TRUE(timer1.get());
ASSERT_EQ(looper.get(), timer1->parentLooper());
group.timer1 = timer1.get();
std::unique_ptr<Looper::Timer> timer2(looper->createTimer(
myTimerCallbackShouldNeverBeCalled, looper.get()));
ASSERT_TRUE(timer1.get());
ASSERT_EQ(looper.get(), timer2->parentLooper());
group.timer2 = timer2.get();
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
timer1->startAbsolute(now0 + kDelayMs);
EXPECT_TRUE(timer1->isActive());
timer2->startAbsolute(now0 + 2 * kDelayMs);
EXPECT_TRUE(timer2->isActive());
looper->run();
Duration now1 = looper->nowMs();
EXPECT_GE(now1 - now0, kDelayMs);
EXPECT_LT(now1 - now0, 2 * kDelayMs);
EXPECT_FALSE(timer1->isActive());
EXPECT_FALSE(timer2->isActive());
}
static void myFdWatchCallbackWriteBooleanFlag(void* opaque, int fd,
unsigned events) {
EXPECT_EQ(Looper::FdWatch::kEventRead, events);
auto pflag = static_cast<bool*>(opaque);
*pflag = true;
}
TEST(GenericLooper, RunWithSingleFdWatchWithTimeout) {
ScopedSocket s0;
ScopedSocket s1;
ASSERT_TRUE(createScopedSocketPair(&s0, &s1));
std::unique_ptr<Looper> looper(Looper::create());
// Create a singl FdWatch to set a boolean flag on socket event,
// but do not activate it (i.e. do not call ::wantRead()), the
// looper should consider there are no active handle and return
// immediately from its loop with EWOULDBLOCK.
bool flag = false;
std::unique_ptr<Looper::FdWatch> watch1(
looper->createFdWatch(s1.get(),
myFdWatchCallbackWriteBooleanFlag,
&flag));
ASSERT_TRUE(watch1.get());
EXPECT_EQ(0U, watch1->poll());
EXPECT_FALSE(flag);
socketSend(s0.get(), "x", 1);
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
int ret = looper->runWithDeadlineMs(now0 + kDelayMs);
Duration now1 = looper->nowMs();
EXPECT_EQ(ETIMEDOUT, ret);
EXPECT_LT(now0 - now1, kDelayMs);
EXPECT_FALSE(flag); // Callback was not called.
}
TEST(GenericLooper, RunWithSingleFdWatchSettingBooleanFlag) {
ScopedSocket s0;
ScopedSocket s1;
ASSERT_TRUE(createScopedSocketPair(&s0, &s1));
std::unique_ptr<Looper> looper(Looper::create());
// Create a singl FdWatch to set a boolean flag on socket event,
// and activate it. Verify that the looper returns before a max delay
// of 100ms due to the event.
bool flag = false;
std::unique_ptr<Looper::FdWatch> watch1(
looper->createFdWatch(s1.get(),
myFdWatchCallbackWriteBooleanFlag,
&flag));
ASSERT_TRUE(watch1.get());
EXPECT_EQ(0U, watch1->poll());
EXPECT_FALSE(flag);
socketSend(s0.get(), "x", 1);
watch1->wantRead();
const Duration kDelayMs = 100;
Duration now0 = looper->nowMs();
int ret = looper->runWithDeadlineMs(now0 + kDelayMs);
Duration now1 = looper->nowMs();
EXPECT_EQ(ETIMEDOUT, ret);
EXPECT_LT(now0 - now1, kDelayMs);
EXPECT_TRUE(flag); // Callback was called.
}
static void myTimerCallbackRemoveFdWatchRead(void* opaque,
Looper::Timer* timer) {
auto watch = static_cast<Looper::FdWatch*>(opaque);
ASSERT_EQ(Looper::FdWatch::kEventRead, watch->poll());
watch->dontWantRead();
}
TEST(GenericLooper, RunTimerAndFdWatchWithTimerRemovingPendingWatch) {
ScopedSocket s0;
ScopedSocket s1;
ASSERT_TRUE(createScopedSocketPair(&s0, &s1));
std::unique_ptr<Looper> looper(Looper::create());
// Create a singl FdWatch to set a boolean flag on socket event,
// and activate it. Verify that the looper returns before a max delay
// of 100ms due to the event.
bool flag = false;
std::unique_ptr<Looper::FdWatch> watch1(
looper->createFdWatch(s1.get(),
myFdWatchCallbackWriteBooleanFlag,
&flag));
ASSERT_TRUE(watch1.get());
EXPECT_EQ(0U, watch1->poll());
EXPECT_FALSE(flag);
watch1->wantRead();
// Write a byte to the socket pair, this will make the watch pending
// for the next Looper::run() call.
socketSend(s0.get(), "x", 1);
// Create a new timer that will be fired immediately and will remove the
// pending watch.
std::unique_ptr<Looper::Timer> timer1(
looper->createTimer(myTimerCallbackRemoveFdWatchRead,
watch1.get()));
const Duration kDelayMs = 10;
Duration now0 = looper->nowMs();
timer1->startAbsolute(now0);
int ret = looper->runWithDeadlineMs(now0 + kDelayMs);
Duration now1 = looper->nowMs();
EXPECT_EQ(ETIMEDOUT, ret);
EXPECT_LT(now0 - now1, kDelayMs);
EXPECT_FALSE(flag); // Callback was not called.
}
} // namespace base
} // namespace android