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