blob: ac17973374c508dabd4c75007cbb212b695617c1 [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 <fcntl.h>
#include <time.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "src/developer/debug/shared/fd_watcher.h"
#include "src/developer/debug/shared/platform_message_loop.h"
#if defined(__Fuchsia__)
#include <lib/zx/socket.h>
#include "src/developer/debug/shared/socket_watcher.h"
#endif
namespace debug_ipc {
// This test either passes or hangs forever because the post didn't work.
// We could add a timer timeout, but if regular task posting doesn't work it's
// not clear why timer tasks would.
TEST(MessageLoop, PostQuit) {
PlatformMessageLoop loop;
loop.Init();
loop.PostTask(FROM_HERE, [loop_ptr = &loop]() { loop_ptr->QuitNow(); });
loop.Run();
loop.Cleanup();
}
TEST(MessageLoop, TimerQuit) {
const uint64_t kNano = 1000000000;
PlatformMessageLoop loop;
loop.Init();
struct timespec start;
ASSERT_FALSE(clock_gettime(CLOCK_MONOTONIC, &start));
loop.PostTimer(FROM_HERE, 50, [loop_ptr = &loop]() { loop_ptr->QuitNow(); });
loop.Run();
struct timespec end;
ASSERT_FALSE(clock_gettime(CLOCK_MONOTONIC, &end));
ASSERT_GE(end.tv_sec, start.tv_sec);
uint64_t nsec = (end.tv_sec - start.tv_sec) * kNano;
nsec += end.tv_nsec;
nsec -= start.tv_nsec;
EXPECT_GE(nsec, 50u);
// If we test an upper bound for nsec this test could potentially be flaky.
// We don't actually make any guarantees about the upper bound anyway.
loop.Cleanup();
}
TEST(MessageLoop, WatchPipeFD) {
// Make a pipe to talk about.
int pipefd[2] = {-1, -1};
ASSERT_EQ(0, pipe(pipefd));
ASSERT_NE(-1, pipefd[0]);
ASSERT_NE(-1, pipefd[1]);
int flags = fcntl(pipefd[0], F_GETFD);
flags |= O_NONBLOCK;
ASSERT_EQ(0, fcntl(pipefd[0], F_SETFD, flags));
flags = fcntl(pipefd[1], F_GETFD);
flags |= O_NONBLOCK;
ASSERT_EQ(0, fcntl(pipefd[1], F_SETFD, flags));
class ReadableWatcher : public FDWatcher {
public:
explicit ReadableWatcher(MessageLoop* loop) : loop_(loop) {}
void OnFDReady(int fd, bool read, bool write, bool err) override {
got_read = read;
got_write = write;
got_err = err;
loop_->QuitNow();
}
bool got_read = false;
bool got_write = true;
bool got_err = true;
private:
MessageLoop* loop_;
};
PlatformMessageLoop loop;
loop.Init();
// Scope everything to before MessageLoop::Cleanup().
{
ReadableWatcher watcher(&loop);
// Going to write to pipefd[0] -> read from pipefd[1].
MessageLoop::WatchHandle watch_handle =
loop.WatchFD(MessageLoop::WatchMode::kRead, pipefd[0], &watcher);
ASSERT_TRUE(watch_handle.watching());
// Enqueue a task that should cause pipefd[1] to become readable.
loop.PostTask(FROM_HERE,
[write_fd = pipefd[1]]() { write(write_fd, "Hello", 5); });
// This will quit on success because the OnFDReady callback called QuitNow,
// or hang forever on failure.
// TODO(brettw) add a timeout when timers are supported in the message loop.
loop.Run();
EXPECT_TRUE(watcher.got_read);
EXPECT_FALSE(watcher.got_write);
EXPECT_FALSE(watcher.got_err);
}
loop.Cleanup();
}
#if defined(__Fuchsia__)
TEST(MessageLoop, ZirconSocket) {
zx::socket sender, receiver;
ASSERT_EQ(ZX_OK, zx::socket::create(ZX_SOCKET_STREAM, &sender, &receiver));
class ReadableWatcher : public SocketWatcher {
public:
explicit ReadableWatcher(MessageLoop* loop) : loop_(loop) {}
void OnSocketReadable(zx_handle_t socket_handle) override {
loop_->QuitNow();
}
private:
MessageLoop* loop_;
};
PlatformMessageLoop loop;
loop.Init();
// Scope everything to before MessageLoop::Cleanup().
{
ReadableWatcher watcher(&loop);
MessageLoop::WatchHandle watch_handle = loop.WatchSocket(
MessageLoop::WatchMode::kRead, receiver.get(), &watcher);
ASSERT_TRUE(watch_handle.watching());
// Enqueue a task that should cause receiver to become readable.
loop.PostTask(FROM_HERE,
[&sender]() { sender.write(0, "Hello", 5, nullptr); });
// This will quit on success because the OnSocketReadable callback called
// QuitNow, or hang forever on failure.
// TODO(brettw) add a timeout when timers are supported in the message loop.
loop.Run();
}
loop.Cleanup();
}
#endif
} // namespace debug_ipc