Chromium Code Reviews| Index: third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp |
| diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9e12a07fcbd8d43c135f6bb641d64afc99a6cc56 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletGlobalScopeTest.cpp |
| @@ -0,0 +1,282 @@ |
| +// 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 "modules/webaudio/AudioWorkletGlobalScope.h" |
| + |
| +#include "bindings/core/v8/ScriptSourceCode.h" |
| +#include "bindings/core/v8/ScriptState.h" |
| +#include "bindings/core/v8/ScriptValue.h" |
| +#include "bindings/core/v8/SourceLocation.h" |
| +#include "bindings/core/v8/ToV8.h" |
| +#include "bindings/core/v8/V8Binding.h" |
| +#include "bindings/core/v8/V8BindingForTesting.h" |
| +#include "bindings/core/v8/V8BindingMacros.h" |
| +#include "bindings/core/v8/V8GCController.h" |
| +#include "bindings/core/v8/V8ObjectConstructor.h" |
| +#include "bindings/core/v8/WorkerOrWorkletScriptController.h" |
| +#include "core/workers/WorkerBackingThread.h" |
| +#include "core/workers/WorkerReportingProxy.h" |
| +#include "core/workers/WorkerThreadStartupData.h" |
| +#include "modules/webaudio/AudioBuffer.h" |
| +#include "modules/webaudio/AudioWorkletProcessor.h" |
| +#include "modules/webaudio/AudioWorkletProcessorDefinition.h" |
| +#include "modules/webaudio/AudioWorkletThread.h" |
| +#include "platform/weborigin/SecurityOrigin.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace blink { |
| + |
| +namespace { |
| + |
| +static const size_t kRenderQuantumFrames = 128; |
| +static const float kTestingSampleRate = 44100; |
|
Raymond Toy
2017/03/17 16:45:32
Add comment that 44100 isn't really special, just
hongchan
2017/03/20 17:03:32
Done.
|
| + |
| +// A null WorkerReportingProxy, supplied when creating AudioWorkletThreads. |
| +class TestAudioWorkletReportingProxy : public WorkerReportingProxy { |
| + public: |
| + static std::unique_ptr<TestAudioWorkletReportingProxy> create() { |
| + return WTF::wrapUnique(new TestAudioWorkletReportingProxy()); |
| + } |
| + |
| + // (Empty) WorkerReportingProxy implementation: |
| + void countFeature(UseCounter::Feature) override {} |
| + void countDeprecation(UseCounter::Feature) override {} |
| + void reportException(const String& errorMessage, |
| + std::unique_ptr<SourceLocation>, |
| + int exceptionId) override {} |
| + void reportConsoleMessage(MessageSource, |
| + MessageLevel, |
| + const String& message, |
| + SourceLocation*) override {} |
| + void postMessageToPageInspector(const String&) override {} |
| + void didEvaluateWorkerScript(bool success) override {} |
| + void didCloseWorkerGlobalScope() override {} |
| + void willDestroyWorkerGlobalScope() override {} |
| + void didTerminateWorkerThread() override {} |
| + |
| + private: |
| + TestAudioWorkletReportingProxy() {} |
| +}; |
| + |
| +} // namespace |
| + |
| +class AudioWorkletGlobalScopeTest : public ::testing::Test { |
| + public: |
| + void SetUp() override { |
| + AudioWorkletThread::createSharedBackingThreadForTest(); |
| + m_reportingProxy = TestAudioWorkletReportingProxy::create(); |
| + m_securityOrigin = |
| + SecurityOrigin::create(KURL(ParsedURLString, "http://fake.url/")); |
| + } |
| + |
| + void TearDown() override { AudioWorkletThread::clearSharedBackingThread(); } |
| + |
| + std::unique_ptr<AudioWorkletThread> createAudioWorkletThread() { |
| + std::unique_ptr<AudioWorkletThread> thread = |
| + AudioWorkletThread::create(nullptr, *m_reportingProxy); |
| + thread->start( |
| + WorkerThreadStartupData::create( |
| + KURL(ParsedURLString, "http://fake.url/"), "fake user agent", "", |
| + nullptr, DontPauseWorkerGlobalScopeOnStart, nullptr, "", |
| + m_securityOrigin.get(), nullptr, WebAddressSpaceLocal, nullptr, |
| + nullptr, WorkerV8Settings::Default()), |
| + ParentFrameTaskRunners::create(nullptr)); |
| + return thread; |
| + } |
| + |
| + void runBasicTest(WorkerThread* thread) { |
|
Raymond Toy
2017/03/17 16:45:32
Describe what these tests are trying to test.
hongchan
2017/03/20 17:03:32
Done.
|
| + WaitableEvent waitableEvent; |
| + thread->postTask( |
| + BLINK_FROM_HERE, |
| + crossThreadBind( |
| + &AudioWorkletGlobalScopeTest::runBasicTestOnWorkletThread, |
| + crossThreadUnretained(this), crossThreadUnretained(thread), |
| + crossThreadUnretained(&waitableEvent))); |
| + waitableEvent.wait(); |
| + } |
| + |
| + void runSimpleProcessTest(WorkerThread* thread) { |
| + WaitableEvent waitableEvent; |
| + thread->postTask( |
| + BLINK_FROM_HERE, |
| + crossThreadBind( |
| + &AudioWorkletGlobalScopeTest::runSimpleProcessTestOnWorkletThread, |
| + crossThreadUnretained(this), crossThreadUnretained(thread), |
| + crossThreadUnretained(&waitableEvent))); |
| + waitableEvent.wait(); |
| + } |
| + |
| + void runParsingTest(WorkerThread* thread) { |
| + WaitableEvent waitableEvent; |
| + thread->postTask( |
| + BLINK_FROM_HERE, |
| + crossThreadBind( |
| + &AudioWorkletGlobalScopeTest::runParsingTestOnWorkletThread, |
| + crossThreadUnretained(this), crossThreadUnretained(thread), |
| + crossThreadUnretained(&waitableEvent))); |
| + waitableEvent.wait(); |
| + } |
| + |
| + private: |
| + void runBasicTestOnWorkletThread(WorkerThread* thread, |
| + WaitableEvent* waitEvent) { |
| + EXPECT_TRUE(thread->isCurrentThread()); |
| + |
| + AudioWorkletGlobalScope* globalScope = |
| + static_cast<AudioWorkletGlobalScope*>(thread->globalScope()); |
| + EXPECT_TRUE(globalScope); |
| + EXPECT_TRUE(globalScope->isAudioWorkletGlobalScope()); |
| + |
| + ScriptState* scriptState = |
| + globalScope->scriptController()->getScriptState(); |
| + EXPECT_TRUE(scriptState); |
| + |
| + v8::Isolate* isolate = scriptState->isolate(); |
| + EXPECT_TRUE(isolate); |
| + |
| + ScriptState::Scope scope(scriptState); |
| + |
| + globalScope->scriptController()->evaluate(ScriptSourceCode( |
| + R"JS( |
| + registerProcessor('testProcessor', class { |
| + constructor () {} |
| + process () {} |
| + }); |
| + )JS")); |
| + |
| + AudioWorkletProcessorDefinition* definition = |
| + globalScope->findDefinition("testProcessor"); |
| + EXPECT_TRUE(definition); |
| + EXPECT_EQ(definition->name(), "testProcessor"); |
| + EXPECT_TRUE(definition->constructorLocal(isolate)->IsFunction()); |
| + EXPECT_TRUE(definition->processLocal(isolate)->IsFunction()); |
| + |
| + AudioWorkletProcessor* processor = |
| + globalScope->createInstance("testProcessor"); |
| + EXPECT_TRUE(processor); |
| + EXPECT_EQ(processor->name(), "testProcessor"); |
| + EXPECT_TRUE(processor->instanceLocal(isolate)->IsObject()); |
| + |
| + waitEvent->signal(); |
| + } |
| + |
| + void runParsingTestOnWorkletThread(WorkerThread* thread, |
| + WaitableEvent* waitEvent) { |
| + EXPECT_TRUE(thread->isCurrentThread()); |
| + |
| + AudioWorkletGlobalScope* globalScope = |
| + static_cast<AudioWorkletGlobalScope*>(thread->globalScope()); |
| + ScriptState* scriptState = |
| + globalScope->scriptController()->getScriptState(); |
| + |
| + ScriptState::Scope scope(scriptState); |
| + |
| + globalScope->scriptController()->evaluate(ScriptSourceCode( |
| + R"JS( |
| + var class1 = function () {}; |
| + class1.prototype.process = function () {}; |
| + registerProcessor('class1', class1); |
| + |
| + var class2 = function () {}; |
| + class2.prototype = { process: function () {} }; |
| + registerProcessor('class2', class2); |
| + |
| + var class3 = function () {}; |
| + Object.defineProperty(class3, 'prototype', { |
| + get: function () { |
| + return { |
| + process: function () {} |
| + }; |
| + } |
| + }); |
| + registerProcessor('class3', class3); |
| + )JS")); |
| + |
| + EXPECT_TRUE(globalScope->findDefinition("class1")); |
| + EXPECT_TRUE(globalScope->findDefinition("class2")); |
| + EXPECT_FALSE(globalScope->findDefinition("class3")); |
| + |
| + waitEvent->signal(); |
| + } |
| + |
| + void runSimpleProcessTestOnWorkletThread(WorkerThread* thread, |
| + WaitableEvent* waitEvent) { |
| + EXPECT_TRUE(thread->isCurrentThread()); |
| + |
| + AudioWorkletGlobalScope* globalScope = |
| + static_cast<AudioWorkletGlobalScope*>(thread->globalScope()); |
| + ScriptState* scriptState = |
| + globalScope->scriptController()->getScriptState(); |
| + |
| + ScriptState::Scope scope(scriptState); |
| + |
| + globalScope->scriptController()->evaluate(ScriptSourceCode( |
| + R"JS( |
| + registerProcessor('testProcessor', class { |
| + constructor () { |
| + this.constant = 1; |
| + } |
| + process (input, output) { |
| + let inputChannelData = input.getChannelData(0); |
| + let outputChannelData = output.getChannelData(0); |
| + for (let i = 0; i < input.length; ++i) { |
| + outputChannelData[i] = inputChannelData[i] + this.constant; |
| + } |
| + } |
| + } |
| + ) |
| + )JS")); |
| + |
| + AudioWorkletProcessor* processor = |
| + globalScope->createInstance("testProcessor"); |
| + EXPECT_TRUE(processor); |
| + |
| + AudioBuffer* inputBuffer = |
| + AudioBuffer::create(1, kRenderQuantumFrames, kTestingSampleRate); |
| + AudioBuffer* outputBuffer = |
| + AudioBuffer::create(1, kRenderQuantumFrames, kTestingSampleRate); |
| + DOMFloat32Array* inputChannelData = inputBuffer->getChannelData(0); |
| + float* inputArrayData = inputChannelData->data(); |
| + EXPECT_TRUE(inputArrayData); |
| + DOMFloat32Array* outputChannelData = outputBuffer->getChannelData(0); |
| + float* outputArrayData = outputChannelData->data(); |
| + EXPECT_TRUE(outputArrayData); |
| + |
| + // Fill |inputBuffer| with 1 and zero out |outputBuffer|. |
| + std::fill(inputArrayData, inputArrayData + inputBuffer->length(), 1); |
| + outputBuffer->zero(); |
| + |
| + // Then invoke the process() method to perform JS buffer manipulation. The |
| + // output buffer should contain a constant value of 2. |
| + processor->process(inputBuffer, outputBuffer); |
| + for (unsigned i = 0; i < outputBuffer->length(); ++i) { |
| + EXPECT_EQ(outputArrayData[i], 2); |
| + } |
| + |
| + waitEvent->signal(); |
| + } |
| + |
| + RefPtr<SecurityOrigin> m_securityOrigin; |
| + std::unique_ptr<WorkerReportingProxy> m_reportingProxy; |
| +}; |
| + |
| +TEST_F(AudioWorkletGlobalScopeTest, Basic) { |
| + std::unique_ptr<AudioWorkletThread> thread = createAudioWorkletThread(); |
| + runBasicTest(thread.get()); |
| + thread->terminateAndWait(); |
| +} |
| + |
| +TEST_F(AudioWorkletGlobalScopeTest, Parsing) { |
| + std::unique_ptr<AudioWorkletThread> thread = createAudioWorkletThread(); |
| + runParsingTest(thread.get()); |
| + thread->terminateAndWait(); |
| +} |
| + |
| +TEST_F(AudioWorkletGlobalScopeTest, BufferProcessing) { |
| + std::unique_ptr<AudioWorkletThread> thread = createAudioWorkletThread(); |
| + runSimpleProcessTest(thread.get()); |
| + thread->terminateAndWait(); |
| +} |
| + |
| +} // namespace blink |