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

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

Issue 2555653002: [WIP Prototype] ES6 https://html.spec.whatwg.org/#fetch-a-single-module-script implementation (Closed)
Patch Set: ModuleTreeLinkerTest.fetchTreeInstantiationFailure 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) {
126 instantiate_should_fail_ = b;
127 }
128
129 private:
130 // Implements Modulator:
131
132 void FetchSingle(const ModuleScriptFetchRequest& request,
133 ModuleGraphLevel,
134 SingleModuleClient* client) override {
135 pending_request_url_ = request.Url();
136 EXPECT_FALSE(pending_client_);
137 pending_client_ = client;
138 }
139
140 void FetchTreeInternal(const ModuleScriptFetchRequest& request,
141 const AncestorList& list,
142 ModuleGraphLevel level,
143 ModuleTreeClient* client) override {
144 const auto& url = request.Url();
145
146 auto ancestor_result = pending_tree_ancestor_list_.insert(url, list);
147 EXPECT_TRUE(ancestor_result.is_new_entry);
148
149 EXPECT_EQ(ModuleGraphLevel::kDependentModuleFetch, level);
150
151 auto result_map = pending_tree_client_map_.insert(url, client);
152 EXPECT_TRUE(result_map.is_new_entry);
153 }
154
155 ModuleScript* GetFetchedModuleScript(const KURL& url) override {
156 const auto& it = module_map_.Find(url);
157 if (it == module_map_.end())
158 return nullptr;
159
160 return it->value;
161 }
162
163 ScriptValue InstantiateModule(ScriptModule) override {
164 if (instantiate_should_fail_) {
165 ScriptState::Scope scope(script_state_.Get());
166 v8::Local<v8::Value> error = V8ThrowException::CreateError(script_state_-> GetIsolate(), "Instantiation failure.");
167 return ScriptValue(script_state_.Get(), error);
168 }
169 return ScriptValue();
170 }
171
172 Vector<String> ModuleRequestsFromScriptModule(
173 ScriptModule script_module) override {
174 const auto& it = dependency_module_requests_map_.Find(script_module);
175 if (it == dependency_module_requests_map_.end())
176 return Vector<String>();
177
178 return it->value;
179 }
180
181 RefPtr<ScriptState> script_state_;
182 KURL pending_request_url_;
183 Member<SingleModuleClient> pending_client_;
184 HashMap<ScriptModule, Vector<String>> dependency_module_requests_map_;
185 HeapHashMap<KURL, Member<ModuleScript>> module_map_;
186 HeapHashMap<KURL, Member<ModuleTreeClient>> pending_tree_client_map_;
187 HashMap<KURL, AncestorList> pending_tree_ancestor_list_;
188 bool instantiate_should_fail_ = false;
189 };
190
191 DEFINE_TRACE(ModuleTreeLinkerTestModulator) {
192 visitor->Trace(pending_client_);
193 visitor->Trace(module_map_);
194 visitor->Trace(pending_tree_client_map_);
195 DummyModulator::Trace(visitor);
196 }
197
198 } // namespace
199
200 class ModuleTreeLinkerTest : public ::testing::Test {
201 DISALLOW_COPY_AND_ASSIGN(ModuleTreeLinkerTest);
202
203 public:
204 ModuleTreeLinkerTest() = default;
205 void SetUp() override;
206
207 ModuleTreeLinkerTestModulator* GetModulator() { return modulator_.Get(); }
208
209 protected:
210 std::unique_ptr<DummyPageHolder> dummy_page_holder_;
211 Persistent<ModuleTreeLinkerTestModulator> modulator_;
212 };
213
214 void ModuleTreeLinkerTest::SetUp() {
215 dummy_page_holder_ = DummyPageHolder::Create(IntSize(500, 500));
216 RefPtr<ScriptState> script_state =
217 ToScriptStateForMainWorld(&dummy_page_holder_->GetFrame());
218 modulator_ = new ModuleTreeLinkerTestModulator(script_state);
219 }
220
221 TEST_F(ModuleTreeLinkerTest, fetchTreeNoDeps) {
222 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
223
224 KURL url(kParsedURLString, "http://example.com/root.js");
225 ModuleScriptFetchRequest module_request(
226 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
227 TestModuleTreeClient* client = new TestModuleTreeClient;
228 registry->Fetch(module_request, AncestorList(),
229 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
230 client);
231
232 EXPECT_FALSE(client->WasNotifyFinished())
233 << "ModuleTreeLinker should always finish asynchronously.";
234 EXPECT_FALSE(client->GetModuleScript());
235
236 GetModulator()->ResolveSingleModuleScriptFetch(url, {});
237 EXPECT_TRUE(client->WasNotifyFinished());
238 ASSERT_TRUE(client->GetModuleScript());
239 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
240 ModuleInstantiationState::kInstantiated);
241 }
242
243 TEST_F(ModuleTreeLinkerTest, fetchTreeInstantiationFailure) {
244 GetModulator()->SetInstantiateShouldFail(true);
245
246 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
247
248 KURL url(kParsedURLString, "http://example.com/root.js");
249 ModuleScriptFetchRequest module_request(
250 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
251 TestModuleTreeClient* client = new TestModuleTreeClient;
252 registry->Fetch(module_request, AncestorList(),
253 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
254 client);
255
256 EXPECT_FALSE(client->WasNotifyFinished())
257 << "ModuleTreeLinker should always finish asynchronously.";
258 EXPECT_FALSE(client->GetModuleScript());
259
260 GetModulator()->ResolveSingleModuleScriptFetch(url, {});
261 EXPECT_TRUE(client->WasNotifyFinished());
262 ASSERT_TRUE(client->GetModuleScript());
263 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
264 ModuleInstantiationState::kErrored);
265 }
266
267
268 TEST_F(ModuleTreeLinkerTest, fetchTreeWithSingleDependency) {
269 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
270
271 KURL url(kParsedURLString, "http://example.com/root.js");
272 ModuleScriptFetchRequest module_request(
273 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
274 TestModuleTreeClient* client = new TestModuleTreeClient;
275 registry->Fetch(module_request, AncestorList(),
276 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
277 client);
278
279 EXPECT_FALSE(client->WasNotifyFinished())
280 << "ModuleTreeLinker should always finish asynchronously.";
281 EXPECT_FALSE(client->GetModuleScript());
282
283 GetModulator()->ResolveSingleModuleScriptFetch(url, {"./dep1.js"});
284 EXPECT_FALSE(client->WasNotifyFinished());
285
286 KURL url_dep1(kParsedURLString, "http://example.com/dep1.js");
287 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep1);
288 EXPECT_EQ(1u, ancestor_list.size());
289 EXPECT_TRUE(ancestor_list.Contains(
290 KURL(kParsedURLString, "http://example.com/root.js")));
291
292 GetModulator()->ResolveDependentTreeFetch(url_dep1);
293 EXPECT_TRUE(client->WasNotifyFinished());
294
295 ASSERT_TRUE(client->GetModuleScript());
296 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
297 ModuleInstantiationState::kInstantiated);
298 }
299
300 TEST_F(ModuleTreeLinkerTest, fetchTreeWith3Deps) {
301 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
302
303 KURL url(kParsedURLString, "http://example.com/root.js");
304 ModuleScriptFetchRequest module_request(
305 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
306 TestModuleTreeClient* client = new TestModuleTreeClient;
307 registry->Fetch(module_request, AncestorList(),
308 ModuleGraphLevel::kTopLevelModuleFetch, GetModulator(),
309 client);
310
311 EXPECT_FALSE(client->WasNotifyFinished())
312 << "ModuleTreeLinker should always finish asynchronously.";
313 EXPECT_FALSE(client->GetModuleScript());
314
315 GetModulator()->ResolveSingleModuleScriptFetch(
316 url, {"./dep1.js", "./dep2.js", "./dep3.js"});
317 EXPECT_FALSE(client->WasNotifyFinished());
318
319 Vector<KURL> url_deps;
320 for (int i = 1; i <= 3; ++i) {
321 StringBuilder url_dep_str;
322 url_dep_str.Append("http://example.com/dep");
323 url_dep_str.AppendNumber(i);
324 url_dep_str.Append(".js");
325
326 KURL url_dep(kParsedURLString, url_dep_str.ToString());
327 url_deps.push_back(url_dep);
328 }
329
330 for (const auto& url_dep : url_deps) {
331 SCOPED_TRACE(url_dep.GetString());
332 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep);
333 EXPECT_EQ(1u, ancestor_list.size());
334 EXPECT_TRUE(ancestor_list.Contains(
335 KURL(kParsedURLString, "http://example.com/root.js")));
336 }
337
338 for (const auto& url_dep : url_deps)
339 GetModulator()->ResolveDependentTreeFetch(url_dep);
340
341 EXPECT_TRUE(client->WasNotifyFinished());
342 ASSERT_TRUE(client->GetModuleScript());
343 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
344 ModuleInstantiationState::kInstantiated);
345 }
346
347 TEST_F(ModuleTreeLinkerTest, fetchDependencyTree) {
348 ModuleTreeLinkerRegistry* registry = ModuleTreeLinkerRegistry::Create();
349
350 KURL url(kParsedURLString, "http://example.com/depth1.js");
351 ModuleScriptFetchRequest module_request(
352 url, String(), kParserInserted, WebURLRequest::kFetchCredentialsModeOmit);
353 TestModuleTreeClient* client = new TestModuleTreeClient;
354 registry->Fetch(
355 module_request,
356 AncestorList{KURL(kParsedURLString, "http://example.com/root.js")},
357 ModuleGraphLevel::kDependentModuleFetch, GetModulator(), client);
358
359 EXPECT_FALSE(client->WasNotifyFinished())
360 << "ModuleTreeLinker should always finish asynchronously.";
361 EXPECT_FALSE(client->GetModuleScript());
362
363 GetModulator()->ResolveSingleModuleScriptFetch(url, {"./depth2.js"});
364
365 KURL url_dep2(kParsedURLString, "http://example.com/depth2.js");
366 auto ancestor_list = GetModulator()->GetAncestorListForTreeFetch(url_dep2);
367 EXPECT_EQ(2u, ancestor_list.size());
368 EXPECT_TRUE(ancestor_list.Contains(
369 KURL(kParsedURLString, "http://example.com/root.js")));
370 EXPECT_TRUE(ancestor_list.Contains(
371 KURL(kParsedURLString, "http://example.com/depth1.js")));
372
373 GetModulator()->ResolveDependentTreeFetch(url_dep2);
374
375 EXPECT_TRUE(client->WasNotifyFinished());
376 ASSERT_TRUE(client->GetModuleScript());
377 EXPECT_EQ(client->GetModuleScript()->InstantiationState(),
378 ModuleInstantiationState::kInstantiated);
379 }
380
381 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698