Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(703)

Unified Diff: third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp

Issue 2823803003: [ES6 modules] Introduce ModuleTreeLinker (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b0672214faab663eebe48f95c777796ab8d369f
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerTest.cpp
@@ -0,0 +1,379 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/loader/modulescript/ModuleTreeLinker.h"
+
+#include "bindings/core/v8/ScriptModule.h"
+#include "bindings/core/v8/ScriptState.h"
+#include "bindings/core/v8/V8Binding.h"
+#include "bindings/core/v8/V8BindingForTesting.h"
+#include "bindings/core/v8/V8ThrowException.h"
+#include "core/dom/Modulator.h"
+#include "core/dom/ModuleScript.h"
+#include "core/loader/modulescript/ModuleScriptFetchRequest.h"
+#include "core/loader/modulescript/ModuleTreeLinkerRegistry.h"
+#include "core/testing/DummyModulator.h"
+#include "core/testing/DummyPageHolder.h"
+#include "platform/heap/Handle.h"
+#include "platform/weborigin/KURL.h"
+#include "platform/wtf/text/StringBuilder.h"
+#include "public/platform/Platform.h"
+#include "public/platform/scheduler/renderer/renderer_scheduler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+namespace {
+
+class TestModuleTreeClient final
+ : public GarbageCollectedFinalized<TestModuleTreeClient>,
+ public ModuleTreeClient {
+ USING_GARBAGE_COLLECTED_MIXIN(TestModuleTreeClient);
+
+ public:
+ TestModuleTreeClient() = default;
+
+ DEFINE_INLINE_TRACE() { visitor->Trace(module_script_); }
+
+ void NotifyModuleTreeLoadFinished(ModuleScript* module_script) override {
+ was_notify_finished_ = true;
+ module_script_ = module_script;
+ }
+
+ bool WasNotifyFinished() const { return was_notify_finished_; }
+ ModuleScript* GetModuleScript() { return module_script_; }
+
+ private:
+ bool was_notify_finished_ = false;
+ Member<ModuleScript> module_script_;
+};
+
+class ModuleTreeLinkerTestModulator final : public DummyModulator {
+ public:
+ ModuleTreeLinkerTestModulator(RefPtr<ScriptState> script_state)
+ : script_state_(std::move(script_state)) {}
+ ~ModuleTreeLinkerTestModulator() override {}
+
+ DECLARE_TRACE();
+
+ // Resolve last |Modulator::FetchSingle()| call.
+ ModuleScript* ResolveSingleModuleScriptFetch(
+ const KURL& url,
+ const Vector<String>& dependency_module_requests) {
+ ScriptState::Scope scope(script_state_.Get());
+
+ StringBuilder source_text;
+ for (const auto& request : dependency_module_requests) {
+ source_text.Append("import '");
+ source_text.Append(request);
+ source_text.Append("';\n");
+ }
+ source_text.Append("export default 'grapes';");
+
+ ScriptModule script_module = ScriptModule::Compile(
+ script_state_->GetIsolate(), source_text.ToString(), url.GetString(),
+ kSharableCrossOrigin);
+ ModuleScript* module_script =
+ ModuleScript::Create(this, script_module, url, "", kParserInserted,
+ WebURLRequest::kFetchCredentialsModeOmit);
+ auto result_request = dependency_module_requests_map_.insert(
+ script_module, dependency_module_requests);
+ EXPECT_TRUE(result_request.is_new_entry);
+ auto result_map = module_map_.insert(url, module_script);
+ EXPECT_TRUE(result_map.is_new_entry);
+
+ EXPECT_EQ(url, pending_request_url_);
+ EXPECT_TRUE(pending_client_);
+ pending_client_->NotifyModuleLoadFinished(module_script);
+ pending_client_.Clear();
+
+ return module_script;
+ }
+
+ // Get AncestorList specified in |Modulator::FetchTreeInternal()| call for
+ // request matching |url|.
+ AncestorList GetAncestorListForTreeFetch(const KURL& url) const {
+ const auto& it = pending_tree_ancestor_list_.Find(url);
+ if (it == pending_tree_ancestor_list_.end())
+ return AncestorList();
+ return it->value;
+ }
+
+ // Resolve |Modulator::FetchTreeInternal()| for given url.
+ void ResolveDependentTreeFetch(const KURL& url) {
+ ScriptState::Scope scope(script_state_.Get());
+
+ ScriptModule script_module = ScriptModule::Compile(
+ script_state_->GetIsolate(), "export default 'pineapples';",
+ url.GetString(), kSharableCrossOrigin);
+ ModuleScript* module_script =
+ ModuleScript::Create(this, script_module, url, "", kParserInserted,
+ WebURLRequest::kFetchCredentialsModeOmit);
+ auto result_map = module_map_.insert(url, module_script);
+ EXPECT_TRUE(result_map.is_new_entry);
+
+ const auto& it = pending_tree_client_map_.Find(url);
+ EXPECT_NE(pending_tree_client_map_.end(), it);
+
+ auto pending_client = it->value;
+ EXPECT_TRUE(pending_client);
+ pending_client->NotifyModuleTreeLoadFinished(module_script);
+ pending_tree_client_map_.erase(it);
+ }
+
+ void SetInstantiateShouldFail(bool b) { instantiate_should_fail_ = b; }
+
+ private:
+ // Implements Modulator:
+
+ void FetchSingle(const ModuleScriptFetchRequest& request,
+ ModuleGraphLevel,
+ SingleModuleClient* client) override {
+ pending_request_url_ = request.Url();
+ EXPECT_FALSE(pending_client_);
+ pending_client_ = client;
+ }
+
+ void FetchTreeInternal(const ModuleScriptFetchRequest& request,
+ const AncestorList& list,
+ ModuleGraphLevel level,
+ ModuleTreeClient* client) override {
+ const auto& url = request.Url();
+
+ auto ancestor_result = pending_tree_ancestor_list_.insert(url, list);
+ EXPECT_TRUE(ancestor_result.is_new_entry);
+
+ EXPECT_EQ(ModuleGraphLevel::kDependentModuleFetch, level);
+
+ auto result_map = pending_tree_client_map_.insert(url, client);
+ EXPECT_TRUE(result_map.is_new_entry);
+ }
+
+ ModuleScript* GetFetchedModuleScript(const KURL& url) override {
+ const auto& it = module_map_.Find(url);
+ if (it == module_map_.end())
+ return nullptr;
+
+ return it->value;
+ }
+
+ ScriptValue InstantiateModule(ScriptModule) override {
+ if (instantiate_should_fail_) {
+ ScriptState::Scope scope(script_state_.Get());
+ v8::Local<v8::Value> error = V8ThrowException::CreateError(
+ script_state_->GetIsolate(), "Instantiation failure.");
+ return ScriptValue(script_state_.Get(), error);
+ }
+ return ScriptValue();
+ }
+
+ Vector<String> ModuleRequestsFromScriptModule(
+ ScriptModule script_module) override {
+ const auto& it = dependency_module_requests_map_.Find(script_module);
+ if (it == dependency_module_requests_map_.end())
+ return Vector<String>();
+
+ return it->value;
+ }
+
+ RefPtr<ScriptState> script_state_;
+ KURL pending_request_url_;
+ Member<SingleModuleClient> pending_client_;
+ HashMap<ScriptModule, Vector<String>> dependency_module_requests_map_;
+ HeapHashMap<KURL, Member<ModuleScript>> module_map_;
+ HeapHashMap<KURL, Member<ModuleTreeClient>> pending_tree_client_map_;
+ HashMap<KURL, AncestorList> pending_tree_ancestor_list_;
+ bool instantiate_should_fail_ = false;
+};
+
+DEFINE_TRACE(ModuleTreeLinkerTestModulator) {
+ visitor->Trace(pending_client_);
+ visitor->Trace(module_map_);
+ visitor->Trace(pending_tree_client_map_);
+ DummyModulator::Trace(visitor);
+}
+
+} // namespace
+
+class ModuleTreeLinkerTest : public ::testing::Test {
+ DISALLOW_COPY_AND_ASSIGN(ModuleTreeLinkerTest);
+
+ public:
+ ModuleTreeLinkerTest() = default;
+ void SetUp() override;
+
+ ModuleTreeLinkerTestModulator* GetModulator() { return modulator_.Get(); }
+
+ protected:
+ std::unique_ptr<DummyPageHolder> dummy_page_holder_;
+ Persistent<ModuleTreeLinkerTestModulator> modulator_;
+};
+
+void ModuleTreeLinkerTest::SetUp() {
+ dummy_page_holder_ = DummyPageHolder::Create(IntSize(500, 500));
+ RefPtr<ScriptState> script_state =
+ ToScriptStateForMainWorld(&dummy_page_holder_->GetFrame());
+ modulator_ = new ModuleTreeLinkerTestModulator(script_state);
+}
+
+TEST_F(ModuleTreeLinkerTest, fetchTreeNoDeps) {
+ ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
+
+ KURL url(kParsedURLString, "http://example.com/root.js");
+ ModuleScriptFetchRequest module_request(
+ url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
+ TestModuleTreeClient* client = new TestModuleTreeClient;
+ registry->Fetch(module_request, AncestorList(),
+ ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
+ client);
+
+ EXPECT_FALSE(client->WasNotifyFinished())
+ << "ModuleTreeLinker should always finish asynchronously.";
+ EXPECT_FALSE(client->GetModuleScript());
+
+ GetModulator()->ResolveSingleModuleScriptFetch(url, {});
+ EXPECT_TRUE(client->WasNotifyFinished());
+ ASSERT_TRUE(client->GetModuleScript());
+ EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+ ModuleInstantiationState::kInstantiated);
+}
+
+TEST_F(ModuleTreeLinkerTest, fetchTreeInstantiationFailure) {
+ GetModulator()->SetInstantiateShouldFail(true);
+
+ ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
+
+ KURL url(kParsedURLString, "http://example.com/root.js");
+ ModuleScriptFetchRequest module_request(
+ url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
+ TestModuleTreeClient* client = new TestModuleTreeClient;
+ registry->Fetch(module_request, AncestorList(),
+ ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
+ client);
+
+ EXPECT_FALSE(client->WasNotifyFinished())
+ << "ModuleTreeLinker should always finish asynchronously.";
+ EXPECT_FALSE(client->GetModuleScript());
+
+ GetModulator()->ResolveSingleModuleScriptFetch(url, {});
+ EXPECT_TRUE(client->WasNotifyFinished());
+ ASSERT_TRUE(client->GetModuleScript());
+ EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+ ModuleInstantiationState::kErrored);
+}
+
+TEST_F(ModuleTreeLinkerTest, fetchTreeWithSingleDependency) {
+ ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
+
+ KURL url(kParsedURLString, "http://example.com/root.js");
+ ModuleScriptFetchRequest module_request(
+ url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
+ TestModuleTreeClient* client = new TestModuleTreeClient;
+ registry->Fetch(module_request, AncestorList(),
+ ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
+ client);
+
+ EXPECT_FALSE(client->WasNotifyFinished())
+ << "ModuleTreeLinker should always finish asynchronously.";
+ EXPECT_FALSE(client->GetModuleScript());
+
+ GetModulator()->ResolveSingleModuleScriptFetch(url, {"./dep1.js"});
+ EXPECT_FALSE(client->WasNotifyFinished());
+
+ KURL url_dep1(kParsedURLString, "http://example.com/dep1.js");
+ auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep1);
+ EXPECT_EQ(1u, ancestor_list.size());
+ EXPECT_TRUE(ancestor_list.Contains(
+ KURL(kParsedURLString, "http://example.com/root.js")));
+
+ GetModulator()->ResolveDependentTreeFetch(url_dep1);
+ EXPECT_TRUE(client->WasNotifyFinished());
+
+ ASSERT_TRUE(client->GetModuleScript());
+ EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+ ModuleInstantiationState::kInstantiated);
+}
+
+TEST_F(ModuleTreeLinkerTest, fetchTreeWith3Deps) {
+ ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
+
+ KURL url(kParsedURLString, "http://example.com/root.js");
+ ModuleScriptFetchRequest module_request(
+ url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
+ TestModuleTreeClient* client = new TestModuleTreeClient;
+ registry->Fetch(module_request, AncestorList(),
+ ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
+ client);
+
+ EXPECT_FALSE(client->WasNotifyFinished())
+ << "ModuleTreeLinker should always finish asynchronously.";
+ EXPECT_FALSE(client->GetModuleScript());
+
+ GetModulator()->ResolveSingleModuleScriptFetch(
+ url, {"./dep1.js", "./dep2.js", "./dep3.js"});
+ EXPECT_FALSE(client->WasNotifyFinished());
+
+ Vector<KURL> url_deps;
+ for (int i = 1; i <= 3; ++i) {
+ StringBuilder url_dep_str;
+ url_dep_str.Append("http://example.com/dep");
+ url_dep_str.AppendNumber(i);
+ url_dep_str.Append(".js");
+
+ KURL url_dep(kParsedURLString, url_dep_str.ToString());
+ url_deps.push_back(url_dep);
+ }
+
+ for (const auto& url_dep : url_deps) {
+ SCOPED_TRACE(url_dep.GetString());
+ auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep);
+ EXPECT_EQ(1u, ancestor_list.size());
+ EXPECT_TRUE(ancestor_list.Contains(
+ KURL(kParsedURLString, "http://example.com/root.js")));
+ }
+
+ for (const auto& url_dep : url_deps)
+ GetModulator()->ResolveDependentTreeFetch(url_dep);
+
+ EXPECT_TRUE(client->WasNotifyFinished());
+ ASSERT_TRUE(client->GetModuleScript());
+ EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+ ModuleInstantiationState::kInstantiated);
+}
+
+TEST_F(ModuleTreeLinkerTest, fetchDependencyTree) {
+ ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
+
+ KURL url(kParsedURLString, "http://example.com/depth1.js");
+ ModuleScriptFetchRequest module_request(
+ url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
+ TestModuleTreeClient* client = new TestModuleTreeClient;
+ registry->Fetch(
+ module_request,
+ AncestorList{KURL(kParsedURLString, "http://example.com/root.js")},
+ ModuleGraphLevel::kDependentModuleFetch, GetModulator(), client);
+
+ EXPECT_FALSE(client->WasNotifyFinished())
+ << "ModuleTreeLinker should always finish asynchronously.";
+ EXPECT_FALSE(client->GetModuleScript());
+
+ GetModulator()->ResolveSingleModuleScriptFetch(url, {"./depth2.js"});
+
+ KURL url_dep2(kParsedURLString, "http://example.com/depth2.js");
+ auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep2);
+ EXPECT_EQ(2u, ancestor_list.size());
+ EXPECT_TRUE(ancestor_list.Contains(
+ KURL(kParsedURLString, "http://example.com/root.js")));
+ EXPECT_TRUE(ancestor_list.Contains(
+ KURL(kParsedURLString, "http://example.com/depth1.js")));
+
+ GetModulator()->ResolveDependentTreeFetch(url_dep2);
+
+ EXPECT_TRUE(client->WasNotifyFinished());
+ ASSERT_TRUE(client->GetModuleScript());
+ EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
+ ModuleInstantiationState::kInstantiated);
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698