blob: ce3a79cd996307ed8a2971972a82dcd5e0ab01e6 [file] [log] [blame]
// Copyright 2016 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 "build/build_config.h"
#include "core/testing/sim/SimRequest.h"
#include "core/testing/sim/SimTest.h"
#include "platform/scheduler/renderer/web_view_scheduler.h"
#include "platform/testing/UnitTestHelpers.h"
#include "public/platform/Platform.h"
#include "public/platform/TaskType.h"
#include "public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "public/web/WebLocalFrame.h"
#include "public/web/WebScriptExecutionCallback.h"
#include "public/web/WebScriptSource.h"
#include "public/web/WebView.h"
namespace blink {
namespace virtual_time_test {
class ScriptExecutionCallbackHelper : public WebScriptExecutionCallback {
public:
const String Result() const { return result_; }
private:
void Completed(const WebVector<v8::Local<v8::Value>>& values) override {
if (!values.IsEmpty() && !values[0].IsEmpty() && values[0]->IsString()) {
result_ = ToCoreString(v8::Local<v8::String>::Cast(values[0]));
}
}
String result_;
};
class VirtualTimeTest : public SimTest {
protected:
String ExecuteJavaScript(String script_source) {
ScriptExecutionCallbackHelper callback_helper;
WebView()
.MainFrame()
->ToWebLocalFrame()
->RequestExecuteScriptAndReturnValue(
WebScriptSource(WebString(script_source)), false, &callback_helper);
return callback_helper.Result();
}
void TearDown() override {
// The SimTest destructor calls runPendingTasks. This is a problem because
// if there are any repeating tasks, advancing virtual time will cause the
// runloop to busy loop. Disabling virtual time here fixes that.
WebView().Scheduler()->DisableVirtualTimeForTesting();
}
void StopVirtualTimeAndExitRunLoop() {
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kPause);
testing::ExitRunLoop();
}
// Some task queues may have repeating v8 tasks that run forever so we impose
// a hard (virtual) time limit.
void RunTasksForPeriod(double delay_ms) {
scheduler::GetSingleThreadTaskRunnerForTesting()->PostDelayedTask(
FROM_HERE,
WTF::Bind(&VirtualTimeTest::StopVirtualTimeAndExitRunLoop,
WTF::Unretained(this)),
TimeDelta::FromMillisecondsD(delay_ms));
testing::EnterRunLoop();
}
};
// http://crbug.com/633321
#if defined(OS_ANDROID)
#define MAYBE_DOMTimersFireInExpectedOrder DISABLED_DOMTimersFireInExpectedOrder
#else
#define MAYBE_DOMTimersFireInExpectedOrder DOMTimersFireInExpectedOrder
#endif
TEST_F(VirtualTimeTest, MAYBE_DOMTimersFireInExpectedOrder) {
WebView().Scheduler()->EnableVirtualTime();
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kAdvance);
ExecuteJavaScript(
"var run_order = [];"
"function timerFn(delay, value) {"
" setTimeout(function() { run_order.push(value); }, delay);"
"};"
"var one_minute = 60 * 1000;"
"timerFn(one_minute * 4, 'a');"
"timerFn(one_minute * 2, 'b');"
"timerFn(one_minute, 'c');");
// Normally the JS runs pretty much instantly but the timer callbacks will
// take 4 mins to fire, but thanks to timer fast forwarding we can make them
// fire immediatly.
RunTasksForPeriod(60 * 1000 * 4);
EXPECT_EQ("c, b, a", ExecuteJavaScript("run_order.join(', ')"));
}
// http://crbug.com/633321
#if defined(OS_ANDROID)
#define MAYBE_SetInterval DISABLED_SetInterval
#else
#define MAYBE_SetInterval SetInterval
#endif
TEST_F(VirtualTimeTest, MAYBE_SetInterval) {
WebView().Scheduler()->EnableVirtualTime();
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kAdvance);
ExecuteJavaScript(
"var run_order = [];"
"var count = 10;"
"var interval_handle = setInterval(function() {"
" if (--window.count == 0) {"
" clearInterval(interval_handle);"
" }"
" run_order.push(count);"
"}, 1000);"
"setTimeout(function() { run_order.push('timer'); }, 1500);");
RunTasksForPeriod(10001);
EXPECT_EQ("9, timer, 8, 7, 6, 5, 4, 3, 2, 1, 0",
ExecuteJavaScript("run_order.join(', ')"));
}
// http://crbug.com/633321
#if defined(OS_ANDROID)
#define MAYBE_AllowVirtualTimeToAdvance DISABLED_AllowVirtualTimeToAdvance
#else
#define MAYBE_AllowVirtualTimeToAdvance AllowVirtualTimeToAdvance
#endif
TEST_F(VirtualTimeTest, MAYBE_AllowVirtualTimeToAdvance) {
WebView().Scheduler()->EnableVirtualTime();
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kPause);
ExecuteJavaScript(
"var run_order = [];"
"timerFn = function(delay, value) {"
" setTimeout(function() { run_order.push(value); }, delay);"
"};"
"timerFn(100, 'a');"
"timerFn(10, 'b');"
"timerFn(1, 'c');");
testing::RunPendingTasks();
EXPECT_EQ("", ExecuteJavaScript("run_order.join(', ')"));
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kAdvance);
RunTasksForPeriod(1000);
EXPECT_EQ("c, b, a", ExecuteJavaScript("run_order.join(', ')"));
}
// http://crbug.com/633321
#if defined(OS_ANDROID)
#define MAYBE_VirtualTimeNotAllowedToAdvanceWhileResourcesLoading \
DISABLED_VirtualTimeNotAllowedToAdvanceWhileResourcesLoading
#else
#define MAYBE_VirtualTimeNotAllowedToAdvanceWhileResourcesLoading \
VirtualTimeNotAllowedToAdvanceWhileResourcesLoading
#endif
TEST_F(VirtualTimeTest,
MAYBE_VirtualTimeNotAllowedToAdvanceWhileResourcesLoading) {
WebView().Scheduler()->EnableVirtualTime();
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kDeterministicLoading);
EXPECT_TRUE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
SimRequest main_resource("https://example.com/test.html", "text/html");
SimRequest css_resource("https://example.com/test.css", "text/css");
// Loading, virtual time should not advance.
LoadURL("https://example.com/test.html");
EXPECT_FALSE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
main_resource.Start();
// Still Loading, virtual time should not advance.
main_resource.Write("<!DOCTYPE html><link rel=stylesheet href=test.css>");
EXPECT_FALSE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
// Still Loading, virtual time should not advance.
css_resource.Start();
css_resource.Write("a { color: red; }");
EXPECT_FALSE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
// Still Loading, virtual time should not advance.
css_resource.Finish();
EXPECT_FALSE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
// Still Loading, virtual time should not advance.
main_resource.Write("<body>");
EXPECT_FALSE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
// Finished loading, virtual time should be able to advance.
main_resource.Finish();
EXPECT_TRUE(WebView().Scheduler()->VirtualTimeAllowedToAdvance());
// The loading events are delayed for 10 virtual ms after they have run, we
// let tasks run for a little while to ensure we don't get any asserts on
// teardown as a result.
RunTasksForPeriod(10);
}
// http://crbug.com/633321
#if defined(OS_ANDROID)
#define MAYBE_DOMTimersSuspended DISABLED_DOMTimersSuspended
#else
#define MAYBE_DOMTimersSuspended DOMTimersSuspended
#endif
TEST_F(VirtualTimeTest, MAYBE_DOMTimersSuspended) {
WebView().Scheduler()->EnableVirtualTime();
WebView().Scheduler()->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kAdvance);
// Schedule normal DOM timers to run at 1s and 1.001s in the future.
ExecuteJavaScript(
"var run_order = [];"
"setTimeout(() => { run_order.push(1); }, 1000);"
"setTimeout(() => { run_order.push(2); }, 1001);");
scoped_refptr<WebTaskRunner> runner =
Window().GetExecutionContext()->GetTaskRunner(TaskType::kJavascriptTimer);
// Schedule a task to suspend virtual time at the same point in time.
runner->PostDelayedTask(
FROM_HERE,
WTF::Bind(
[](WebViewScheduler* scheduler) {
scheduler->SetVirtualTimePolicy(
WebViewScheduler::VirtualTimePolicy::kPause);
},
WTF::Unretained(WebView().Scheduler())),
TimeDelta::FromMilliseconds(1000));
// ALso schedule a third timer for the same point in time.
ExecuteJavaScript("setTimeout(() => { run_order.push(2); }, 1000);");
// The second DOM timer shouldn't have run because the virtual time budget
// expired.
testing::RunPendingTasks();
EXPECT_EQ("1, 2", ExecuteJavaScript("run_order.join(', ')"));
}
#undef MAYBE_DOMTimersFireInExpectedOrder
#undef MAYBE_SetInterval
#undef MAYBE_AllowVirtualTimeToAdvance
#undef MAYBE_VirtualTimeNotAllowedToAdvanceWhileResourcesLoading
#undef MAYBE_DOMTimersSuspended
} // namespace virtual_time_test
} // namespace blink