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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/loader/modulescript/ModuleTreeLinker.h"
6
7 #include "bindings/core/v8/ScriptModule.h"
8 #include "bindings/core/v8/ScriptState.h"
9 #include "bindings/core/v8/V8Binding.h"
10 #include "bindings/core/v8/V8BindingForTesting.h"
11 #include "bindings/core/v8/V8ThrowException.h"
12 #include "core/dom/Modulator.h"
13 #include "core/dom/ModuleScript.h"
14 #include "core/loader/modulescript/ModuleScriptFetchRequest.h"
15 #include "core/loader/modulescript/ModuleTreeLinkerRegistry.h"
16 #include "core/testing/DummyModulator.h"
17 #include "core/testing/DummyPageHolder.h"
18 #include "platform/heap/Handle.h"
19 #include "platform/weborigin/KURL.h"
20 #include "platform/wtf/text/StringBuilder.h"
21 #include "public/platform/Platform.h"
22 #include "public/platform/scheduler/renderer/renderer_scheduler.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace blink {
26
27 namespace {
28
29 class TestModuleTreeClient final
30 : public GarbageCollectedFinalized<TestModuleTreeClient>,
31 public ModuleTreeClient {
32 USING_GARBAGE_COLLECTED_MIXIN(TestModuleTreeClient);
33
34 public:
35 TestModuleTreeClient() = default;
36
37 DEFINE_INLINE_TRACE() { visitor->Trace(module_script_); }
38
39 void NotifyModuleTreeLoadFinished(ModuleScript* module_script) override {
40 was_notify_finished_ = true;
41 module_script_ = module_script;
42 }
43
44 bool WasNotifyFinished() const { return was_notify_finished_; }
45 ModuleScript* GetModuleScript() { return module_script_; }
46
47 private:
48 bool was_notify_finished_ = false;
49 Member<ModuleScript> module_script_;
50 };
51
52 class ModuleTreeLinkerTestModulator final : public DummyModulator {
53 public:
54 ModuleTreeLinkerTestModulator(RefPtr<ScriptState> script_state)
55 : script_state_(std::move(script_state)) {}
56 ~ModuleTreeLinkerTestModulator() override {}
57
58 DECLARE_TRACE();
59
60 // Resolve last |Modulator::FetchSingle()| call.
61 ModuleScript* ResolveSingleModuleScriptFetch(
62 const KURL& url,
63 const Vector<String>& dependency_module_requests) {
64 ScriptState::Scope scope(script_state_.Get());
65
66 StringBuilder source_text;
67 for (const auto& request : dependency_module_requests) {
68 source_text.Append("import '");
69 source_text.Append(request);
70 source_text.Append("';\n");
71 }
72 source_text.Append("export default 'grapes';");
73
74 ScriptModule script_module = ScriptModule::Compile(
75 script_state_->GetIsolate(), source_text.ToString(), url.GetString(),
76 kSharableCrossOrigin);
77 ModuleScript* module_script =
78 ModuleScript::Create(this, script_module, url, "", kParserInserted,
79 WebURLRequest::kFetchCredentialsModeOmit);
80 auto result_request = dependency_module_requests_map_.insert(
81 script_module, dependency_module_requests);
82 EXPECT_TRUE(result_request.is_new_entry);
83 auto result_map = module_map_.insert(url, module_script);
84 EXPECT_TRUE(result_map.is_new_entry);
85
86 EXPECT_EQ(url, pending_request_url_);
87 EXPECT_TRUE(pending_client_);
88 pending_client_->NotifyModuleLoadFinished(module_script);
89 pending_client_.Clear();
90
91 return module_script;
92 }
93
94 // Get AncestorList specified in |Modulator::FetchTreeInternal()| call for
95 // request matching |url|.
96 AncestorList GetAncestorListForTreeFetch(const KURL& url) const {
97 const auto& it = pending_tree_ancestor_list_.Find(url);
98 if (it == pending_tree_ancestor_list_.end())
99 return AncestorList();
100 return it->value;
101 }
102
103 // Resolve |Modulator::FetchTreeInternal()| for given url.
104 void ResolveDependentTreeFetch(const KURL& url) {
105 ScriptState::Scope scope(script_state_.Get());
106
107 ScriptModule script_module = ScriptModule::Compile(
108 script_state_->GetIsolate(), "export default 'pineapples';",
109 url.GetString(), kSharableCrossOrigin);
110 ModuleScript* module_script =
111 ModuleScript::Create(this, script_module, url, "", kParserInserted,
112 WebURLRequest::kFetchCredentialsModeOmit);
113 auto result_map = module_map_.insert(url, module_script);
114 EXPECT_TRUE(result_map.is_new_entry);
115
116 const auto& it = pending_tree_client_map_.Find(url);
117 EXPECT_NE(pending_tree_client_map_.end(), it);
118
119 auto pending_client = it->value;
120 EXPECT_TRUE(pending_client);
121 pending_client->NotifyModuleTreeLoadFinished(module_script);
122 pending_tree_client_map_.erase(it);
123 }
124
125 void SetInstantiateShouldFail(bool b) { instantiate_should_fail_ = b; }
126
127 private:
128 // Implements Modulator:
129
130 void FetchSingle(const ModuleScriptFetchRequest& request,
131 ModuleGraphLevel,
132 SingleModuleClient* client) override {
133 pending_request_url_ = request.Url();
134 EXPECT_FALSE(pending_client_);
135 pending_client_ = client;
136 }
137
138 void FetchTreeInternal(const ModuleScriptFetchRequest& request,
139 const AncestorList& list,
140 ModuleGraphLevel level,
141 ModuleTreeClient* client) override {
142 const auto& url = request.Url();
143
144 auto ancestor_result = pending_tree_ancestor_list_.insert(url, list);
145 EXPECT_TRUE(ancestor_result.is_new_entry);
146
147 EXPECT_EQ(ModuleGraphLevel::kDependentModuleFetch, level);
148
149 auto result_map = pending_tree_client_map_.insert(url, client);
150 EXPECT_TRUE(result_map.is_new_entry);
151 }
152
153 ModuleScript* GetFetchedModuleScript(const KURL& url) override {
154 const auto& it = module_map_.Find(url);
155 if (it == module_map_.end())
156 return nullptr;
157
158 return it->value;
159 }
160
161 ScriptValue InstantiateModule(ScriptModule) override {
162 if (instantiate_should_fail_) {
163 ScriptState::Scope scope(script_state_.Get());
164 v8::Local<v8::Value> error = V8ThrowException::CreateError(
165 script_state_->GetIsolate(), "Instantiation failure.");
166 return ScriptValue(script_state_.Get(), error);
167 }
168 return ScriptValue();
169 }
170
171 Vector<String> ModuleRequestsFromScriptModule(
172 ScriptModule script_module) override {
173 const auto& it = dependency_module_requests_map_.Find(script_module);
174 if (it == dependency_module_requests_map_.end())
175 return Vector<String>();
176
177 return it->value;
178 }
179
180 RefPtr<ScriptState> script_state_;
181 KURL pending_request_url_;
182 Member<SingleModuleClient> pending_client_;
183 HashMap<ScriptModule, Vector<String>> dependency_module_requests_map_;
184 HeapHashMap<KURL, Member<ModuleScript>> module_map_;
185 HeapHashMap<KURL, Member<ModuleTreeClient>> pending_tree_client_map_;
186 HashMap<KURL, AncestorList> pending_tree_ancestor_list_;
187 bool instantiate_should_fail_ = false;
188 };
189
190 DEFINE_TRACE(ModuleTreeLinkerTestModulator) {
191 visitor->Trace(pending_client_);
192 visitor->Trace(module_map_);
193 visitor->Trace(pending_tree_client_map_);
194 DummyModulator::Trace(visitor);
195 }
196
197 } // namespace
198
199 class ModuleTreeLinkerTest : public ::testing::Test {
200 DISALLOW_COPY_AND_ASSIGN(ModuleTreeLinkerTest);
201
202 public:
203 ModuleTreeLinkerTest() = default;
204 void SetUp() override;
205
206 ModuleTreeLinkerTestModulator* GetModulator() { return modulator_.Get(); }
207
208 protected:
209 std::unique_ptr<DummyPageHolder> dummy_page_holder_;
210 Persistent<ModuleTreeLinkerTestModulator> modulator_;
211 };
212
213 void ModuleTreeLinkerTest::SetUp() {
214 dummy_page_holder_ = DummyPageHolder::Create(IntSize(500, 500));
215 RefPtr<ScriptState> script_state =
216 ToScriptStateForMainWorld(&dummy_page_holder_->GetFrame());
217 modulator_ = new ModuleTreeLinkerTestModulator(script_state);
218 }
219
220 TEST_F(ModuleTreeLinkerTest, fetchTreeNoDeps) {
221 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
222
223 KURL url(kParsedURLString, "http://example.com/root.js");
224 ModuleScriptFetchRequest module_request(
225 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
226 TestModuleTreeClient* client = new TestModuleTreeClient;
227 registry->Fetch(module_request, AncestorList(),
228 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
229 client);
230
231 EXPECT_FALSE(client->WasNotifyFinished())
232 << "ModuleTreeLinker should always finish asynchronously.";
233 EXPECT_FALSE(client->GetModuleScript());
234
235 GetModulator()->ResolveSingleModuleScriptFetch(url, {});
236 EXPECT_TRUE(client->WasNotifyFinished());
237 ASSERT_TRUE(client->GetModuleScript());
238 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
239 ModuleInstantiationState::kInstantiated);
240 }
241
242 TEST_F(ModuleTreeLinkerTest, fetchTreeInstantiationFailure) {
243 GetModulator()->SetInstantiateShouldFail(true);
244
245 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
246
247 KURL url(kParsedURLString, "http://example.com/root.js");
248 ModuleScriptFetchRequest module_request(
249 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
250 TestModuleTreeClient* client = new TestModuleTreeClient;
251 registry->Fetch(module_request, AncestorList(),
252 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
253 client);
254
255 EXPECT_FALSE(client->WasNotifyFinished())
256 << "ModuleTreeLinker should always finish asynchronously.";
257 EXPECT_FALSE(client->GetModuleScript());
258
259 GetModulator()->ResolveSingleModuleScriptFetch(url, {});
260 EXPECT_TRUE(client->WasNotifyFinished());
261 ASSERT_TRUE(client->GetModuleScript());
262 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
263 ModuleInstantiationState::kErrored);
264 }
265
266 TEST_F(ModuleTreeLinkerTest, fetchTreeWithSingleDependency) {
267 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
268
269 KURL url(kParsedURLString, "http://example.com/root.js");
270 ModuleScriptFetchRequest module_request(
271 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
272 TestModuleTreeClient* client = new TestModuleTreeClient;
273 registry->Fetch(module_request, AncestorList(),
274 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
275 client);
276
277 EXPECT_FALSE(client->WasNotifyFinished())
278 << "ModuleTreeLinker should always finish asynchronously.";
279 EXPECT_FALSE(client->GetModuleScript());
280
281 GetModulator()->ResolveSingleModuleScriptFetch(url, {"./dep1.js"});
282 EXPECT_FALSE(client->WasNotifyFinished());
283
284 KURL url_dep1(kParsedURLString, "http://example.com/dep1.js");
285 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep1);
286 EXPECT_EQ(1u, ancestor_list.size());
287 EXPECT_TRUE(ancestor_list.Contains(
288 KURL(kParsedURLString, "http://example.com/root.js")));
289
290 GetModulator()->ResolveDependentTreeFetch(url_dep1);
291 EXPECT_TRUE(client->WasNotifyFinished());
292
293 ASSERT_TRUE(client->GetModuleScript());
294 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
295 ModuleInstantiationState::kInstantiated);
296 }
297
298 TEST_F(ModuleTreeLinkerTest, fetchTreeWith3Deps) {
299 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
300
301 KURL url(kParsedURLString, "http://example.com/root.js");
302 ModuleScriptFetchRequest module_request(
303 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
304 TestModuleTreeClient* client = new TestModuleTreeClient;
305 registry->Fetch(module_request, AncestorList(),
306 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
307 client);
308
309 EXPECT_FALSE(client->WasNotifyFinished())
310 << "ModuleTreeLinker should always finish asynchronously.";
311 EXPECT_FALSE(client->GetModuleScript());
312
313 GetModulator()->ResolveSingleModuleScriptFetch(
314 url, {"./dep1.js", "./dep2.js", "./dep3.js"});
315 EXPECT_FALSE(client->WasNotifyFinished());
316
317 Vector<KURL> url_deps;
318 for (int i = 1; i <= 3; ++i) {
319 StringBuilder url_dep_str;
320 url_dep_str.Append("http://example.com/dep");
321 url_dep_str.AppendNumber(i);
322 url_dep_str.Append(".js");
323
324 KURL url_dep(kParsedURLString, url_dep_str.ToString());
325 url_deps.push_back(url_dep);
326 }
327
328 for (const auto& url_dep : url_deps) {
329 SCOPED_TRACE(url_dep.GetString());
330 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep);
331 EXPECT_EQ(1u, ancestor_list.size());
332 EXPECT_TRUE(ancestor_list.Contains(
333 KURL(kParsedURLString, "http://example.com/root.js")));
334 }
335
336 for (const auto& url_dep : url_deps)
337 GetModulator()->ResolveDependentTreeFetch(url_dep);
338
339 EXPECT_TRUE(client->WasNotifyFinished());
340 ASSERT_TRUE(client->GetModuleScript());
341 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
342 ModuleInstantiationState::kInstantiated);
343 }
344
345 TEST_F(ModuleTreeLinkerTest, fetchDependencyTree) {
346 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
347
348 KURL url(kParsedURLString, "http://example.com/depth1.js");
349 ModuleScriptFetchRequest module_request(
350 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
351 TestModuleTreeClient* client = new TestModuleTreeClient;
352 registry->Fetch(
353 module_request,
354 AncestorList{KURL(kParsedURLString, "http://example.com/root.js")},
355 ModuleGraphLevel::kDependentModuleFetch, GetModulator(), client);
356
357 EXPECT_FALSE(client->WasNotifyFinished())
358 << "ModuleTreeLinker should always finish asynchronously.";
359 EXPECT_FALSE(client->GetModuleScript());
360
361 GetModulator()->ResolveSingleModuleScriptFetch(url, {"./depth2.js"});
362
363 KURL url_dep2(kParsedURLString, "http://example.com/depth2.js");
364 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep2);
365 EXPECT_EQ(2u, ancestor_list.size());
366 EXPECT_TRUE(ancestor_list.Contains(
367 KURL(kParsedURLString, "http://example.com/root.js")));
368 EXPECT_TRUE(ancestor_list.Contains(
369 KURL(kParsedURLString, "http://example.com/depth1.js")));
370
371 GetModulator()->ResolveDependentTreeFetch(url_dep2);
372
373 EXPECT_TRUE(client->WasNotifyFinished());
374 ASSERT_TRUE(client->GetModuleScript());
375 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
376 ModuleInstantiationState::kInstantiated);
377 }
378
379 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698