TEST(AsyncTask, ThreadSafety) { RunLoop loop; unsigned count = 0; AsyncTask async([&count] { ++count; }); unsigned numThreads = 25; auto callback = [&] { if (!--numThreads) { loop.stop(); } }; std::vector<std::unique_ptr<Thread<TestWorker>>> threads; std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests; ThreadContext context = {"Test"}; for (unsigned i = 0; i < numThreads; ++i) { std::unique_ptr<Thread<TestWorker>> thread = std::make_unique<Thread<TestWorker>>(context, &async); requests.push_back( thread->invokeWithCallback(&TestWorker::runWithCallback, callback)); threads.push_back(std::move(thread)); } loop.run(); // We expect here more than 1 but 1 would also be // a valid result, although very unlikely (I hope). EXPECT_GT(count, 0u); }
TEST(AsyncTask, ThreadSafety) { RunLoop loop; unsigned count = 0, numThreads = 25; std::atomic_uint completed(numThreads); AsyncTask async([&count] { ++count; }); auto retainer = Scheduler::GetBackground(); auto mailbox = std::make_shared<Mailbox>(*retainer); TestWorker worker(&async); ActorRef<TestWorker> workerRef(worker, mailbox); for (unsigned i = 0; i < numThreads; ++i) { // The callback runs on the worker, thus the atomic type. workerRef.invoke(&TestWorker::runWithCallback, [&] { if (!--completed) loop.stop(); }); } loop.run(); // We expect here more than 1 but 1 would also be // a valid result, although very unlikely (I hope). EXPECT_GT(count, 0u); }
TEST(Timer, Repeat) { RunLoop loop; Timer timer; unsigned count = 10; auto callback = [&] { if (!--count) { loop.stop(); } }; auto interval = std::chrono::milliseconds(50); auto expectedTotalTime = interval * count; auto first = mbgl::Clock::now(); timer.start(interval, interval, callback); loop.run(); using namespace std::chrono; auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); EXPECT_GE(totalTime, expectedTotalTime * 0.8); EXPECT_LE(totalTime, expectedTotalTime * 1.2); }
TEST(Timer, StartOverrides) { RunLoop loop; Timer timer; auto interval1 = std::chrono::milliseconds(50); auto interval2 = std::chrono::milliseconds(250); auto expectedTotalTime = interval1 + interval2; int count = 0; auto callback2 = [&] { ++count; loop.stop(); }; auto callback1 = [&] { ++count; timer.start(interval2, mbgl::Duration::zero(), callback2); }; auto first = mbgl::Clock::now(); timer.start(interval1, mbgl::Duration::zero(), callback1); loop.run(); using namespace std::chrono; auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); EXPECT_EQ(count, 2); EXPECT_GE(totalTime, expectedTotalTime * 0.8); EXPECT_LE(totalTime, expectedTotalTime * 1.2); }
TEST(Timer, DestroyShouldStop) { RunLoop loop; auto timer1 = std::make_unique<Timer>(); Timer timer2; auto interval1 = std::chrono::milliseconds(50); auto interval2 = std::chrono::milliseconds(250); auto expectedTotalTime = interval2; int count = 0; auto callback1 = [&] { ++count; timer1.reset(); }; auto callback2 = [&] { ++count; loop.stop(); }; auto first = mbgl::Clock::now(); timer1->start(interval1, interval1, callback1); timer2.start(interval2, mbgl::Duration::zero(), callback2); loop.run(); using namespace std::chrono; auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); EXPECT_EQ(count, 2); EXPECT_GE(totalTime, expectedTotalTime * 0.8); EXPECT_LE(totalTime, expectedTotalTime * 1.2); }
TEST(Timer, Basic) { RunLoop loop; Timer timer; auto callback = [&loop] { loop.stop(); }; auto interval = std::chrono::milliseconds(300); auto expectedTotalTime = interval; auto first = mbgl::Clock::now(); timer.start(interval, mbgl::Duration::zero(), callback); loop.run(); using namespace std::chrono; auto totalTime = duration_cast<milliseconds>(mbgl::Clock::now() - first); // These are not high precision timers. Especially libuv uses // cached time from the beginning of of the main loop iteration // and it is very prone to fire earlier, which is, odd. EXPECT_GE(totalTime, expectedTotalTime * 0.8); EXPECT_LE(totalTime, expectedTotalTime * 1.2); }