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

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

Issue 2823803003: [ES6 modules] Introduce ModuleTreeLinker (Closed)
Patch Set: yhirano 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
« no previous file with comments | « third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerRegistry.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 enum class ResolveResult { kFailure, kSuccess };
61
62 // Resolve last |Modulator::FetchSingle()| call.
63 ModuleScript* ResolveSingleModuleScriptFetch(
64 const KURL& url,
65 const Vector<String>& dependency_module_requests) {
66 ScriptState::Scope scope(script_state_.Get());
67
68 StringBuilder source_text;
69 for (const auto& request : dependency_module_requests) {
70 source_text.Append("import '");
71 source_text.Append(request);
72 source_text.Append("';\n");
73 }
74 source_text.Append("export default 'grapes';");
75
76 ScriptModule script_module = ScriptModule::Compile(
77 script_state_->GetIsolate(), source_text.ToString(), url.GetString(),
78 kSharableCrossOrigin);
79 ModuleScript* module_script =
80 ModuleScript::Create(this, script_module, url, "", kParserInserted,
81 WebURLRequest::kFetchCredentialsModeOmit);
82 auto result_request = dependency_module_requests_map_.insert(
83 script_module, dependency_module_requests);
84 EXPECT_TRUE(result_request.is_new_entry);
85 auto result_map = module_map_.insert(url, module_script);
86 EXPECT_TRUE(result_map.is_new_entry);
87
88 EXPECT_EQ(url, pending_request_url_);
89 EXPECT_TRUE(pending_client_);
90 pending_client_->NotifyModuleLoadFinished(module_script);
91 pending_client_.Clear();
92
93 return module_script;
94 }
95
96 // Get AncestorList specified in |Modulator::FetchTreeInternal()| call for
97 // request matching |url|.
98 AncestorList GetAncestorListForTreeFetch(const KURL& url) const {
99 const auto& it = pending_tree_ancestor_list_.Find(url);
100 if (it == pending_tree_ancestor_list_.end())
101 return AncestorList();
102 return it->value;
103 }
104
105 // Resolve |Modulator::FetchTreeInternal()| for given url.
106 void ResolveDependentTreeFetch(const KURL& url, ResolveResult result) {
107 const auto& it = pending_tree_client_map_.Find(url);
108 EXPECT_NE(pending_tree_client_map_.end(), it);
109 auto pending_client = it->value;
110 EXPECT_TRUE(pending_client);
111 pending_tree_client_map_.erase(it);
112
113 if (result == ResolveResult::kFailure) {
114 pending_client->NotifyModuleTreeLoadFinished(nullptr);
115 return;
116 }
117 EXPECT_EQ(ResolveResult::kSuccess, result);
118
119 ScriptState::Scope scope(script_state_.Get());
120
121 ScriptModule script_module = ScriptModule::Compile(
122 script_state_->GetIsolate(), "export default 'pineapples';",
123 url.GetString(), kSharableCrossOrigin);
124 ModuleScript* module_script =
125 ModuleScript::Create(this, script_module, url, "", kParserInserted,
126 WebURLRequest::kFetchCredentialsModeOmit);
127 auto result_map = module_map_.insert(url, module_script);
128 EXPECT_TRUE(result_map.is_new_entry);
129
130 pending_client->NotifyModuleTreeLoadFinished(module_script);
131 }
132
133 void SetInstantiateShouldFail(bool b) { instantiate_should_fail_ = b; }
134
135 private:
136 // Implements Modulator:
137
138 void FetchSingle(const ModuleScriptFetchRequest& request,
139 ModuleGraphLevel,
140 SingleModuleClient* client) override {
141 pending_request_url_ = request.Url();
142 EXPECT_FALSE(pending_client_);
143 pending_client_ = client;
144 }
145
146 void FetchTreeInternal(const ModuleScriptFetchRequest& request,
147 const AncestorList& list,
148 ModuleGraphLevel level,
149 ModuleTreeClient* client) override {
150 const auto& url = request.Url();
151
152 auto ancestor_result = pending_tree_ancestor_list_.insert(url, list);
153 EXPECT_TRUE(ancestor_result.is_new_entry);
154
155 EXPECT_EQ(ModuleGraphLevel::kDependentModuleFetch, level);
156
157 auto result_map = pending_tree_client_map_.insert(url, client);
158 EXPECT_TRUE(result_map.is_new_entry);
159 }
160
161 ModuleScript* GetFetchedModuleScript(const KURL& url) override {
162 const auto& it = module_map_.Find(url);
163 if (it == module_map_.end())
164 return nullptr;
165
166 return it->value;
167 }
168
169 ScriptValue InstantiateModule(ScriptModule) override {
170 if (instantiate_should_fail_) {
171 ScriptState::Scope scope(script_state_.Get());
172 v8::Local<v8::Value> error = V8ThrowException::CreateError(
173 script_state_->GetIsolate(), "Instantiation failure.");
174 return ScriptValue(script_state_.Get(), error);
175 }
176 return ScriptValue();
177 }
178
179 Vector<String> ModuleRequestsFromScriptModule(
180 ScriptModule script_module) override {
181 const auto& it = dependency_module_requests_map_.Find(script_module);
182 if (it == dependency_module_requests_map_.end())
183 return Vector<String>();
184
185 return it->value;
186 }
187
188 RefPtr<ScriptState> script_state_;
189 KURL pending_request_url_;
190 Member<SingleModuleClient> pending_client_;
191 HashMap<ScriptModule, Vector<String>> dependency_module_requests_map_;
192 HeapHashMap<KURL, Member<ModuleScript>> module_map_;
193 HeapHashMap<KURL, Member<ModuleTreeClient>> pending_tree_client_map_;
194 HashMap<KURL, AncestorList> pending_tree_ancestor_list_;
195 bool instantiate_should_fail_ = false;
196 };
197
198 DEFINE_TRACE(ModuleTreeLinkerTestModulator) {
199 visitor->Trace(pending_client_);
200 visitor->Trace(module_map_);
201 visitor->Trace(pending_tree_client_map_);
202 DummyModulator::Trace(visitor);
203 }
204
205 } // namespace
206
207 class ModuleTreeLinkerTest : public ::testing::Test {
208 DISALLOW_COPY_AND_ASSIGN(ModuleTreeLinkerTest);
209
210 public:
211 ModuleTreeLinkerTest() = default;
212 void SetUp() override;
213
214 ModuleTreeLinkerTestModulator* GetModulator() { return modulator_.Get(); }
215
216 protected:
217 std::unique_ptr<DummyPageHolder> dummy_page_holder_;
218 Persistent<ModuleTreeLinkerTestModulator> modulator_;
219 };
220
221 void ModuleTreeLinkerTest::SetUp() {
222 dummy_page_holder_ = DummyPageHolder::Create(IntSize(500, 500));
223 RefPtr<ScriptState> script_state =
224 ToScriptStateForMainWorld(&dummy_page_holder_->GetFrame());
225 modulator_ = new ModuleTreeLinkerTestModulator(script_state);
226 }
227
228 TEST_F(ModuleTreeLinkerTest, fetchTreeNoDeps) {
229 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
230
231 KURL url(kParsedURLString, "http://example.com/root.js");
232 ModuleScriptFetchRequest module_request(
233 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
234 TestModuleTreeClient* client = new TestModuleTreeClient;
235 registry->Fetch(module_request, AncestorList(),
236 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
237 client);
238
239 EXPECT_FALSE(client->WasNotifyFinished())
240 << "ModuleTreeLinker should always finish asynchronously.";
241 EXPECT_FALSE(client->GetModuleScript());
242
243 GetModulator()->ResolveSingleModuleScriptFetch(url, {});
244 EXPECT_TRUE(client->WasNotifyFinished());
245 ASSERT_TRUE(client->GetModuleScript());
246 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
247 ModuleInstantiationState::kInstantiated);
248 }
249
250 TEST_F(ModuleTreeLinkerTest, fetchTreeInstantiationFailure) {
251 GetModulator()->SetInstantiateShouldFail(true);
252
253 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
254
255 KURL url(kParsedURLString, "http://example.com/root.js");
256 ModuleScriptFetchRequest module_request(
257 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
258 TestModuleTreeClient* client = new TestModuleTreeClient;
259 registry->Fetch(module_request, AncestorList(),
260 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
261 client);
262
263 EXPECT_FALSE(client->WasNotifyFinished())
264 << "ModuleTreeLinker should always finish asynchronously.";
265 EXPECT_FALSE(client->GetModuleScript());
266
267 GetModulator()->ResolveSingleModuleScriptFetch(url, {});
268 EXPECT_TRUE(client->WasNotifyFinished());
269 ASSERT_TRUE(client->GetModuleScript());
270 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
271 ModuleInstantiationState::kErrored);
272 }
273
274 TEST_F(ModuleTreeLinkerTest, fetchTreeWithSingleDependency) {
275 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
276
277 KURL url(kParsedURLString, "http://example.com/root.js");
278 ModuleScriptFetchRequest module_request(
279 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
280 TestModuleTreeClient* client = new TestModuleTreeClient;
281 registry->Fetch(module_request, AncestorList(),
282 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
283 client);
284
285 EXPECT_FALSE(client->WasNotifyFinished())
286 << "ModuleTreeLinker should always finish asynchronously.";
287 EXPECT_FALSE(client->GetModuleScript());
288
289 GetModulator()->ResolveSingleModuleScriptFetch(url, {"./dep1.js"});
290 EXPECT_FALSE(client->WasNotifyFinished());
291
292 KURL url_dep1(kParsedURLString, "http://example.com/dep1.js");
293 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep1);
294 EXPECT_EQ(1u, ancestor_list.size());
295 EXPECT_TRUE(ancestor_list.Contains(
296 KURL(kParsedURLString, "http://example.com/root.js")));
297
298 GetModulator()->ResolveDependentTreeFetch(
299 url_dep1, ModuleTreeLinkerTestModulator::ResolveResult::kSuccess);
300 EXPECT_TRUE(client->WasNotifyFinished());
301
302 ASSERT_TRUE(client->GetModuleScript());
303 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
304 ModuleInstantiationState::kInstantiated);
305 }
306
307 TEST_F(ModuleTreeLinkerTest, fetchTreeWith3Deps) {
308 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
309
310 KURL url(kParsedURLString, "http://example.com/root.js");
311 ModuleScriptFetchRequest module_request(
312 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
313 TestModuleTreeClient* client = new TestModuleTreeClient;
314 registry->Fetch(module_request, AncestorList(),
315 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
316 client);
317
318 EXPECT_FALSE(client->WasNotifyFinished())
319 << "ModuleTreeLinker should always finish asynchronously.";
320 EXPECT_FALSE(client->GetModuleScript());
321
322 GetModulator()->ResolveSingleModuleScriptFetch(
323 url, {"./dep1.js", "./dep2.js", "./dep3.js"});
324 EXPECT_FALSE(client->WasNotifyFinished());
325
326 Vector<KURL> url_deps;
327 for (int i = 1; i <= 3; ++i) {
328 StringBuilder url_dep_str;
329 url_dep_str.Append("http://example.com/dep");
330 url_dep_str.AppendNumber(i);
331 url_dep_str.Append(".js");
332
333 KURL url_dep(kParsedURLString, url_dep_str.ToString());
334 url_deps.push_back(url_dep);
335 }
336
337 for (const auto& url_dep : url_deps) {
338 SCOPED_TRACE(url_dep.GetString());
339 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep);
340 EXPECT_EQ(1u, ancestor_list.size());
341 EXPECT_TRUE(ancestor_list.Contains(
342 KURL(kParsedURLString, "http://example.com/root.js")));
343 }
344
345 for (const auto& url_dep : url_deps) {
yhirano 2017/04/19 05:25:08 EXPECT_FALSE(client->WasNotifyFinished());
kouhei (in TOK) 2017/04/19 05:50:35 Done.
346 GetModulator()->ResolveDependentTreeFetch(
347 url_dep, ModuleTreeLinkerTestModulator::ResolveResult::kSuccess);
348 }
349
350 EXPECT_TRUE(client->WasNotifyFinished());
351 ASSERT_TRUE(client->GetModuleScript());
352 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
353 ModuleInstantiationState::kInstantiated);
354 }
355
356 TEST_F(ModuleTreeLinkerTest, fetchTreeWith3Deps1Fail) {
357 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
358
359 KURL url(kParsedURLString, "http://example.com/root.js");
360 ModuleScriptFetchRequest module_request(
361 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
362 TestModuleTreeClient* client = new TestModuleTreeClient;
363 registry->Fetch(module_request, AncestorList(),
364 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
365 client);
366
367 EXPECT_FALSE(client->WasNotifyFinished())
368 << "ModuleTreeLinker should always finish asynchronously.";
369 EXPECT_FALSE(client->GetModuleScript());
370
371 GetModulator()->ResolveSingleModuleScriptFetch(
372 url, {"./dep1.js", "./dep2.js", "./dep3.js"});
373 EXPECT_FALSE(client->WasNotifyFinished());
374
375 Vector<KURL> url_deps;
376 for (int i = 1; i <= 3; ++i) {
377 StringBuilder url_dep_str;
378 url_dep_str.Append("http://example.com/dep");
379 url_dep_str.AppendNumber(i);
380 url_dep_str.Append(".js");
381
382 KURL url_dep(kParsedURLString, url_dep_str.ToString());
383 url_deps.push_back(url_dep);
384 }
385
386 for (const auto& url_dep : url_deps) {
387 SCOPED_TRACE(url_dep.GetString());
388 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep);
389 EXPECT_EQ(1u, ancestor_list.size());
390 EXPECT_TRUE(ancestor_list.Contains(
391 KURL(kParsedURLString, "http://example.com/root.js")));
392 }
393
394 auto url_dep = url_deps.back();
395 url_deps.pop_back();
396 GetModulator()->ResolveDependentTreeFetch(
397 url_dep, ModuleTreeLinkerTestModulator::ResolveResult::kSuccess);
398 url_dep = url_deps.back();
399 url_deps.pop_back();
yhirano 2017/04/19 05:25:08 EXPECT_FALSE(client->WasNotifyFinished());
kouhei (in TOK) 2017/04/19 05:50:35 Done.
400 GetModulator()->ResolveDependentTreeFetch(
401 url_dep, ModuleTreeLinkerTestModulator::ResolveResult::kFailure);
402
403 EXPECT_TRUE(client->WasNotifyFinished());
404 EXPECT_FALSE(client->GetModuleScript());
405
406 // Check below doesn't crash.
407 url_dep = url_deps.back();
408 url_deps.pop_back();
409 GetModulator()->ResolveDependentTreeFetch(
410 url_dep, ModuleTreeLinkerTestModulator::ResolveResult::kSuccess);
411 EXPECT_TRUE(url_deps.IsEmpty());
412 }
413
414 TEST_F(ModuleTreeLinkerTest, fetchDependencyTree) {
415 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
416
417 KURL url(kParsedURLString, "http://example.com/depth1.js");
418 ModuleScriptFetchRequest module_request(
419 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
420 TestModuleTreeClient* client = new TestModuleTreeClient;
421 registry->Fetch(
422 module_request,
423 AncestorList{KURL(kParsedURLString, "http://example.com/root.js")},
424 ModuleGraphLevel::kDependentModuleFetch, GetModulator(), client);
425
426 EXPECT_FALSE(client->WasNotifyFinished())
427 << "ModuleTreeLinker should always finish asynchronously.";
428 EXPECT_FALSE(client->GetModuleScript());
429
430 GetModulator()->ResolveSingleModuleScriptFetch(url, {"./depth2.js"});
431
432 KURL url_dep2(kParsedURLString, "http://example.com/depth2.js");
433 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep2);
434 EXPECT_EQ(2u, ancestor_list.size());
435 EXPECT_TRUE(ancestor_list.Contains(
436 KURL(kParsedURLString, "http://example.com/root.js")));
437 EXPECT_TRUE(ancestor_list.Contains(
438 KURL(kParsedURLString, "http://example.com/depth1.js")));
439
440 GetModulator()->ResolveDependentTreeFetch(
441 url_dep2, ModuleTreeLinkerTestModulator::ResolveResult::kSuccess);
442
443 EXPECT_TRUE(client->WasNotifyFinished());
444 ASSERT_TRUE(client->GetModuleScript());
445 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
446 ModuleInstantiationState::kInstantiated);
447 }
448
449 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinkerRegistry.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698