Index: Source/bindings/core/v8/ScriptStreamerTest.cpp |
diff --git a/Source/bindings/core/v8/ScriptStreamerTest.cpp b/Source/bindings/core/v8/ScriptStreamerTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c48ad384b179cb9955f7d5193158d6276887bd14 |
--- /dev/null |
+++ b/Source/bindings/core/v8/ScriptStreamerTest.cpp |
@@ -0,0 +1,221 @@ |
+// Copyright 2014 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 "config.h" |
+#include "bindings/core/v8/ScriptStreamer.h" |
+ |
+#include "bindings/core/v8/ScriptStreamerThread.h" |
+#include "bindings/core/v8/V8Binding.h" |
+#include "bindings/core/v8/V8ScriptRunner.h" |
+#include "core/dom/PendingScript.h" |
+#include "core/frame/Settings.h" |
+#include "platform/Task.h" |
+#include "public/platform/Platform.h" |
+ |
+#include <gtest/gtest.h> |
+#include <v8.h> |
+ |
+namespace blink { |
+ |
+namespace { |
+ |
+class ScriptStreamingTest : public testing::Test { |
+public: |
+ ScriptStreamingTest() |
+ : m_scope(v8::Isolate::GetCurrent()) |
+ , m_settings(Settings::create()) |
+ , m_resourceRequest("http://www.streaming-test.com/") |
+ , m_resource(new ScriptResource(m_resourceRequest, "text/utf-8")) |
+ , m_pendingScript(0, m_resource) // Takes ownership of m_resource. |
+ { |
+ m_settings->setV8ScriptStreamingEnabled(true); |
+ m_resource->setLoading(true); |
+ } |
+ |
+ ScriptState* scriptState() const { return m_scope.scriptState(); } |
+ v8::Isolate* isolate() const { return m_scope.isolate(); } |
+ |
+protected: |
+ void appendData(const char* data) |
+ { |
+ m_resource->appendData(data, strlen(data)); |
+ // Yield control to the background thread, so that V8 gets a change to |
+ // process the data before the main thread adds more. Note that we |
+ // cannot fully control in what kind of chunks the data is passed to V8 |
+ // (if the V8 is not requesting more data between two appendData calls, |
+ // V8 will get both chunks together). |
+ WTF::yield(); |
+ } |
+ |
+ void appendPadding() |
+ { |
+ for (int i = 0; i < 10; ++i) { |
+ appendData(" /* this is padding to make the script long enough, so " |
+ "that V8's buffer gets filled and it starts processing " |
+ "the data */ "); |
+ } |
+ } |
+ |
+ void finish() |
+ { |
+ m_resource->finish(); |
+ m_resource->setLoading(false); |
+ } |
+ |
+ void processTasksUntilStreamingComplete() |
+ { |
+ while (ScriptStreamerThread::shared()->isRunningTask()) { |
+ WebThread* currentThread = blink::Platform::current()->currentThread(); |
+ currentThread->postTask(new Task(WTF::bind(&WebThread::exitRunLoop, currentThread))); |
+ currentThread->enterRunLoop(); |
+ } |
+ } |
+ |
+ V8TestingScope m_scope; |
+ OwnPtr<Settings> m_settings; |
+ // The Resource and PendingScript where we stream from. These don't really |
+ // fetch any data outside the test; the test controls the data by calling |
+ // ScriptResource::appendData. |
+ ResourceRequest m_resourceRequest; |
+ ScriptResource* m_resource; |
+ PendingScript m_pendingScript; |
+}; |
+ |
+class TestScriptResourceClient : public ScriptResourceClient { |
+public: |
+ TestScriptResourceClient() |
+ : m_finished(false) { } |
+ |
+ virtual void notifyFinished(Resource*) OVERRIDE { m_finished = true; } |
+ |
+ bool finished() const { return m_finished; } |
+ |
+private: |
+ bool m_finished; |
+}; |
+ |
+TEST_F(ScriptStreamingTest, CompilingStreamedScript) |
+{ |
+ // Test that we can successfully compile a streamed script. |
+ bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.get(), m_scope.scriptState()); |
+ TestScriptResourceClient client; |
+ m_pendingScript.watchForLoad(&client); |
+ EXPECT_TRUE(started); |
+ |
+ appendData("function foo() {"); |
+ appendPadding(); |
+ appendData("return 5; }"); |
+ appendPadding(); |
+ appendData("foo();"); |
+ EXPECT_FALSE(client.finished()); |
+ finish(); |
+ |
+ // Process tasks on the main thread until the streaming background thread |
+ // has completed its tasks. |
+ processTasksUntilStreamingComplete(); |
+ EXPECT_TRUE(client.finished()); |
+ bool errorOccurred = false; |
+ ScriptSourceCode sourceCode = m_pendingScript.getSource(KURL(), errorOccurred); |
+ EXPECT_FALSE(errorOccurred); |
+ EXPECT_TRUE(sourceCode.streamer()); |
+ v8::TryCatch tryCatch; |
+ v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, isolate()); |
+ EXPECT_FALSE(script.IsEmpty()); |
+ EXPECT_FALSE(tryCatch.HasCaught()); |
+} |
+ |
+TEST_F(ScriptStreamingTest, CompilingStreamedScriptWithParseError) |
+{ |
+ // Test that scripts with parse errors are handled properly. In those cases, |
+ // the V8 side typically finished before loading finishes: make sure we |
+ // handle it gracefully. |
+ bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.get(), m_scope.scriptState()); |
+ TestScriptResourceClient client; |
+ m_pendingScript.watchForLoad(&client); |
+ EXPECT_TRUE(started); |
+ appendData("function foo() {"); |
+ appendData("this is the part which will be a parse error"); |
+ // V8 won't realize the parse error until it actually starts parsing the |
+ // script, and this happens only when its buffer is filled. |
+ appendPadding(); |
+ |
+ EXPECT_FALSE(client.finished()); |
+ |
+ // Force the V8 side to finish before the loading. |
+ processTasksUntilStreamingComplete(); |
+ EXPECT_FALSE(client.finished()); |
+ |
+ finish(); |
+ EXPECT_TRUE(client.finished()); |
+ |
+ bool errorOccurred = false; |
+ ScriptSourceCode sourceCode = m_pendingScript.getSource(KURL(), errorOccurred); |
+ EXPECT_FALSE(errorOccurred); |
+ EXPECT_TRUE(sourceCode.streamer()); |
+ v8::TryCatch tryCatch; |
+ v8::Handle<v8::Script> script = V8ScriptRunner::compileScript(sourceCode, isolate()); |
+ EXPECT_TRUE(script.IsEmpty()); |
+ EXPECT_TRUE(tryCatch.HasCaught()); |
+} |
+ |
+TEST_F(ScriptStreamingTest, CancellingStreaming) |
+{ |
+ // Test that the upper layers (PendingScript and up) can be ramped down |
+ // while streaming is ongoing, and ScriptStreamer handles it gracefully. |
+ bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.get(), m_scope.scriptState()); |
+ TestScriptResourceClient client; |
+ m_pendingScript.watchForLoad(&client); |
+ EXPECT_TRUE(started); |
+ appendData("function foo() {"); |
+ |
+ // In general, we cannot control what the background thread is doing |
+ // (whether it's parsing or waiting for more data). In this test, we have |
+ // given it so little data that it's surely waiting for more. |
+ |
+ // Simulate cancelling the network load (e.g., because the user navigated |
+ // away). |
+ EXPECT_FALSE(client.finished()); |
+ m_pendingScript.stopWatchingForLoad(&client); |
+ m_pendingScript = PendingScript(); // This will destroy m_resource. |
+ m_resource = 0; |
+ |
+ // The V8 side will complete too. This should not crash. We don't receive |
+ // any results from the streaming and the client doesn't get notified. |
+ processTasksUntilStreamingComplete(); |
+ EXPECT_FALSE(client.finished()); |
+} |
+ |
+TEST_F(ScriptStreamingTest, SuppressingStreaming) |
+{ |
+ // If we notice during streaming that there is a code cache, streaming |
+ // is suppressed (V8 doesn't parse while the script is loading), and the |
+ // upper layer (ScriptResourceClient) should get a notification when the |
+ // script is loaded. |
+ bool started = ScriptStreamer::startStreaming(m_pendingScript, m_settings.get(), m_scope.scriptState()); |
+ TestScriptResourceClient client; |
+ m_pendingScript.watchForLoad(&client); |
+ EXPECT_TRUE(started); |
+ appendData("function foo() {"); |
+ appendPadding(); |
+ |
+ m_resource->setCachedMetadata(V8ScriptRunner::tagForCodeCache(), "X", 1, Resource::CacheLocally); |
+ |
+ appendPadding(); |
+ finish(); |
+ processTasksUntilStreamingComplete(); |
+ EXPECT_TRUE(client.finished()); |
+ |
+ bool errorOccurred = false; |
+ ScriptSourceCode sourceCode = m_pendingScript.getSource(KURL(), errorOccurred); |
+ EXPECT_FALSE(errorOccurred); |
+ // ScriptSourceCode doesn't refer to the streamer, since we have suppressed |
+ // the streaming and resumed the non-streaming code path for script |
+ // compilation. |
+ EXPECT_FALSE(sourceCode.streamer()); |
+} |
+ |
+} // namespace |
+ |
+} // namespace blink |