Commit b19b9003 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[blink] Enable top-level-await for worklet tests

- Use the ParametrizedModuletTest for worklet tests
- Split ParametrizedModuleTestBase from ParametrizedModuleTest

Bug: 1132793
Change-Id: I28f89c0f1c1a1093d43922eaf9e53949ae4e2bf1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2465902
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Reviewed-by: default avatarHongchan Choi <hongchan@chromium.org>
Reviewed-by: default avatarHiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825752}
parent 15488798
......@@ -204,32 +204,11 @@ TEST_P(ModuleRecordTest, instantiateWithDeps) {
EXPECT_EQ("b", resolver->Specifiers()[1]);
}
class SaveResultFunction final : public ScriptFunction {
public:
static v8::Local<v8::Function> CreateFunction(ScriptState* script_state,
ScriptValue* output) {
SaveResultFunction* self =
MakeGarbageCollected<SaveResultFunction>(script_state, output);
return self->BindToV8Function();
}
SaveResultFunction(ScriptState* script_state, ScriptValue* output)
: ScriptFunction(script_state), output_(output) {}
private:
ScriptValue Call(ScriptValue value) override {
*output_ = value;
return value;
}
ScriptValue* output_;
};
TEST_P(ModuleRecordTest, EvaluationErrorIsRemembered) {
V8TestingScope scope;
ScriptState* state = scope.GetScriptState();
auto* modulator =
MakeGarbageCollected<ModuleRecordTestModulator>(scope.GetScriptState());
auto* modulator = MakeGarbageCollected<ModuleRecordTestModulator>(state);
auto* resolver = modulator->GetTestModuleRecordResolver();
const KURL js_url_f("https://example.com/failure.js");
......@@ -238,9 +217,8 @@ TEST_P(ModuleRecordTest, EvaluationErrorIsRemembered) {
ScriptFetchOptions(), TextPosition::MinimumPosition(),
ASSERT_NO_EXCEPTION);
ASSERT_FALSE(module_failure.IsEmpty());
ASSERT_TRUE(ModuleRecord::Instantiate(scope.GetScriptState(), module_failure,
js_url_f)
.IsEmpty());
ASSERT_TRUE(
ModuleRecord::Instantiate(state, module_failure, js_url_f).IsEmpty());
ScriptEvaluationResult evaluation_result1 =
JSModuleScript::CreateForTest(modulator, module_failure, js_url_f)
->RunScriptAndReturnValue();
......@@ -253,40 +231,16 @@ TEST_P(ModuleRecordTest, EvaluationErrorIsRemembered) {
js_url_c, ScriptFetchOptions(), TextPosition::MinimumPosition(),
scope.GetExceptionState());
ASSERT_FALSE(module.IsEmpty());
ASSERT_TRUE(
ModuleRecord::Instantiate(scope.GetScriptState(), module, js_url_c)
.IsEmpty());
ASSERT_TRUE(ModuleRecord::Instantiate(state, module, js_url_c).IsEmpty());
ScriptEvaluationResult evaluation_result2 =
JSModuleScript::CreateForTest(modulator, module, js_url_c)
->RunScriptAndReturnValue();
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
EXPECT_EQ(evaluation_result1.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
EXPECT_EQ(evaluation_result2.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
ScriptValue value1;
ScriptValue value2;
evaluation_result1.GetPromise(scope.GetScriptState())
.Then(v8::Local<v8::Function>(), SaveResultFunction::CreateFunction(
scope.GetScriptState(), &value1));
evaluation_result2.GetPromise(scope.GetScriptState())
.Then(v8::Local<v8::Function>(), SaveResultFunction::CreateFunction(
scope.GetScriptState(), &value2));
v8::MicrotasksScope::PerformCheckpoint(
scope.GetScriptState()->GetIsolate());
EXPECT_FALSE(value1.IsEmpty());
EXPECT_FALSE(value2.IsEmpty());
EXPECT_EQ(value1, value2);
} else {
EXPECT_EQ(evaluation_result1.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_EQ(evaluation_result2.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_EQ(evaluation_result1.GetExceptionForModule(),
evaluation_result2.GetExceptionForModule());
}
v8::Local<v8::Value> exception1 = GetException(state, evaluation_result1);
v8::Local<v8::Value> exception2 = GetException(state, evaluation_result2);
EXPECT_FALSE(exception1.IsEmpty());
EXPECT_FALSE(exception2.IsEmpty());
EXPECT_EQ(exception1, exception2);
ASSERT_EQ(1u, resolver->ResolveCount());
EXPECT_EQ("failure", resolver->Specifiers()[0]);
......@@ -339,34 +293,17 @@ TEST_P(ModuleRecordTest, EvaluateCaptureError) {
scope.GetIsolate(), "throw 'bar';", js_url, js_url, ScriptFetchOptions(),
TextPosition::MinimumPosition(), ASSERT_NO_EXCEPTION);
ASSERT_FALSE(module.IsEmpty());
ScriptValue exception =
ScriptValue instantiation_exception =
ModuleRecord::Instantiate(scope.GetScriptState(), module, js_url);
ASSERT_TRUE(exception.IsEmpty());
ASSERT_TRUE(instantiation_exception.IsEmpty());
ScriptEvaluationResult result =
JSModuleScript::CreateForTest(modulator, module, js_url)
->RunScriptAndReturnValue();
v8::Local<v8::Value> value;
if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
ASSERT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
ScriptValue script_value;
result.GetPromise(scope.GetScriptState())
.Then(v8::Local<v8::Function>(),
SaveResultFunction::CreateFunction(scope.GetScriptState(),
&script_value));
v8::MicrotasksScope::PerformCheckpoint(
scope.GetScriptState()->GetIsolate());
EXPECT_FALSE(script_value.IsEmpty());
value = script_value.V8Value();
} else {
ASSERT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
value = result.GetExceptionForModule();
}
ASSERT_TRUE(value->IsString());
EXPECT_EQ("bar", ToCoreString(v8::Local<v8::String>::Cast(value)));
v8::Local<v8::Value> exception = GetException(scope.GetScriptState(), result);
ASSERT_TRUE(exception->IsString());
EXPECT_EQ("bar", ToCoreString(exception.As<v8::String>()));
}
// Instantiate tests once with TLA and once without:
......
......@@ -7,6 +7,7 @@
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/module_record.h"
#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
......@@ -15,19 +16,26 @@
#include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h"
#include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h"
#include "third_party/blink/renderer/core/script/js_module_script.h"
#include "third_party/blink/renderer/core/testing/module_test_base.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
namespace blink {
class LayoutWorkletTest : public PageTestBase {
class LayoutWorkletTest : public PageTestBase, public ParametrizedModuleTest {
public:
void SetUp() override {
ParametrizedModuleTest::SetUp();
PageTestBase::SetUp(IntSize());
layout_worklet_ =
MakeGarbageCollected<LayoutWorklet>(*GetDocument().domWindow());
proxy_ = layout_worklet_->CreateGlobalScope();
}
void TearDown() override {
PageTestBase::TearDown();
ParametrizedModuleTest::TearDown();
}
LayoutWorkletGlobalScopeProxy* GetProxy() {
return LayoutWorkletGlobalScopeProxy::From(proxy_.Get());
}
......@@ -70,18 +78,17 @@ class LayoutWorkletTest : public PageTestBase {
Persistent<LayoutWorklet> layout_worklet_;
};
TEST_F(LayoutWorkletTest, ParseProperties) {
TEST_P(LayoutWorkletTest, ParseProperties) {
ScriptState::Scope scope(GetScriptState());
EXPECT_EQ(EvaluateScriptModule(R"JS(
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
static get inputProperties() { return ['--prop', 'flex-basis', 'thing'] }
static get childInputProperties() { return ['--child-prop', 'margin-top', 'other-thing'] }
async intrinsicSizes() { }
async layout() { }
});
)JS")
.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
)JS");
EXPECT_FALSE(GetResult(GetScriptState(), result).IsEmpty());
LayoutWorkletGlobalScope* global_scope = GetGlobalScope();
CSSLayoutDefinition* definition = global_scope->FindDefinition("foo");
......@@ -107,7 +114,7 @@ TEST_F(LayoutWorkletTest, ParseProperties) {
// TODO(ikilpatrick): Move all the tests below to wpt tests once we have the
// layout API actually have effects that we can test in script.
TEST_F(LayoutWorkletTest, RegisterLayout) {
TEST_P(LayoutWorkletTest, RegisterLayout) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -116,8 +123,7 @@ TEST_F(LayoutWorkletTest, RegisterLayout) {
});
)JS");
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
EXPECT_FALSE(GetResult(GetScriptState(), result).IsEmpty());
result = EvaluateScriptModule(R"JS(
registerLayout('bar', class {
......@@ -128,11 +134,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout) {
});
)JS");
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
EXPECT_FALSE(GetResult(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_EmptyName) {
TEST_P(LayoutWorkletTest, RegisterLayout_EmptyName) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('', class {
......@@ -140,11 +145,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_EmptyName) {
)JS");
// "The empty string is not a valid name."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_Duplicate) {
TEST_P(LayoutWorkletTest, RegisterLayout_Duplicate) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -158,11 +162,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_Duplicate) {
)JS");
// "A class with name:'foo' is already registered."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_NoIntrinsicSizes) {
TEST_P(LayoutWorkletTest, RegisterLayout_NoIntrinsicSizes) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -170,11 +173,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_NoIntrinsicSizes) {
)JS");
// "The 'intrinsicSizes' property on the prototype does not exist."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_ThrowingPropertyGetter) {
TEST_P(LayoutWorkletTest, RegisterLayout_ThrowingPropertyGetter) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -183,11 +185,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_ThrowingPropertyGetter) {
)JS");
// "Uncaught Error"
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadPropertyGetter) {
TEST_P(LayoutWorkletTest, RegisterLayout_BadPropertyGetter) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -196,11 +197,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_BadPropertyGetter) {
)JS");
// "The provided value cannot be converted to a sequence."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_NoPrototype) {
TEST_P(LayoutWorkletTest, RegisterLayout_NoPrototype) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
const foo = function() { };
......@@ -209,11 +209,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_NoPrototype) {
)JS");
// "The 'prototype' object on the class does not exist."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadPrototype) {
TEST_P(LayoutWorkletTest, RegisterLayout_BadPrototype) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
const foo = function() { };
......@@ -222,11 +221,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_BadPrototype) {
)JS");
// "The 'prototype' property on the class is not an object."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadIntrinsicSizes) {
TEST_P(LayoutWorkletTest, RegisterLayout_BadIntrinsicSizes) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -235,11 +233,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_BadIntrinsicSizes) {
)JS");
// "The 'intrinsicSizes' property on the prototype is not a function."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_NoLayout) {
TEST_P(LayoutWorkletTest, RegisterLayout_NoLayout) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -248,11 +245,10 @@ TEST_F(LayoutWorkletTest, RegisterLayout_NoLayout) {
)JS");
// "The 'layout' property on the prototype does not exist."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
TEST_F(LayoutWorkletTest, RegisterLayout_BadLayout) {
TEST_P(LayoutWorkletTest, RegisterLayout_BadLayout) {
ScriptState::Scope scope(GetScriptState());
ScriptEvaluationResult result = EvaluateScriptModule(R"JS(
registerLayout('foo', class {
......@@ -262,8 +258,13 @@ TEST_F(LayoutWorkletTest, RegisterLayout_BadLayout) {
)JS");
// "The 'layout' property on the prototype is not a function."
EXPECT_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
EXPECT_FALSE(GetException(GetScriptState(), result).IsEmpty());
}
// Instantiate tests once with TLA and once without:
INSTANTIATE_TEST_SUITE_P(LayoutWorkletTestGroup,
LayoutWorkletTest,
testing::Bool(),
ParametrizedModuleTestParamName());
} // namespace blink
......@@ -3,24 +3,22 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/core/testing/module_test_base.h"
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
namespace blink {
void ParametrizedModuleTest::SetUp() {
if (UseTopLevelAwait()) {
void ParametrizedModuleTestBase::SetUp(bool use_top_level_await) {
if (use_top_level_await) {
feature_list_.InitAndEnableFeature(features::kTopLevelAwait);
} else {
feature_list_.InitAndDisableFeature(features::kTopLevelAwait);
}
SetV8Flags(UseTopLevelAwait());
SetV8Flags(use_top_level_await);
}
void ParametrizedModuleTest::TearDown() {
feature_list_.Reset();
SetV8Flags(base::FeatureList::IsEnabled(features::kTopLevelAwait));
}
void ParametrizedModuleTest::SetV8Flags(bool use_top_level_await) {
void ParametrizedModuleTestBase::SetV8Flags(bool use_top_level_await) {
if (use_top_level_await) {
v8::V8::SetFlagsFromString("--harmony-top-level-await");
} else {
......@@ -28,4 +26,99 @@ void ParametrizedModuleTest::SetV8Flags(bool use_top_level_await) {
}
}
void ParametrizedModuleTest::SetUp() {
ParametrizedModuleTestBase::SetUp(UseTopLevelAwait());
}
class SaveResultFunction final : public ScriptFunction {
public:
explicit SaveResultFunction(ScriptState* script_state)
: ScriptFunction(script_state) {}
v8::Local<v8::Function> Bind() { return BindToV8Function(); }
v8::Local<v8::Value> GetResult() {
EXPECT_TRUE(result_);
EXPECT_FALSE(result_->IsEmpty());
return result_->V8Value();
}
private:
ScriptValue Call(ScriptValue value) override {
*result_ = value;
return value;
}
ScriptValue* result_ = nullptr;
};
class ExpectNotReached final : public ScriptFunction {
public:
static v8::Local<v8::Function> Create(ScriptState* script_state) {
auto* self = MakeGarbageCollected<ExpectNotReached>(script_state);
return self->BindToV8Function();
}
explicit ExpectNotReached(ScriptState* script_state)
: ScriptFunction(script_state) {}
private:
ScriptValue Call(ScriptValue value) override {
ADD_FAILURE() << "ExpectNotReached was reached";
return value;
}
};
v8::Local<v8::Value> ParametrizedModuleTestBase::GetResult(
ScriptState* script_state,
ScriptEvaluationResult result) {
CHECK_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
if (!base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
return result.GetSuccessValue();
}
ScriptPromise script_promise = result.GetPromise(script_state);
v8::Local<v8::Promise> promise = script_promise.V8Value().As<v8::Promise>();
if (promise->State() == v8::Promise::kFulfilled) {
return promise->Result();
}
auto* resolve_function =
MakeGarbageCollected<SaveResultFunction>(script_state);
result.GetPromise(script_state)
.Then(resolve_function->Bind(), ExpectNotReached::Create(script_state));
v8::MicrotasksScope::PerformCheckpoint(script_state->GetIsolate());
return resolve_function->GetResult();
}
v8::Local<v8::Value> ParametrizedModuleTestBase::GetException(
ScriptState* script_state,
ScriptEvaluationResult result) {
if (!base::FeatureList::IsEnabled(features::kTopLevelAwait)) {
CHECK_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kException);
return result.GetExceptionForModule();
}
CHECK_EQ(result.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
ScriptPromise script_promise = result.GetPromise(script_state);
v8::Local<v8::Promise> promise = script_promise.V8Value().As<v8::Promise>();
if (promise->State() == v8::Promise::kRejected) {
return promise->Result();
}
auto* reject_function =
MakeGarbageCollected<SaveResultFunction>(script_state);
script_promise.Then(ExpectNotReached::Create(script_state),
reject_function->Bind());
v8::MicrotasksScope::PerformCheckpoint(script_state->GetIsolate());
return reject_function->GetResult();
}
} // namespace blink
......@@ -8,21 +8,38 @@
#include <gtest/gtest.h>
#include "base/test/scoped_feature_list.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
#include "v8/include/v8.h"
namespace blink {
// Helper used to enable or disable top-level await in parametrized tests.
class ParametrizedModuleTest : public testing::WithParamInterface<bool> {
class ParametrizedModuleTestBase {
protected:
void SetUp();
void TearDown();
bool UseTopLevelAwait() { return GetParam(); }
base::test::ScopedFeatureList feature_list_;
void SetUp(bool use_top_level_await);
void TearDown() {}
// Get the results of a ScriptEvaluationResult from a module.
// If top-level await is enabled, the method will wait for the result
// Promise to be resolved.
v8::Local<v8::Value> GetResult(ScriptState* script_state,
ScriptEvaluationResult result);
// Get the exception of a ScriptEvaluationResult from a module.
// If top-level await is enabled, the method will wait for the result
// Promise to be rejected.
v8::Local<v8::Value> GetException(ScriptState* script_state,
ScriptEvaluationResult result);
private:
void SetV8Flags(bool use_top_level_await);
base::test::ScopedFeatureList feature_list_;
};
class ParametrizedModuleTest : public ParametrizedModuleTestBase,
public testing::WithParamInterface<bool> {
protected:
void SetUp();
bool UseTopLevelAwait() { return GetParam(); }
};
// Used in INSTANTIATE_TEST_SUITE_P for returning more readable test names.
......
......@@ -28,6 +28,7 @@
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/core/script/js_module_script.h"
#include "third_party/blink/renderer/core/script/script.h"
#include "third_party/blink/renderer/core/testing/module_test_base.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
......@@ -55,14 +56,21 @@ static const size_t kRenderQuantumFrames = 128;
// The test uses OfflineAudioWorkletThread because the test does not have a
// strict real-time constraint.
class AudioWorkletGlobalScopeTest : public PageTestBase {
class AudioWorkletGlobalScopeTest : public PageTestBase,
public ParametrizedModuleTest {
public:
void SetUp() override {
ParametrizedModuleTest::SetUp();
PageTestBase::SetUp(IntSize());
NavigateTo(KURL("https://example.com/"));
reporting_proxy_ = std::make_unique<WorkerReportingProxy>();
}
void TearDown() override {
PageTestBase::TearDown();
ParametrizedModuleTest::TearDown();
}
std::unique_ptr<OfflineAudioWorkletThread> CreateAudioWorkletThread() {
std::unique_ptr<OfflineAudioWorkletThread> thread =
std::make_unique<OfflineAudioWorkletThread>(*reporting_proxy_);
......@@ -133,9 +141,9 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
}
private:
// Returns false when a script evaluation error happens.
bool EvaluateScriptModule(AudioWorkletGlobalScope* global_scope,
const String& source_code) {
void ExpectEvaluateScriptModule(AudioWorkletGlobalScope* global_scope,
const String& source_code,
bool expect_success) {
ScriptState* script_state =
global_scope->ScriptController()->GetScriptState();
EXPECT_TRUE(script_state);
......@@ -153,8 +161,11 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
JSModuleScript::CreateForTest(Modulator::From(script_state), module,
js_url)
->RunScriptAndReturnValue();
return result.GetResultType() ==
ScriptEvaluationResult::ResultType::kSuccess;
if (expect_success) {
EXPECT_FALSE(GetResult(script_state, result).IsEmpty());
} else {
EXPECT_FALSE(GetException(script_state, result).IsEmpty());
}
}
// Test if AudioWorkletGlobalScope and V8 components (ScriptState, Isolate)
......@@ -184,7 +195,7 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
}
registerProcessor('testProcessor', TestProcessor);
)JS";
ASSERT_TRUE(EvaluateScriptModule(global_scope, source_code));
ExpectEvaluateScriptModule(global_scope, source_code, true);
AudioWorkletProcessorDefinition* definition =
global_scope->FindDefinition("testProcessor");
......@@ -233,7 +244,7 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
class2.prototype = { process: function () {} };
registerProcessor('class2', class2);
)JS";
ASSERT_TRUE(EvaluateScriptModule(global_scope, source_code));
ExpectEvaluateScriptModule(global_scope, source_code, true);
EXPECT_TRUE(global_scope->FindDefinition("class1"));
EXPECT_TRUE(global_scope->FindDefinition("class2"));
}
......@@ -253,7 +264,7 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
});
registerProcessor('class3', class3);
)JS";
ASSERT_FALSE(EvaluateScriptModule(global_scope, source_code));
ExpectEvaluateScriptModule(global_scope, source_code, false);
EXPECT_FALSE(global_scope->FindDefinition("class3"));
}
......@@ -289,7 +300,7 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
}
registerProcessor('testProcessor', TestProcessor);
)JS";
ASSERT_TRUE(EvaluateScriptModule(global_scope, source_code));
ExpectEvaluateScriptModule(global_scope, source_code, true);
auto* channel = MakeGarbageCollected<MessageChannel>(thread->GlobalScope());
MessagePortChannel dummy_port_channel = channel->port2()->Disentangle();
......@@ -354,7 +365,7 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
}
registerProcessor('testProcessor', TestProcessor);
)JS";
ASSERT_TRUE(EvaluateScriptModule(global_scope, source_code));
ExpectEvaluateScriptModule(global_scope, source_code, true);
AudioWorkletProcessorDefinition* definition =
global_scope->FindDefinition("testProcessor");
......@@ -377,7 +388,7 @@ class AudioWorkletGlobalScopeTest : public PageTestBase {
std::unique_ptr<WorkerReportingProxy> reporting_proxy_;
};
TEST_F(AudioWorkletGlobalScopeTest, Basic) {
TEST_P(AudioWorkletGlobalScopeTest, Basic) {
std::unique_ptr<OfflineAudioWorkletThread> thread
= CreateAudioWorkletThread();
RunBasicTest(thread.get());
......@@ -385,7 +396,7 @@ TEST_F(AudioWorkletGlobalScopeTest, Basic) {
thread->WaitForShutdownForTesting();
}
TEST_F(AudioWorkletGlobalScopeTest, Parsing) {
TEST_P(AudioWorkletGlobalScopeTest, Parsing) {
std::unique_ptr<OfflineAudioWorkletThread> thread
= CreateAudioWorkletThread();
RunParsingTest(thread.get());
......@@ -393,7 +404,7 @@ TEST_F(AudioWorkletGlobalScopeTest, Parsing) {
thread->WaitForShutdownForTesting();
}
TEST_F(AudioWorkletGlobalScopeTest, BufferProcessing) {
TEST_P(AudioWorkletGlobalScopeTest, BufferProcessing) {
std::unique_ptr<OfflineAudioWorkletThread> thread
= CreateAudioWorkletThread();
RunSimpleProcessTest(thread.get());
......@@ -401,7 +412,7 @@ TEST_F(AudioWorkletGlobalScopeTest, BufferProcessing) {
thread->WaitForShutdownForTesting();
}
TEST_F(AudioWorkletGlobalScopeTest, ParsingParameterDescriptor) {
TEST_P(AudioWorkletGlobalScopeTest, ParsingParameterDescriptor) {
std::unique_ptr<OfflineAudioWorkletThread> thread
= CreateAudioWorkletThread();
RunParsingParameterDescriptorTest(thread.get());
......@@ -409,4 +420,10 @@ TEST_F(AudioWorkletGlobalScopeTest, ParsingParameterDescriptor) {
thread->WaitForShutdownForTesting();
}
// Instantiate tests once with TLA and once without:
INSTANTIATE_TEST_SUITE_P(AudioWorkletGlobalScopeTestGroup,
AudioWorkletGlobalScopeTest,
testing::Bool(),
ParametrizedModuleTestParamName());
} // namespace blink
......@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/core/script/js_module_script.h"
#include "third_party/blink/renderer/core/script/script.h"
#include "third_party/blink/renderer/core/testing/module_test_base.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
......@@ -46,9 +47,14 @@
namespace blink {
class AudioWorkletThreadTest : public PageTestBase {
class AudioWorkletThreadTestBase : public PageTestBase,
public ParametrizedModuleTestBase {
public:
explicit AudioWorkletThreadTestBase(bool use_top_level_await)
: use_top_level_await_(use_top_level_await) {}
void SetUp() override {
ParametrizedModuleTestBase::SetUp(use_top_level_await_);
PageTestBase::SetUp(IntSize());
NavigateTo(KURL("https://example.com/"));
reporting_proxy_ = std::make_unique<WorkerReportingProxy>();
......@@ -58,6 +64,7 @@ class AudioWorkletThreadTest : public PageTestBase {
OfflineAudioWorkletThread::ClearSharedBackingThread();
RealtimeAudioWorkletThread::ClearSharedBackingThread();
SemiRealtimeAudioWorkletThread::ClearSharedBackingThread();
ParametrizedModuleTestBase::TearDown();
}
std::unique_ptr<WorkerThread> CreateAudioWorkletThread(
......@@ -77,7 +84,7 @@ class AudioWorkletThreadTest : public PageTestBase {
PostCrossThreadTask(
*thread->GetWorkerBackingThread().BackingThread().GetTaskRunner(),
FROM_HERE,
CrossThreadBindOnce(&AudioWorkletThreadTest::ExecuteScriptInWorklet,
CrossThreadBindOnce(&AudioWorkletThreadTestBase::ExecuteScriptInWorklet,
CrossThreadUnretained(this),
CrossThreadUnretained(thread),
CrossThreadUnretained(&wait_event)));
......@@ -131,9 +138,16 @@ class AudioWorkletThreadTest : public PageTestBase {
}
std::unique_ptr<WorkerReportingProxy> reporting_proxy_;
bool use_top_level_await_;
};
class AudioWorkletThreadTest : public AudioWorkletThreadTestBase,
public testing::WithParamInterface<bool> {
public:
AudioWorkletThreadTest() : AudioWorkletThreadTestBase(GetParam()) {}
};
TEST_F(AudioWorkletThreadTest, Basic) {
TEST_P(AudioWorkletThreadTest, Basic) {
std::unique_ptr<WorkerThread> audio_worklet_thread =
CreateAudioWorkletThread(true, true);
CheckWorkletCanExecuteScript(audio_worklet_thread.get());
......@@ -143,7 +157,7 @@ TEST_F(AudioWorkletThreadTest, Basic) {
// Creates 2 different AudioWorkletThreads with different RT constraints.
// Checks if they are running on a different thread.
TEST_F(AudioWorkletThreadTest, CreateDifferentWorkletThreadsAndTerminate_1) {
TEST_P(AudioWorkletThreadTest, CreateDifferentWorkletThreadsAndTerminate_1) {
// Create RealtimeAudioWorkletThread.
std::unique_ptr<WorkerThread> first_worklet_thread =
CreateAudioWorkletThread(true, true);
......@@ -170,7 +184,7 @@ TEST_F(AudioWorkletThreadTest, CreateDifferentWorkletThreadsAndTerminate_1) {
// Creates 2 AudioWorkletThreads with RT constraint from 2 different
// originating frames. Checks if they are running on a different thread.
TEST_F(AudioWorkletThreadTest, CreateDifferentWorkletThreadsAndTerminate_2) {
TEST_P(AudioWorkletThreadTest, CreateDifferentWorkletThreadsAndTerminate_2) {
// Create an AudioWorkletThread from a main frame with RT constraint.
std::unique_ptr<WorkerThread> first_worklet_thread =
CreateAudioWorkletThread(true, true);
......@@ -195,17 +209,24 @@ TEST_F(AudioWorkletThreadTest, CreateDifferentWorkletThreadsAndTerminate_2) {
second_worklet_thread->WaitForShutdownForTesting();
}
// Instantiate tests once with TLA and once without:
INSTANTIATE_TEST_SUITE_P(AudioWorkletThreadTestGroup,
AudioWorkletThreadTest,
testing::Bool(),
ParametrizedModuleTestParamName());
class AudioWorkletThreadInteractionTest
: public AudioWorkletThreadTest,
public testing::WithParamInterface<std::tuple<bool, bool>> {
: public AudioWorkletThreadTestBase,
public testing::WithParamInterface<std::tuple<bool, bool, bool>> {
public:
AudioWorkletThreadInteractionTest()
: has_realtime_constraint_(std::get<0>(GetParam())),
is_top_level_frame_(std::get<1>(GetParam())) {}
: AudioWorkletThreadTestBase(std::get<0>(GetParam())),
has_realtime_constraint_(std::get<1>(GetParam())),
is_top_level_frame_(std::get<2>(GetParam())) {}
protected:
const bool has_realtime_constraint_;
const bool is_top_level_frame_;
protected:
const bool has_realtime_constraint_;
const bool is_top_level_frame_;
};
TEST_P(AudioWorkletThreadInteractionTest, CreateSecondAndTerminateFirst) {
......@@ -296,11 +317,14 @@ TEST_P(AudioWorkletThreadInteractionTest,
second_worklet_thread->WaitForShutdownForTesting();
}
INSTANTIATE_TEST_SUITE_P(AudioWorkletThreadInteractionTest,
INSTANTIATE_TEST_SUITE_P(AudioWorkletThreadInteractionTestGroup,
AudioWorkletThreadInteractionTest,
testing::Combine(testing::Bool(), testing::Bool()));
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool()));
struct ThreadPriorityTestParam {
const bool use_top_level_await;
const bool has_realtime_constraint;
const bool is_top_level_frame;
const bool is_flag_enabled;
......@@ -311,30 +335,43 @@ constexpr ThreadPriorityTestParam kThreadPriorityTestParams[] = {
// A real-time priority thread is guaranteed when the context has real-time
// constraint and is spawned from a top-level frame. The flag setting
// is ignored.
{true, true, true, base::ThreadPriority::REALTIME_AUDIO},
{true, true, false, base::ThreadPriority::REALTIME_AUDIO},
{false, true, true, true, base::ThreadPriority::REALTIME_AUDIO},
{false, true, true, false, base::ThreadPriority::REALTIME_AUDIO},
// With top-level-await module:
{true, true, true, true, base::ThreadPriority::REALTIME_AUDIO},
{true, true, true, false, base::ThreadPriority::REALTIME_AUDIO},
// A DISPLAY priority thread is given when the context has real-time
// constraint but is spawned from a sub frame.
{true, false, false, base::ThreadPriority::DISPLAY},
{false, true, false, false, base::ThreadPriority::DISPLAY},
// With top-level-await module:
{true, true, false, false, base::ThreadPriority::DISPLAY},
// Enabling the real-time thread flag will override thread priority logic
// for a real-time context.
{true, false, true, base::ThreadPriority::REALTIME_AUDIO},
{false, true, false, true, base::ThreadPriority::REALTIME_AUDIO},
// With top-level-await module:
{true, true, false, true, base::ThreadPriority::REALTIME_AUDIO},
// OfflineAudioWorkletThread is always a NORMAL priority no matter what
// the flag setting or the originating frame level is.
{false, true, true, base::ThreadPriority::NORMAL},
{false, true, false, base::ThreadPriority::NORMAL},
{false, false, true, base::ThreadPriority::NORMAL},
{false, false, false, base::ThreadPriority::NORMAL},
{false, false, true, true, base::ThreadPriority::NORMAL},
{false, false, true, false, base::ThreadPriority::NORMAL},
{false, false, false, true, base::ThreadPriority::NORMAL},
{false, false, false, false, base::ThreadPriority::NORMAL},
// With top-level-await module:
{true, false, true, true, base::ThreadPriority::NORMAL},
{true, false, true, false, base::ThreadPriority::NORMAL},
{true, false, false, true, base::ThreadPriority::NORMAL},
{true, false, false, false, base::ThreadPriority::NORMAL},
};
class AudioWorkletThreadPriorityTest
: public AudioWorkletThreadTest,
: public AudioWorkletThreadTestBase,
public testing::WithParamInterface<ThreadPriorityTestParam> {
public:
AudioWorkletThreadPriorityTest() = default;
AudioWorkletThreadPriorityTest()
: AudioWorkletThreadTestBase(GetParam().use_top_level_await) {}
void InitAndSetRealtimePriorityFlag(bool is_flag_enabled) {
if (is_flag_enabled) {
......@@ -394,7 +431,6 @@ class AudioWorkletThreadPriorityTest
wait_event->Signal();
}
base::test::ScopedFeatureList feature_list_;
};
......@@ -406,7 +442,7 @@ TEST_P(AudioWorkletThreadPriorityTest, CheckThreadPriority) {
test_param.expected_priority);
}
INSTANTIATE_TEST_SUITE_P(AudioWorkletThreadPriorityTest,
INSTANTIATE_TEST_SUITE_P(AudioWorkletThreadPriorityTestGroup,
AudioWorkletThreadPriorityTest,
testing::ValuesIn(kThreadPriorityTestParams));
......
......@@ -16,6 +16,7 @@
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/script/js_module_script.h"
#include "third_party/blink/renderer/core/script/script.h"
#include "third_party/blink/renderer/core/testing/module_test_base.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
#include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
......@@ -42,14 +43,21 @@ class TestAnimationWorkletProxyClient : public AnimationWorkletProxyClient {
} // namespace
class AnimationAndPaintWorkletThreadTest : public PageTestBase {
class AnimationAndPaintWorkletThreadTest : public PageTestBase,
public ParametrizedModuleTest {
public:
void SetUp() override {
ParametrizedModuleTest::SetUp();
PageTestBase::SetUp(IntSize());
NavigateTo(KURL("https://example.com/"));
reporting_proxy_ = std::make_unique<WorkerReportingProxy>();
}
void TearDown() override {
PageTestBase::TearDown();
ParametrizedModuleTest::TearDown();
}
// Attempts to run some simple script for |thread|.
void CheckWorkletCanExecuteScript(WorkerThread* thread) {
std::unique_ptr<base::WaitableEvent> wait_event =
......@@ -82,16 +90,16 @@ class AnimationAndPaintWorkletThreadTest : public PageTestBase {
ScriptValue exception =
ModuleRecord::Instantiate(script_state, module, js_url);
EXPECT_TRUE(exception.IsEmpty());
EXPECT_EQ(JSModuleScript::CreateForTest(Modulator::From(script_state),
module, js_url)
->RunScriptAndReturnValue()
.GetResultType(),
ScriptEvaluationResult::ResultType::kSuccess);
ScriptEvaluationResult result =
JSModuleScript::CreateForTest(Modulator::From(script_state), module,
js_url)
->RunScriptAndReturnValue();
EXPECT_FALSE(GetResult(script_state, result).IsEmpty());
wait_event->Signal();
}
};
TEST_F(AnimationAndPaintWorkletThreadTest, Basic) {
TEST_P(AnimationAndPaintWorkletThreadTest, Basic) {
std::unique_ptr<AnimationAndPaintWorkletThread> worklet =
CreateThreadAndProvideAnimationWorkletProxyClient(&GetDocument(),
reporting_proxy_.get());
......@@ -102,7 +110,7 @@ TEST_F(AnimationAndPaintWorkletThreadTest, Basic) {
// Tests that the same WebThread is used for new worklets if the WebThread is
// still alive.
TEST_F(AnimationAndPaintWorkletThreadTest, CreateSecondAndTerminateFirst) {
TEST_P(AnimationAndPaintWorkletThreadTest, CreateSecondAndTerminateFirst) {
// Create the first worklet and wait until it is initialized.
std::unique_ptr<AnimationAndPaintWorkletThread> first_worklet =
CreateThreadAndProvideAnimationWorkletProxyClient(&GetDocument(),
......@@ -140,7 +148,7 @@ TEST_F(AnimationAndPaintWorkletThreadTest, CreateSecondAndTerminateFirst) {
// Tests that the WebThread is reused if all existing worklets are terminated
// before a new worklet is created, as long as the worklets are not destructed.
TEST_F(AnimationAndPaintWorkletThreadTest, TerminateFirstAndCreateSecond) {
TEST_P(AnimationAndPaintWorkletThreadTest, TerminateFirstAndCreateSecond) {
// Create the first worklet, wait until it is initialized, and terminate it.
std::unique_ptr<AnimationAndPaintWorkletThread> worklet =
CreateThreadAndProvideAnimationWorkletProxyClient(&GetDocument(),
......@@ -165,7 +173,7 @@ TEST_F(AnimationAndPaintWorkletThreadTest, TerminateFirstAndCreateSecond) {
// Tests that v8::Isolate and WebThread are correctly set-up if a worklet is
// created while another is terminating.
TEST_F(AnimationAndPaintWorkletThreadTest,
TEST_P(AnimationAndPaintWorkletThreadTest,
CreatingSecondDuringTerminationOfFirst) {
std::unique_ptr<AnimationAndPaintWorkletThread> first_worklet =
CreateThreadAndProvideAnimationWorkletProxyClient(&GetDocument(),
......@@ -198,7 +206,7 @@ TEST_F(AnimationAndPaintWorkletThreadTest,
// Tests that the backing thread is correctly created, torn down, and recreated
// as AnimationWorkletThreads are created and destroyed.
TEST_F(AnimationAndPaintWorkletThreadTest,
TEST_P(AnimationAndPaintWorkletThreadTest,
WorkletThreadHolderIsRefCountedProperly) {
EXPECT_FALSE(
AnimationAndPaintWorkletThread::GetWorkletThreadHolderForTesting());
......@@ -242,4 +250,9 @@ TEST_F(AnimationAndPaintWorkletThreadTest,
worklet3->WaitForShutdownForTesting();
}
// Instantiate tests once with TLA and once without:
INSTANTIATE_TEST_SUITE_P(AnimationAndPaintWorkletThreadTestGroup,
AnimationAndPaintWorkletThreadTest,
testing::Bool(),
ParametrizedModuleTestParamName());
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment