blob: d359e0759cb533f8b91485c860a857fc995e5ad9 [file] [log] [blame]
// Copyright 2016 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 "topaz/lib/deprecated_loop/message_loop.h"
#include <lib/fdio/io.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <poll.h>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "gtest/gtest.h"
#include "lib/fsl/tasks/fd_waiter.h"
#include "lib/fxl/files/unique_fd.h"
#include "lib/fxl/functional/closure.h"
#include "lib/fxl/functional/make_copyable.h"
#include "lib/fxl/macros.h"
namespace deprecated_loop {
namespace {
TEST(MessageLoop, Current) {
EXPECT_TRUE(MessageLoop::GetCurrent() == nullptr);
{
MessageLoop message_loop;
EXPECT_EQ(&message_loop, MessageLoop::GetCurrent());
}
EXPECT_TRUE(MessageLoop::GetCurrent() == nullptr);
}
TEST(MessageLoop, RunsTasksOnCurrentThread) {
fxl::RefPtr<fxl::TaskRunner> task_runner;
{
MessageLoop loop;
task_runner = loop.task_runner();
EXPECT_TRUE(task_runner->RunsTasksOnCurrentThread());
bool run_on_other_thread;
std::thread t([task_runner, &run_on_other_thread]() {
run_on_other_thread = task_runner->RunsTasksOnCurrentThread();
});
t.join();
EXPECT_FALSE(run_on_other_thread);
}
EXPECT_FALSE(task_runner->RunsTasksOnCurrentThread());
}
TEST(MessageLoop, CanRunTasks) {
bool did_run = false;
MessageLoop loop;
loop.task_runner()->PostTask([&did_run, &loop]() {
EXPECT_FALSE(did_run);
did_run = true;
});
loop.RunUntilIdle();
EXPECT_TRUE(did_run);
}
TEST(MessageLoop, CanPostTasksFromTasks) {
bool did_run = false;
MessageLoop loop;
fxl::Closure nested_task = [&did_run, &loop]() {
EXPECT_FALSE(did_run);
did_run = true;
};
loop.task_runner()->PostTask([&nested_task, &loop]() {
loop.task_runner()->PostTask(std::move(nested_task));
});
loop.RunUntilIdle();
EXPECT_TRUE(did_run);
}
TEST(MessageLoop, TriplyNestedTasks) {
std::vector<std::string> tasks;
MessageLoop loop;
loop.task_runner()->PostTask([&tasks, &loop]() {
tasks.push_back("one");
loop.task_runner()->PostTask([&tasks, &loop]() {
tasks.push_back("two");
loop.task_runner()->PostTask(
[&tasks, &loop]() { tasks.push_back("three"); });
});
});
loop.RunUntilIdle();
EXPECT_EQ(3u, tasks.size());
EXPECT_EQ("one", tasks[0]);
EXPECT_EQ("two", tasks[1]);
EXPECT_EQ("three", tasks[2]);
}
TEST(MessageLoop, CanRunTasksInOrder) {
std::vector<std::string> tasks;
MessageLoop loop;
loop.task_runner()->PostTask([&tasks]() { tasks.push_back("0"); });
loop.task_runner()->PostTask([&tasks]() { tasks.push_back("1"); });
loop.PostQuitTask();
loop.task_runner()->PostTask([&tasks]() { tasks.push_back("2"); });
loop.RunUntilIdle();
EXPECT_EQ(2u, tasks.size());
EXPECT_EQ("0", tasks[0]);
EXPECT_EQ("1", tasks[1]);
}
TEST(MessageLoop, CanPreloadTasks) {
auto incoming_queue = fxl::MakeRefCounted<internal::IncomingTaskQueue>();
bool did_run = false;
MessageLoop* loop_ptr = nullptr;
incoming_queue->PostTask([&did_run, &loop_ptr]() {
EXPECT_FALSE(did_run);
did_run = true;
});
MessageLoop loop(std::move(incoming_queue));
loop_ptr = &loop;
loop.RunUntilIdle();
EXPECT_TRUE(did_run);
}
TEST(MessageLoop, AfterTaskCallbacks) {
std::vector<std::string> tasks;
MessageLoop loop;
loop.SetAfterTaskCallback([&tasks] { tasks.push_back("callback"); });
loop.task_runner()->PostTask([&tasks] { tasks.push_back("0"); });
loop.task_runner()->PostTask([&tasks] { tasks.push_back("1"); });
loop.PostQuitTask();
loop.task_runner()->PostTask([&tasks] { tasks.push_back("2"); });
loop.RunUntilIdle();
EXPECT_EQ(5u, tasks.size());
EXPECT_EQ("0", tasks[0]);
EXPECT_EQ("callback", tasks[1]);
EXPECT_EQ("1", tasks[2]);
EXPECT_EQ("callback", tasks[3]);
}
TEST(MessageLoop, RemoveAfterTaskCallbacksDuringCallback) {
std::vector<std::string> tasks;
MessageLoop loop;
loop.SetAfterTaskCallback([&tasks, &loop]() {
tasks.push_back("callback");
loop.ClearAfterTaskCallback();
});
loop.task_runner()->PostTask([&tasks] { tasks.push_back("0"); });
loop.task_runner()->PostTask([&tasks] { tasks.push_back("1"); });
loop.RunUntilIdle();
EXPECT_EQ(3u, tasks.size());
EXPECT_EQ("0", tasks[0]);
EXPECT_EQ("callback", tasks[1]);
EXPECT_EQ("1", tasks[2]);
}
class DestructorObserver {
public:
DestructorObserver(fxl::Closure callback) : callback_(std::move(callback)) {}
~DestructorObserver() { callback_(); }
private:
fxl::Closure callback_;
};
TEST(MessageLoop, TaskDestructionTime) {
bool destructed = false;
fxl::RefPtr<fxl::TaskRunner> task_runner;
{
MessageLoop loop;
task_runner = fxl::RefPtr<fxl::TaskRunner>(loop.task_runner());
loop.RunUntilIdle();
auto observer1 = std::make_unique<DestructorObserver>(
[&destructed] { destructed = true; });
task_runner->PostTask(fxl::MakeCopyable([p = std::move(observer1)]() {}));
EXPECT_FALSE(destructed);
}
EXPECT_TRUE(destructed);
destructed = false;
auto observer2 = std::make_unique<DestructorObserver>(
[&destructed] { destructed = true; });
task_runner->PostTask(fxl::MakeCopyable([p = std::move(observer2)]() {}));
EXPECT_TRUE(destructed);
}
TEST(MessageLoop, CanQuitCurrent) {
int count = 0;
MessageLoop loop;
loop.task_runner()->PostTask([&count]() {
count++;
MessageLoop::GetCurrent()->QuitNow();
});
loop.task_runner()->PostTask([&count]() { count++; });
loop.RunUntilIdle();
EXPECT_EQ(1, count);
}
TEST(MessageLoop, CanQuitManyTimes) {
MessageLoop loop;
loop.QuitNow();
loop.QuitNow();
loop.PostQuitTask();
loop.RunUntilIdle();
loop.QuitNow();
loop.QuitNow();
}
// Tests that waiting on files in a MessageLoop works.
TEST(MessageLoop, FDWaiter) {
// Create an event and an FD that reflects that event. The fd
// shares ownership of the event.
zx::event fdevent;
EXPECT_EQ(zx::event::create(0u, &fdevent), ZX_OK);
fxl::UniqueFD fd(
fdio_handle_fd(fdevent.get(), ZX_USER_SIGNAL_0, 0, /*shared=*/true));
EXPECT_TRUE(fd.is_valid());
bool callback_ran = false;
{
MessageLoop message_loop;
fsl::FDWaiter waiter;
std::thread thread([&fdevent]() {
// Poke the fdevent, which pokes the fd.
EXPECT_EQ(fdevent.signal(0u, ZX_USER_SIGNAL_0), ZX_OK);
});
auto callback = [&callback_ran, &message_loop](zx_status_t success,
uint32_t events) {
EXPECT_EQ(success, ZX_OK);
EXPECT_EQ(events, static_cast<uint32_t>(POLLIN));
callback_ran = true;
message_loop.QuitNow();
};
EXPECT_TRUE(waiter.Wait(callback, fd.get(), POLLIN));
message_loop.Run();
thread.join();
}
EXPECT_TRUE(callback_ran);
}
} // namespace
} // namespace deprecated_loop