OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "base/message_loop.h" |
5 #include "base/process_util.h" | 6 #include "base/process_util.h" |
| 7 #include "base/synchronization/waitable_event.h" |
| 8 #include "base/test/test_timeouts.h" |
| 9 #include "base/time.h" |
| 10 #include "content/common/child_process.h" |
| 11 #include "content/common/child_thread.h" |
6 #include "content/common/media/audio_messages.h" | 12 #include "content/common/media/audio_messages.h" |
7 #include "content/renderer/media/audio_renderer_impl.h" | 13 #include "content/renderer/media/audio_renderer_impl.h" |
| 14 #include "content/renderer/mock_content_renderer_client.h" |
| 15 #include "content/renderer/render_process.h" |
| 16 #include "content/renderer/render_thread.h" |
| 17 #include "ipc/ipc_channel.h" |
8 #include "media/base/data_buffer.h" | 18 #include "media/base/data_buffer.h" |
9 #include "media/base/mock_callback.h" | 19 #include "media/base/mock_callback.h" |
10 #include "media/base/mock_filter_host.h" | 20 #include "media/base/mock_filter_host.h" |
11 #include "media/base/mock_filters.h" | 21 #include "media/base/mock_filters.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
13 | 23 |
14 using ::testing::Return; | 24 using ::testing::Return; |
15 | 25 |
16 // Class we would be tesing. The only difference between it and "real" one | 26 namespace { |
| 27 // This class is a mock of the child process singleton which is needed |
| 28 // to be able to create a RenderThread object. |
| 29 class MockRenderProcess : public RenderProcess { |
| 30 public: |
| 31 MockRenderProcess() {} |
| 32 virtual ~MockRenderProcess() {} |
| 33 |
| 34 // RenderProcess implementation. |
| 35 virtual skia::PlatformCanvas* GetDrawingCanvas(TransportDIB** memory, |
| 36 const gfx::Rect& rect) { return NULL; } |
| 37 virtual void ReleaseTransportDIB(TransportDIB* memory) {} |
| 38 virtual bool UseInProcessPlugins() const { return false; } |
| 39 virtual bool HasInitializedMediaLibrary() const { return false; } |
| 40 |
| 41 private: |
| 42 DISALLOW_COPY_AND_ASSIGN(MockRenderProcess); |
| 43 }; |
| 44 } |
| 45 |
| 46 // This class defines a set of methods which will be used in combination |
| 47 // with NewRunnableMethod to form tasks which will be posted on the |
| 48 // IO thread. All methods emulate AudioMessageFilter::Delegate calls. |
| 49 class DelegateCaller : public base::RefCountedThreadSafe<DelegateCaller> { |
| 50 public: |
| 51 explicit DelegateCaller(AudioRendererImpl* renderer) |
| 52 : renderer_(renderer) {} |
| 53 |
| 54 void OnCreated(base::SharedMemoryHandle handle, uint32 length) { |
| 55 if (renderer_->latency_type() == AudioRendererImpl::kHighLatency) { |
| 56 renderer_->OnCreated(handle, length); |
| 57 } else { |
| 58 renderer_->OnLowLatencyCreated(handle, 0, length); |
| 59 } |
| 60 } |
| 61 void OnStateChanged(AudioStreamState state) { |
| 62 renderer_->OnStateChanged(state); |
| 63 } |
| 64 void OnRequestPacket(AudioBuffersState buffers_state) { |
| 65 renderer_->OnRequestPacket(buffers_state); |
| 66 } |
| 67 void OnVolume(double volume) { |
| 68 renderer_->OnVolume(volume); |
| 69 } |
| 70 void DestroyCurrentMessageLoop() { |
| 71 renderer_->WillDestroyCurrentMessageLoop(); |
| 72 } |
| 73 private: |
| 74 friend class base::RefCountedThreadSafe<DelegateCaller>; |
| 75 virtual ~DelegateCaller() {} |
| 76 |
| 77 scoped_refptr<AudioRendererImpl> renderer_; |
| 78 DISALLOW_COPY_AND_ASSIGN(DelegateCaller); |
| 79 }; |
| 80 |
| 81 // This task can be posted on the IO thread and will signal an event when |
| 82 // done. The caller can then wait for this signal to ensure that no |
| 83 // additional tasks remain in the task queue. |
| 84 class WaitTask : public Task { |
| 85 public: |
| 86 explicit WaitTask(base::WaitableEvent* event) |
| 87 : event_(event) {} |
| 88 virtual ~WaitTask() {} |
| 89 virtual void Run() { |
| 90 event_->Signal(); |
| 91 } |
| 92 |
| 93 private: |
| 94 base::WaitableEvent* event_; |
| 95 DISALLOW_COPY_AND_ASSIGN(WaitTask); |
| 96 }; |
| 97 |
| 98 // Class we would be testing. The only difference between it and "real" one |
17 // is that test class does not open sockets and launch audio thread. | 99 // is that test class does not open sockets and launch audio thread. |
18 class TestAudioRendererImpl : public AudioRendererImpl { | 100 class TestAudioRendererImpl : public AudioRendererImpl { |
19 public: | 101 public: |
20 explicit TestAudioRendererImpl(AudioMessageFilter* filter) | 102 explicit TestAudioRendererImpl() |
21 : AudioRendererImpl(filter) { | 103 : AudioRendererImpl() { |
22 } | 104 } |
23 private: | 105 private: |
24 virtual void CreateSocket(base::SyncSocket::Handle socket_handle) {} | 106 virtual void CreateSocket(base::SyncSocket::Handle socket_handle) {} |
25 virtual void CreateAudioThread() {} | 107 virtual void CreateAudioThread() {} |
26 }; | 108 }; |
27 | 109 |
28 class AudioRendererImplTest : public ::testing::Test { | 110 class AudioRendererImplTest |
| 111 : public ::testing::Test, |
| 112 public IPC::Channel::Listener { |
29 public: | 113 public: |
30 static const int kRouteId = 0; | |
31 static const int kSize = 1024; | |
32 | |
33 static void SetUpTestCase() { | 114 static void SetUpTestCase() { |
34 // Set low latency mode, as it soon would be on by default. | 115 // Set low latency mode, as it soon would be on by default. |
35 AudioRendererImpl::set_latency_type(AudioRendererImpl::kLowLatency); | 116 AudioRendererImpl::set_latency_type(AudioRendererImpl::kLowLatency); |
36 } | 117 } |
37 | 118 |
38 AudioRendererImplTest() { | 119 // IPC::Channel::Listener implementation. |
39 message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); | 120 virtual bool OnMessageReceived(const IPC::Message& message) { |
| 121 NOTIMPLEMENTED(); |
| 122 return true; |
| 123 } |
40 | 124 |
41 // TODO(scherkus): use gmock with AudioMessageFilter to verify | 125 static const int kSize; |
42 // AudioRendererImpl calls or doesn't call Send(). | 126 |
43 filter_ = new AudioMessageFilter(kRouteId); | 127 AudioRendererImplTest() {} |
44 filter_->message_loop_ = message_loop_.get(); | 128 virtual ~AudioRendererImplTest() {} |
| 129 |
| 130 virtual void SetUp() { |
| 131 // This part sets up a RenderThread environment to ensure that |
| 132 // RenderThread::current() (<=> TLS pointer) is valid. |
| 133 // Main parts are inspired by the RenderViewFakeResourcesTest. |
| 134 // Note that, the IPC part is not utilized in this test. |
| 135 content::GetContentClient()->set_renderer(&mock_content_renderer_client_); |
| 136 |
| 137 static const char kThreadName[] = "RenderThread"; |
| 138 channel_.reset(new IPC::Channel(kThreadName, |
| 139 IPC::Channel::MODE_SERVER, this)); |
| 140 ASSERT_TRUE(channel_->Connect()); |
| 141 |
| 142 mock_process_.reset(new MockRenderProcess); |
| 143 render_thread_ = new RenderThread(kThreadName); |
| 144 mock_process_->set_main_thread(render_thread_); |
45 | 145 |
46 // Create temporary shared memory. | 146 // Create temporary shared memory. |
47 CHECK(shared_mem_.CreateAnonymous(kSize)); | 147 CHECK(shared_mem_.CreateAnonymous(kSize)); |
48 | 148 |
49 // Setup expectations for initialization. | 149 // Setup expectations for initialization. |
50 decoder_ = new media::MockAudioDecoder(); | 150 decoder_ = new media::MockAudioDecoder(); |
51 | 151 |
52 ON_CALL(*decoder_, config()) | 152 ON_CALL(*decoder_, config()) |
53 .WillByDefault(Return(media::AudioDecoderConfig(16, | 153 .WillByDefault(Return(media::AudioDecoderConfig(16, |
54 CHANNEL_LAYOUT_MONO, | 154 CHANNEL_LAYOUT_MONO, |
55 44100))); | 155 44100))); |
56 | 156 |
57 // Create and initialize audio renderer. | 157 // Create and initialize the audio renderer. |
58 renderer_ = new TestAudioRendererImpl(filter_); | 158 renderer_ = new TestAudioRendererImpl(); |
59 renderer_->set_host(&host_); | 159 renderer_->set_host(&host_); |
60 renderer_->Initialize(decoder_, media::NewExpectedCallback()); | 160 renderer_->Initialize(decoder_, media::NewExpectedCallback()); |
61 | 161 |
62 // Run pending tasks and simulate responding with a created audio stream. | 162 // Wraps delegate calls into tasks. |
63 message_loop_->RunAllPending(); | 163 delegate_caller_ = new DelegateCaller(renderer_); |
| 164 |
| 165 // We need an event to verify that all tasks are done before leaving |
| 166 // our tests. |
| 167 event_.reset(new base::WaitableEvent(false, false)); |
64 | 168 |
65 // Duplicate the shared memory handle so both the test and the callee can | 169 // Duplicate the shared memory handle so both the test and the callee can |
66 // close their copy. | 170 // close their copy. |
67 base::SharedMemoryHandle duplicated_handle; | 171 base::SharedMemoryHandle duplicated_handle; |
68 EXPECT_TRUE(shared_mem_.ShareToProcess(base::GetCurrentProcessHandle(), | 172 EXPECT_TRUE(shared_mem_.ShareToProcess(base::GetCurrentProcessHandle(), |
69 &duplicated_handle)); | 173 &duplicated_handle)); |
70 CallOnCreated(duplicated_handle); | 174 |
| 175 // Set things up and ensure that the call comes from the IO thread |
| 176 // as all AudioMessageFilter::Delegate methods. |
| 177 ChildProcess::current()->io_message_loop()->PostTask( |
| 178 FROM_HERE, |
| 179 NewRunnableMethod(delegate_caller_.get(), |
| 180 &DelegateCaller::OnCreated, duplicated_handle, kSize)); |
| 181 WaitForIOThreadCompletion(); |
71 } | 182 } |
72 | 183 |
73 virtual ~AudioRendererImplTest() { | 184 virtual void TearDown() { |
74 } | 185 mock_process_.reset(); |
75 | |
76 void CallOnCreated(base::SharedMemoryHandle handle) { | |
77 if (renderer_->latency_type() == AudioRendererImpl::kHighLatency) { | |
78 renderer_->OnCreated(handle, kSize); | |
79 } else { | |
80 renderer_->OnLowLatencyCreated(handle, 0, kSize); | |
81 } | |
82 } | 186 } |
83 | 187 |
84 protected: | 188 protected: |
85 // Fixtures. | 189 // Posts a final task to the IO message loop and waits for completion. |
86 scoped_ptr<MessageLoop> message_loop_; | 190 void WaitForIOThreadCompletion() { |
87 scoped_refptr<AudioMessageFilter> filter_; | 191 ChildProcess::current()->io_message_loop()->PostTask( |
| 192 FROM_HERE, new WaitTask(event_.get())); |
| 193 EXPECT_TRUE(event_->TimedWait( |
| 194 base::TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms()))); |
| 195 } |
| 196 |
| 197 MessageLoopForIO message_loop_; |
| 198 content::MockContentRendererClient mock_content_renderer_client_; |
| 199 scoped_ptr<IPC::Channel> channel_; |
| 200 RenderThread* render_thread_; // owned by mock_process_ |
| 201 scoped_ptr<MockRenderProcess> mock_process_; |
88 base::SharedMemory shared_mem_; | 202 base::SharedMemory shared_mem_; |
89 media::MockFilterHost host_; | 203 media::MockFilterHost host_; |
90 scoped_refptr<media::MockAudioDecoder> decoder_; | 204 scoped_refptr<media::MockAudioDecoder> decoder_; |
91 scoped_refptr<AudioRendererImpl> renderer_; | 205 scoped_refptr<AudioRendererImpl> renderer_; |
| 206 scoped_ptr<base::WaitableEvent> event_; |
| 207 scoped_refptr<DelegateCaller> delegate_caller_; |
92 | 208 |
93 private: | 209 private: |
94 DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); | 210 DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); |
95 }; | 211 }; |
96 | 212 |
| 213 const int AudioRendererImplTest::kSize = 1024; |
| 214 |
97 TEST_F(AudioRendererImplTest, SetPlaybackRate) { | 215 TEST_F(AudioRendererImplTest, SetPlaybackRate) { |
98 // Execute SetPlaybackRate() codepath to create an IPC message. | 216 // Execute SetPlaybackRate() codepath by toggling play/pause. |
99 | 217 // These methods will be called on the pipeline thread but calling from |
100 // Toggle play/pause to generate some IPC messages. | 218 // here is fine for this test. Tasks will be posted internally on |
| 219 // the IO thread. |
101 renderer_->SetPlaybackRate(0.0f); | 220 renderer_->SetPlaybackRate(0.0f); |
102 renderer_->SetPlaybackRate(1.0f); | 221 renderer_->SetPlaybackRate(1.0f); |
103 renderer_->SetPlaybackRate(0.0f); | 222 renderer_->SetPlaybackRate(0.0f); |
104 | 223 |
105 renderer_->Stop(media::NewExpectedCallback()); | 224 renderer_->Stop(media::NewExpectedCallback()); |
106 message_loop_->RunAllPending(); | 225 WaitForIOThreadCompletion(); |
107 } | 226 } |
108 | 227 |
109 TEST_F(AudioRendererImplTest, SetVolume) { | 228 TEST_F(AudioRendererImplTest, SetVolume) { |
110 // Execute SetVolume() codepath to create an IPC message. | 229 // Execute SetVolume() codepath. |
| 230 // This method will be called on the pipeline thread IRL. |
| 231 // Tasks will be posted internally on the IO thread. |
111 renderer_->SetVolume(0.5f); | 232 renderer_->SetVolume(0.5f); |
| 233 |
112 renderer_->Stop(media::NewExpectedCallback()); | 234 renderer_->Stop(media::NewExpectedCallback()); |
113 message_loop_->RunAllPending(); | 235 WaitForIOThreadCompletion(); |
114 } | 236 } |
115 | 237 |
116 TEST_F(AudioRendererImplTest, Stop) { | 238 TEST_F(AudioRendererImplTest, Stop) { |
117 // Execute Stop() codepath to create an IPC message. | 239 // Execute Stop() codepath. |
| 240 // Tasks will be posted internally on the IO thread. |
118 renderer_->Stop(media::NewExpectedCallback()); | 241 renderer_->Stop(media::NewExpectedCallback()); |
119 message_loop_->RunAllPending(); | |
120 | 242 |
121 // Run AudioMessageFilter::Delegate methods, which can be executed after being | 243 // Run AudioMessageFilter::Delegate methods, which can be executed after being |
122 // stopped. AudioRendererImpl shouldn't create any messages. | 244 // stopped. AudioRendererImpl shouldn't create any messages in this state. |
| 245 // All delegate method calls are posted on the IO thread since it is |
| 246 // a requirement. |
123 if (renderer_->latency_type() == AudioRendererImpl::kHighLatency) { | 247 if (renderer_->latency_type() == AudioRendererImpl::kHighLatency) { |
124 renderer_->OnRequestPacket(AudioBuffersState(kSize, 0)); | 248 ChildProcess::current()->io_message_loop()->PostTask( |
| 249 FROM_HERE, |
| 250 NewRunnableMethod(delegate_caller_.get(), |
| 251 &DelegateCaller::OnRequestPacket, AudioBuffersState(kSize, 0))); |
125 } | 252 } |
126 renderer_->OnStateChanged(kAudioStreamError); | 253 ChildProcess::current()->io_message_loop()->PostTask( |
127 renderer_->OnStateChanged(kAudioStreamPlaying); | 254 FROM_HERE, |
128 renderer_->OnStateChanged(kAudioStreamPaused); | 255 NewRunnableMethod(delegate_caller_.get(), |
129 CallOnCreated(shared_mem_.handle()); | 256 &DelegateCaller::OnStateChanged, kAudioStreamError)); |
130 renderer_->OnVolume(0.5); | 257 ChildProcess::current()->io_message_loop()->PostTask( |
| 258 FROM_HERE, |
| 259 NewRunnableMethod(delegate_caller_.get(), |
| 260 &DelegateCaller::OnStateChanged, kAudioStreamPlaying)); |
| 261 ChildProcess::current()->io_message_loop()->PostTask( |
| 262 FROM_HERE, |
| 263 NewRunnableMethod(delegate_caller_.get(), |
| 264 &DelegateCaller::OnStateChanged, kAudioStreamPaused)); |
| 265 ChildProcess::current()->io_message_loop()->PostTask( |
| 266 FROM_HERE, |
| 267 NewRunnableMethod(delegate_caller_.get(), |
| 268 &DelegateCaller::OnCreated, shared_mem_.handle(), kSize)); |
| 269 ChildProcess::current()->io_message_loop()->PostTask( |
| 270 FROM_HERE, |
| 271 NewRunnableMethod(delegate_caller_.get(), |
| 272 &DelegateCaller::OnVolume, 0.5)); |
| 273 |
| 274 WaitForIOThreadCompletion(); |
131 | 275 |
132 // It's possible that the upstream decoder replies right after being stopped. | 276 // It's possible that the upstream decoder replies right after being stopped. |
133 scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); | 277 scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); |
134 renderer_->ConsumeAudioSamples(buffer); | 278 renderer_->ConsumeAudioSamples(buffer); |
135 } | 279 } |
136 | 280 |
137 TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetPlaybackRate) { | 281 TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetPlaybackRate) { |
138 // Kill the message loop and verify SetPlaybackRate() still works. | 282 // Emulate "killing the message loop" and verify that SetPlaybackRate() |
139 message_loop_.reset(); | 283 // still works. |
| 284 ChildProcess::current()->io_message_loop()->PostTask( |
| 285 FROM_HERE, |
| 286 NewRunnableMethod(delegate_caller_.get(), |
| 287 &DelegateCaller::DestroyCurrentMessageLoop)); |
| 288 WaitForIOThreadCompletion(); |
| 289 |
| 290 // No tasks will be posted on the IO thread here since we are in |
| 291 // a "stopped" state. |
140 renderer_->SetPlaybackRate(0.0f); | 292 renderer_->SetPlaybackRate(0.0f); |
141 renderer_->SetPlaybackRate(1.0f); | 293 renderer_->SetPlaybackRate(1.0f); |
142 renderer_->SetPlaybackRate(0.0f); | 294 renderer_->SetPlaybackRate(0.0f); |
143 renderer_->Stop(media::NewExpectedCallback()); | 295 renderer_->Stop(media::NewExpectedCallback()); |
144 } | 296 } |
145 | 297 |
146 TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetVolume) { | 298 TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetVolume) { |
147 // Kill the message loop and verify SetVolume() still works. | 299 // Emulate "killing the message loop" and verify that SetVolume() |
148 message_loop_.reset(); | 300 // still works. |
| 301 ChildProcess::current()->io_message_loop()->PostTask( |
| 302 FROM_HERE, |
| 303 NewRunnableMethod(delegate_caller_.get(), |
| 304 &DelegateCaller::DestroyCurrentMessageLoop)); |
| 305 WaitForIOThreadCompletion(); |
| 306 |
| 307 // No tasks will be posted on the IO thread here since we are in |
| 308 // a "stopped" state. |
149 renderer_->SetVolume(0.5f); | 309 renderer_->SetVolume(0.5f); |
150 renderer_->Stop(media::NewExpectedCallback()); | 310 renderer_->Stop(media::NewExpectedCallback()); |
151 } | 311 } |
152 | 312 |
153 TEST_F(AudioRendererImplTest, DestroyedMessageLoop_ConsumeAudioSamples) { | 313 TEST_F(AudioRendererImplTest, DestroyedMessageLoop_ConsumeAudioSamples) { |
154 // Kill the message loop and verify OnReadComplete() still works. | 314 // Emulate "killing the message loop" and verify that OnReadComplete() |
155 message_loop_.reset(); | 315 // still works. |
| 316 ChildProcess::current()->io_message_loop()->PostTask( |
| 317 FROM_HERE, |
| 318 NewRunnableMethod(delegate_caller_.get(), |
| 319 &DelegateCaller::DestroyCurrentMessageLoop)); |
| 320 WaitForIOThreadCompletion(); |
| 321 |
| 322 // No tasks will be posted on the IO thread here since we are in |
| 323 // a "stopped" state. |
156 scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); | 324 scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); |
157 renderer_->ConsumeAudioSamples(buffer); | 325 renderer_->ConsumeAudioSamples(buffer); |
158 renderer_->Stop(media::NewExpectedCallback()); | 326 renderer_->Stop(media::NewExpectedCallback()); |
159 } | 327 } |
OLD | NEW |