blob: 7b03e9498e2fa6259d0043dc06707857e1072d30 [file] [log] [blame]
// Copyright 2014 The Chromium 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 "config.h"
#include "platform/scheduler/Scheduler.h"
#include "platform/TestingPlatformSupport.h"
#include "platform/TraceLocation.h"
#include "public/platform/Platform.h"
#include "public/platform/WebThread.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>
#include <vector>
using blink::Scheduler;
using namespace std;
namespace {
class TestMainThread : public blink::WebThread {
public:
// blink::WebThread implementation.
virtual void postTask(Task* task) OVERRIDE
{
m_pendingTasks.append(adoptPtr(task));
}
virtual void postDelayedTask(Task* task, long long delayMs) OVERRIDE
{
ASSERT_NOT_REACHED();
}
virtual bool isCurrentThread() const OVERRIDE
{
return true;
}
virtual void enterRunLoop() OVERRIDE
{
ASSERT_NOT_REACHED();
}
virtual void exitRunLoop() OVERRIDE
{
ASSERT_NOT_REACHED();
}
void runPendingTasks()
{
while (!m_pendingTasks.isEmpty())
m_pendingTasks.takeFirst()->run();
}
private:
WTF::Deque<OwnPtr<Task> > m_pendingTasks;
};
class SchedulerTestingPlatformSupport : blink::TestingPlatformSupport {
public:
SchedulerTestingPlatformSupport()
: TestingPlatformSupport(TestingPlatformSupport::Config())
, m_sharedTimerFunction(nullptr)
, m_sharedTimerRunning(false)
, m_sharedTimerFireInterval(0)
{
}
// blink::Platform implementation.
virtual blink::WebThread* currentThread() OVERRIDE
{
return &m_mainThread;
}
virtual void setSharedTimerFiredFunction(SharedTimerFunction timerFunction) OVERRIDE
{
m_sharedTimerFunction = timerFunction;
}
virtual void setSharedTimerFireInterval(double)
{
m_sharedTimerFireInterval = 0;
m_sharedTimerRunning = true;
}
virtual void stopSharedTimer()
{
m_sharedTimerRunning = false;
}
void runPendingTasks()
{
m_mainThread.runPendingTasks();
}
bool sharedTimerRunning() const
{
return m_sharedTimerRunning;
}
double sharedTimerFireInterval() const
{
return m_sharedTimerFireInterval;
}
void triggerSharedTimer()
{
m_sharedTimerFunction();
}
private:
TestMainThread m_mainThread;
SharedTimerFunction m_sharedTimerFunction;
bool m_sharedTimerRunning;
double m_sharedTimerFireInterval;
};
class SchedulerTest : public testing::Test {
public:
SchedulerTest()
: m_reentrantCount(0)
, m_maxRecursion(4)
{
Scheduler::initializeOnMainThread();
m_scheduler = Scheduler::shared();
}
~SchedulerTest()
{
Scheduler::shutdown();
}
void runPendingTasks()
{
m_platformSupport.runPendingTasks();
}
void appendToVector(string value)
{
m_order.push_back(value);
}
void appendToVectorReentrantTask()
{
m_reentrantOrder.push_back(m_reentrantCount++);
if (m_reentrantCount > m_maxRecursion)
return;
Scheduler::shared()->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantTask, this));
}
void appendToVectorReentrantInputTask()
{
m_reentrantOrder.push_back(m_reentrantCount++);
if (m_reentrantCount > m_maxRecursion)
return;
m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantInputTask, this));
}
void appendToVectorReentrantCompositorTask()
{
m_reentrantOrder.push_back(m_reentrantCount++);
if (m_reentrantCount > m_maxRecursion)
return;
m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantCompositorTask, this));
}
protected:
SchedulerTestingPlatformSupport m_platformSupport;
Scheduler* m_scheduler;
std::vector<string> m_order;
std::vector<int> m_reentrantOrder;
int m_reentrantCount;
int m_maxRecursion;
};
void orderedTestTask(int value, int* result)
{
*result = (*result << 4) | value;
}
void unorderedTestTask(int value, int* result)
{
*result += value;
}
void idleTestTask(int value, int* result, double allottedTime)
{
*result += value;
}
TEST_F(SchedulerTest, TestPostTask)
{
int result = 0;
m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 1, &result));
m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 2, &result));
m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 3, &result));
m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 4, &result));
runPendingTasks();
EXPECT_EQ(0x1234, result);
}
TEST_F(SchedulerTest, TestPostMixedTaskTypes)
{
int result = 0;
m_scheduler->postTask(FROM_HERE, WTF::bind(&unorderedTestTask, 1, &result));
m_scheduler->postInputTask(FROM_HERE, WTF::bind(&unorderedTestTask, 2, &result));
m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&unorderedTestTask, 4, &result));
m_scheduler->postTask(FROM_HERE, WTF::bind(&unorderedTestTask, 8, &result));
runPendingTasks();
EXPECT_EQ(15, result);
}
int s_sharedTimerTickCount;
void sharedTimerFunction()
{
s_sharedTimerTickCount++;
}
TEST_F(SchedulerTest, TestSharedTimer)
{
s_sharedTimerTickCount = 0;
m_scheduler->setSharedTimerFiredFunction(&sharedTimerFunction);
EXPECT_FALSE(m_platformSupport.sharedTimerRunning());
m_scheduler->setSharedTimerFireInterval(0);
EXPECT_TRUE(m_platformSupport.sharedTimerRunning());
m_platformSupport.triggerSharedTimer();
EXPECT_EQ(1, s_sharedTimerTickCount);
m_scheduler->stopSharedTimer();
EXPECT_FALSE(m_platformSupport.sharedTimerRunning());
m_scheduler->setSharedTimerFiredFunction(nullptr);
EXPECT_FALSE(m_platformSupport.sharedTimerRunning());
}
TEST_F(SchedulerTest, TestIdleTask)
{
// TODO: Check task allottedTime when implemented in the scheduler.
int result = 0;
m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
runPendingTasks();
EXPECT_EQ(4, result);
}
TEST_F(SchedulerTest, TestTaskPrioritization)
{
m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("L1")));
m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("L2")));
m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("I1")));
m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("I2")));
m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("C1")));
m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("C2")));
runPendingTasks();
EXPECT_THAT(m_order, testing::ElementsAre(
string("I1"), string("I2"), string("C1"), string("C2"), string("L1"), string("L2")));
}
TEST_F(SchedulerTest, TestRentrantTask)
{
m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantTask, this));
runPendingTasks();
EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
}
TEST_F(SchedulerTest, TestRentrantInputTaskDuringShutdown)
{
m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantInputTask, this));
Scheduler::shutdown();
EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
}
TEST_F(SchedulerTest, TestRentrantCompositorTaskDuringShutdown)
{
m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantCompositorTask, this));
Scheduler::shutdown();
EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
}
} // namespace