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
3 // found in the LICENSE file.
5 #include "base/logging.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "media/base/mock_ffmpeg.h"
8 #include "media/base/mock_filters.h"
9 #include "media/ffmpeg/ffmpeg_common.h"
10 #include "media/filters/ffmpeg_glue.h"
11 #include "testing/gtest/include/gtest/gtest.h"
14 using ::testing::DoAll
;
15 using ::testing::InSequence
;
16 using ::testing::Return
;
17 using ::testing::SetArgumentPointee
;
18 using ::testing::StrictMock
;
22 class MockProtocol
: public FFmpegURLProtocol
{
27 MOCK_METHOD2(Read
, int(int size
, uint8
* data
));
28 MOCK_METHOD1(GetPosition
, bool(int64
* position_out
));
29 MOCK_METHOD1(SetPosition
, bool(int64 position
));
30 MOCK_METHOD1(GetSize
, bool(int64
* size_out
));
31 MOCK_METHOD0(IsStreaming
, bool());
34 DISALLOW_COPY_AND_ASSIGN(MockProtocol
);
37 class FFmpegGlueTest
: public ::testing::Test
{
41 virtual void SetUp() {
42 // Singleton should initialize FFmpeg.
43 CHECK(FFmpegGlue::GetInstance());
45 // Assign our static copy of URLProtocol for the rest of the tests.
46 protocol_
= MockFFmpeg::protocol();
50 // Helper to open a URLContext pointing to the given mocked protocol.
51 // Callers are expected to close the context at the end of their test.
52 virtual void OpenContext(MockProtocol
* protocol
, URLContext
* context
) {
53 // IsStreaming() is called when opening.
54 EXPECT_CALL(*protocol
, IsStreaming()).WillOnce(Return(true));
56 // Add the protocol to the glue layer and open a context.
57 std::string key
= FFmpegGlue::GetInstance()->AddProtocol(protocol
);
58 memset(context
, 0, sizeof(*context
));
59 EXPECT_EQ(0, protocol_
->url_open(context
, key
.c_str(), 0));
60 FFmpegGlue::GetInstance()->RemoveProtocol(protocol
);
65 MockFFmpeg mock_ffmpeg_
;
66 static URLProtocol
* protocol_
;
69 DISALLOW_COPY_AND_ASSIGN(FFmpegGlueTest
);
72 URLProtocol
* FFmpegGlueTest::protocol_
= NULL
;
74 TEST_F(FFmpegGlueTest
, InitializeFFmpeg
) {
75 // Make sure URLProtocol was filled out correctly.
76 EXPECT_STREQ("http", protocol_
->name
);
77 EXPECT_TRUE(protocol_
->url_close
);
78 EXPECT_TRUE(protocol_
->url_open
);
79 EXPECT_TRUE(protocol_
->url_read
);
80 EXPECT_TRUE(protocol_
->url_seek
);
81 EXPECT_TRUE(protocol_
->url_write
);
84 TEST_F(FFmpegGlueTest
, AddRemoveGetProtocol
) {
85 // Prepare testing data.
86 FFmpegGlue
* glue
= FFmpegGlue::GetInstance();
88 // Create our protocols and add them to the glue layer.
89 scoped_ptr
<StrictMock
<Destroyable
<MockProtocol
> > > protocol_a(
90 new StrictMock
<Destroyable
<MockProtocol
> >());
91 scoped_ptr
<StrictMock
<Destroyable
<MockProtocol
> > > protocol_b(
92 new StrictMock
<Destroyable
<MockProtocol
> >());
94 // Make sure the keys are unique.
95 std::string key_a
= glue
->AddProtocol(protocol_a
.get());
96 std::string key_b
= glue
->AddProtocol(protocol_b
.get());
97 EXPECT_EQ(0u, key_a
.find("http://"));
98 EXPECT_EQ(0u, key_b
.find("http://"));
99 EXPECT_NE(key_a
, key_b
);
101 // Our keys should return our protocols.
102 FFmpegURLProtocol
* protocol_c
;
103 FFmpegURLProtocol
* protocol_d
;
104 glue
->GetProtocol(key_a
, &protocol_c
);
105 glue
->GetProtocol(key_b
, &protocol_d
);
106 EXPECT_EQ(protocol_a
.get(), protocol_c
);
107 EXPECT_EQ(protocol_b
.get(), protocol_d
);
109 // Adding the same Protocol should create the same key and not add an extra
111 std::string key_a2
= glue
->AddProtocol(protocol_a
.get());
112 EXPECT_EQ(key_a
, key_a2
);
113 glue
->GetProtocol(key_a2
, &protocol_c
);
114 EXPECT_EQ(protocol_a
.get(), protocol_c
);
116 // Removes the protocols then releases our references. They should be
119 EXPECT_CALL(*protocol_a
, OnDestroy());
120 EXPECT_CALL(*protocol_b
, OnDestroy());
121 EXPECT_CALL(mock_ffmpeg_
, CheckPoint(0));
123 glue
->RemoveProtocol(protocol_a
.get());
124 glue
->GetProtocol(key_a
, &protocol_c
);
125 EXPECT_FALSE(protocol_c
);
126 glue
->GetProtocol(key_b
, &protocol_d
);
127 EXPECT_EQ(protocol_b
.get(), protocol_d
);
128 glue
->RemoveProtocol(protocol_b
.get());
129 glue
->GetProtocol(key_b
, &protocol_d
);
130 EXPECT_FALSE(protocol_d
);
134 // Data sources should be deleted by this point.
135 mock_ffmpeg_
.CheckPoint(0);
138 TEST_F(FFmpegGlueTest
, OpenClose
) {
139 // Prepare testing data.
140 FFmpegGlue
* glue
= FFmpegGlue::GetInstance();
142 // Create our protocol and add them to the glue layer.
143 scoped_ptr
<StrictMock
<Destroyable
<MockProtocol
> > > protocol(
144 new StrictMock
<Destroyable
<MockProtocol
> >());
145 EXPECT_CALL(*protocol
, IsStreaming()).WillOnce(Return(true));
146 std::string key
= glue
->AddProtocol(protocol
.get());
148 // Prepare FFmpeg URLContext structure.
150 memset(&context
, 0, sizeof(context
));
152 // Test opening a URLContext with a protocol that doesn't exist.
153 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_open(&context
, "foobar", 0));
155 // Test opening a URLContext with our protocol.
156 EXPECT_EQ(0, protocol_
->url_open(&context
, key
.c_str(), 0));
157 EXPECT_EQ(URL_RDONLY
, context
.flags
);
158 EXPECT_EQ(protocol
.get(), context
.priv_data
);
159 EXPECT_TRUE(context
.is_streamed
);
161 // We're going to remove references one by one until the last reference is
162 // held by FFmpeg. Once we close the URLContext, the protocol should be
165 EXPECT_CALL(mock_ffmpeg_
, CheckPoint(0));
166 EXPECT_CALL(mock_ffmpeg_
, CheckPoint(1));
167 EXPECT_CALL(*protocol
, OnDestroy());
168 EXPECT_CALL(mock_ffmpeg_
, CheckPoint(2));
170 // Remove the protocol from the glue layer, releasing a reference.
171 glue
->RemoveProtocol(protocol
.get());
172 mock_ffmpeg_
.CheckPoint(0);
174 // Remove our own reference -- URLContext should maintain a reference.
175 mock_ffmpeg_
.CheckPoint(1);
178 // Close the URLContext, which should release the final reference.
179 EXPECT_EQ(0, protocol_
->url_close(&context
));
180 mock_ffmpeg_
.CheckPoint(2);
183 TEST_F(FFmpegGlueTest
, Write
) {
184 scoped_ptr
<StrictMock
<MockProtocol
> > protocol(
185 new StrictMock
<MockProtocol
>());
187 OpenContext(protocol
.get(), &context
);
189 const int kBufferSize
= 16;
190 uint8 buffer
[kBufferSize
];
192 // Writing should always fail and never call the protocol.
193 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_write(&context
, NULL
, 0));
194 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_write(&context
, buffer
, 0));
195 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_write(&context
, buffer
, kBufferSize
));
197 // Destroy the protocol.
198 protocol_
->url_close(&context
);
201 TEST_F(FFmpegGlueTest
, Read
) {
202 scoped_ptr
<StrictMock
<MockProtocol
> > protocol(
203 new StrictMock
<MockProtocol
>());
205 OpenContext(protocol
.get(), &context
);
207 const int kBufferSize
= 16;
208 uint8 buffer
[kBufferSize
];
210 // Reads are for the most part straight-through calls to Read().
212 EXPECT_CALL(*protocol
, Read(0, buffer
))
213 .WillOnce(Return(0));
214 EXPECT_CALL(*protocol
, Read(kBufferSize
, buffer
))
215 .WillOnce(Return(kBufferSize
));
216 EXPECT_CALL(*protocol
, Read(kBufferSize
, buffer
))
217 .WillOnce(Return(DataSource::kReadError
));
219 EXPECT_EQ(0, protocol_
->url_read(&context
, buffer
, 0));
220 EXPECT_EQ(kBufferSize
, protocol_
->url_read(&context
, buffer
, kBufferSize
));
221 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_read(&context
, buffer
, kBufferSize
));
223 // Destroy the protocol.
224 protocol_
->url_close(&context
);
227 TEST_F(FFmpegGlueTest
, Seek
) {
228 scoped_ptr
<StrictMock
<MockProtocol
> > protocol(
229 new StrictMock
<MockProtocol
>());
231 OpenContext(protocol
.get(), &context
);
233 // SEEK_SET should be a straight-through call to SetPosition(), which when
234 // successful will return the result from GetPosition().
236 EXPECT_CALL(*protocol
, SetPosition(-16))
237 .WillOnce(Return(false));
239 EXPECT_CALL(*protocol
, SetPosition(16))
240 .WillOnce(Return(true));
241 EXPECT_CALL(*protocol
, GetPosition(_
))
242 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
244 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_seek(&context
, -16, SEEK_SET
));
245 EXPECT_EQ(8, protocol_
->url_seek(&context
, 16, SEEK_SET
));
247 // SEEK_CUR should call GetPosition() first, and if it succeeds add the offset
248 // to the result then call SetPosition()+GetPosition().
249 EXPECT_CALL(*protocol
, GetPosition(_
))
250 .WillOnce(Return(false));
252 EXPECT_CALL(*protocol
, GetPosition(_
))
253 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
254 EXPECT_CALL(*protocol
, SetPosition(16))
255 .WillOnce(Return(false));
257 EXPECT_CALL(*protocol
, GetPosition(_
))
258 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
259 EXPECT_CALL(*protocol
, SetPosition(16))
260 .WillOnce(Return(true));
261 EXPECT_CALL(*protocol
, GetPosition(_
))
262 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
264 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_seek(&context
, 8, SEEK_CUR
));
265 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_seek(&context
, 8, SEEK_CUR
));
266 EXPECT_EQ(16, protocol_
->url_seek(&context
, 8, SEEK_CUR
));
268 // SEEK_END should call GetSize() first, and if it succeeds add the offset
269 // to the result then call SetPosition()+GetPosition().
270 EXPECT_CALL(*protocol
, GetSize(_
))
271 .WillOnce(Return(false));
273 EXPECT_CALL(*protocol
, GetSize(_
))
274 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
275 EXPECT_CALL(*protocol
, SetPosition(8))
276 .WillOnce(Return(false));
278 EXPECT_CALL(*protocol
, GetSize(_
))
279 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
280 EXPECT_CALL(*protocol
, SetPosition(8))
281 .WillOnce(Return(true));
282 EXPECT_CALL(*protocol
, GetPosition(_
))
283 .WillOnce(DoAll(SetArgumentPointee
<0>(8), Return(true)));
285 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_seek(&context
, -8, SEEK_END
));
286 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_seek(&context
, -8, SEEK_END
));
287 EXPECT_EQ(8, protocol_
->url_seek(&context
, -8, SEEK_END
));
289 // AVSEEK_SIZE should be a straight-through call to GetSize().
290 EXPECT_CALL(*protocol
, GetSize(_
))
291 .WillOnce(Return(false));
293 EXPECT_CALL(*protocol
, GetSize(_
))
294 .WillOnce(DoAll(SetArgumentPointee
<0>(16), Return(true)));
296 EXPECT_EQ(AVERROR(EIO
), protocol_
->url_seek(&context
, 0, AVSEEK_SIZE
));
297 EXPECT_EQ(16, protocol_
->url_seek(&context
, 0, AVSEEK_SIZE
));
299 // Destroy the protocol.
300 protocol_
->url_close(&context
);
303 TEST_F(FFmpegGlueTest
, Destroy
) {
304 // Create our protocol and add them to the glue layer.
305 scoped_ptr
<StrictMock
<Destroyable
<MockProtocol
> > > protocol(
306 new StrictMock
<Destroyable
<MockProtocol
> >());
307 std::string key
= FFmpegGlue::GetInstance()->AddProtocol(protocol
.get());
309 // We should expect the protocol to get destroyed when the unit test
312 EXPECT_CALL(mock_ffmpeg_
, CheckPoint(0));
313 EXPECT_CALL(*protocol
, OnDestroy());
315 // Remove our own reference, we shouldn't be destroyed yet.
316 mock_ffmpeg_
.CheckPoint(0);
319 // ~FFmpegGlue() will be called when this unit test finishes execution. By
320 // leaving something inside FFmpegGlue's map we get to test our cleanup code.