151 lines
3.1 KiB
C++
151 lines
3.1 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <cxxreact/CxxMessageQueue.h>
|
|
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
|
|
using namespace facebook::react;
|
|
using detail::EventFlag;
|
|
using time_point = EventFlag::time_point;
|
|
|
|
using std::chrono::milliseconds;
|
|
|
|
namespace {
|
|
time_point now() {
|
|
return std::chrono::steady_clock::now();
|
|
}
|
|
|
|
std::shared_ptr<CxxMessageQueue> createAndStartQueue(EventFlag& finishedFlag) {
|
|
auto q = std::make_shared<CxxMessageQueue>();
|
|
std::thread t([q, &finishedFlag] () mutable {
|
|
auto loop = CxxMessageQueue::getRunLoop(q);
|
|
// Note: make sure that no stack frames above loop() have a strong reference to q.
|
|
q.reset();
|
|
loop();
|
|
finishedFlag.set();
|
|
});
|
|
t.detach();
|
|
return q;
|
|
}
|
|
|
|
// This is just used to start up a queue for a test and make sure that it is
|
|
// actually shut down after the test.
|
|
struct QueueWithThread {
|
|
QueueWithThread() {
|
|
queue = createAndStartQueue(done);
|
|
}
|
|
|
|
~QueueWithThread() {
|
|
queue->quitSynchronous();
|
|
queue.reset();
|
|
if (!done.wait_until(now() + milliseconds(300))) {
|
|
ADD_FAILURE() << "Queue did not exit";
|
|
}
|
|
}
|
|
|
|
EventFlag done;
|
|
std::shared_ptr<CxxMessageQueue> queue;
|
|
};
|
|
}
|
|
|
|
TEST(CxxMessageQueue, TestQuit) {
|
|
EventFlag done;
|
|
auto q = createAndStartQueue(done);
|
|
q->quitSynchronous();
|
|
if (!done.wait_until(now() + milliseconds(300))) {
|
|
FAIL() << "Queue did not exit runloop after quitSynchronous";
|
|
}
|
|
}
|
|
|
|
TEST(CxxMessageQueue, TestPostTask) {
|
|
QueueWithThread qt;
|
|
auto q = qt.queue;
|
|
|
|
EventFlag flag;
|
|
q->runOnQueue([&] {
|
|
flag.set();
|
|
});
|
|
flag.wait();
|
|
}
|
|
|
|
TEST(CxxMessageQueue, TestPostTaskMultiple) {
|
|
QueueWithThread qt;
|
|
auto q = qt.queue;
|
|
|
|
std::vector<EventFlag> vec(10);
|
|
for (int i = 0; i < 10; i++) {
|
|
q->runOnQueue([&, i] {
|
|
vec[i].set();
|
|
});
|
|
}
|
|
for (int i = 0; i < 10; i++) {
|
|
vec[i].wait();
|
|
}
|
|
}
|
|
|
|
TEST(CxxMessageQueue, TestQueuedTaskOrdering) {
|
|
QueueWithThread qt;
|
|
auto q = qt.queue;
|
|
|
|
// Block the runloop so we can get some queued tasks.
|
|
EventFlag wait;
|
|
q->runOnQueue([&] {
|
|
wait.wait();
|
|
});
|
|
|
|
// These tasks should run in order.
|
|
int failed = -1;
|
|
int i = 0;
|
|
for (int j = 0; j < 10; j++) {
|
|
q->runOnQueue([&, j] {
|
|
if (i != j) {
|
|
failed = j;
|
|
}
|
|
i++;
|
|
});
|
|
}
|
|
wait.set();
|
|
|
|
// Flush the queue.
|
|
q->runOnQueueSync([&] {});
|
|
|
|
ASSERT_EQ(failed, -1);
|
|
ASSERT_EQ(i, 10);
|
|
}
|
|
|
|
TEST(CxxMessageQueue, TestDelayedTaskOrdering) {
|
|
QueueWithThread qt;
|
|
auto q = qt.queue;
|
|
|
|
// Block the runloop so we can get some queued tasks.
|
|
EventFlag wait;
|
|
q->runOnQueue([&] {
|
|
wait.wait();
|
|
});
|
|
|
|
int ids[] = {8, 4, 6, 1, 3, 2, 9, 5, 0, 7};
|
|
|
|
int failed = -1;
|
|
int i = 0;
|
|
EventFlag done;
|
|
// If this loop actually takes longer than the difference between delays, the
|
|
// ordering could get screwed up :/
|
|
for (int j = 0; j < 10; j++) {
|
|
q->runOnQueueDelayed([&, j] {
|
|
if (i != ids[j]) {
|
|
failed = j;
|
|
}
|
|
i++;
|
|
if (ids[j] == 9) {
|
|
done.set();
|
|
}
|
|
}, 50 + 10 * ids[j]);
|
|
}
|
|
wait.set();
|
|
done.wait();
|
|
|
|
ASSERT_EQ(failed, -1);
|
|
ASSERT_EQ(i, 10);
|
|
}
|