先从VideoCaptureInputTest分析VideoCaptureInput的用法,后续再分析具体实现。
VideoCaptureInputTest的实现,依赖google的gmock/gtest单元测试框架:
gmock:
http://code.google.com/p/googlemock/wiki/CheatSheet
http://blog.csdn.net/russell_tao/article/details/7344739
http://www.2cto.com/kf/201406/307020.html
MOCK_METHOD#1(#2, #3(#4) )
#1表示被mock的函数参数个数,#2表示被mock的函数名称,#3表示被mock的函数返回值,#4表示被mock的函数参数列表<span style="font-family:Arial;">ON_CALL(#1, #2(#3)).WillByDefault(Return(#4));</span>
#1表示mock对象。定义了一个Mock类,那么就必须生成相应的mock对象#2表示想定义的那个方法名称。
#3表示#2方法的参数,只有调用#2方法,同时传递参数为#3时,才会用到ON_CALL的定义。
#4表示调用#2方法,同时传递参数为#3时,返回#4这个变量的值。
<span style="font-family:Arial;">EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);</span>
第一个参数是mock对象,第二个参数是mock函数名及其参数。二者中间是以逗号;宏后面还可以紧跟若干语句,以提供对mock函数调用的预期结果的更多信息,Times表示执行次数,WillOnce代表执行1次,WillRepeatedly代表重复执行,action代表预期行为,如果没有指定,系统将会为其指派默认行为(什么都不做,返回0),并且在屏幕上打印WARNING这种风格的语法被一些人称作是“特定领域语言”(Domain-Specific Language,DSL)。
gtest
https://code.google.com/p/googletest/wiki/V1_7_Primer
http://www.cnblogs.com/coderzh/archive/2009/04/06/1430364.html
ASSERT_* 系列的断言,当检查点失败时,退出当前函数(注意:并非退出当前案例)。EXPECT_* 系列的断言,当检查点失败时,继续往下执行。
TEST_F 多个测试使用同样的配置, 例如不同的测试用例都需要准备VideoFrame
VideoCaptureInputTest的实现:
1.构造函数:
<span style="font-family:Arial;"> VideoCaptureInputTest()
: mock_process_thread_(new NiceMock<MockProcessThread>),
mock_frame_callback_(new NiceMock<MockVideoCaptureCallback>),
output_frame_event_(EventWrapper::Create()),
stats_proxy_(Clock::GetRealTimeClock(),
webrtc::VideoSendStream::Config(nullptr)) {}</span>
其中mock_process_thread_变量mock了ProcessThread对象,而mock_frame_callback_则mock了VideoCaptureCallback对象,目的就是对VideoCaptureInput的输入输出进行打桩,用于测试其处理逻辑,相关的定义如下,都是继承自现有类:<span style="font-family:Arial;">class MockVideoCaptureCallback : public VideoCaptureCallback {
public:
MOCK_METHOD1(DeliverFrame, void(VideoFrame video_frame));
};</span>
<span style="font-family:Arial;">class MockProcessThread : public ProcessThread {
public:
MOCK_METHOD0(Start, void());
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(WakeUp, void(Module* module));
MOCK_METHOD1(PostTask, void(ProcessTask* task));
MOCK_METHOD1(RegisterModule, void(Module* module));
MOCK_METHOD1(DeRegisterModule, void(Module* module));
// MOCK_METHOD1 gets confused with mocking this method, so we work around it
// by overriding the method from the interface and forwarding the call to a
// mocked, simpler method.
void PostTask(rtc::scoped_ptr<ProcessTask> task) override {
PostTask(task.get());
}
};</span>
2.SetUp
<span style="font-family:Arial;"> virtual void SetUp() {
/*方法DeliverFrame可以被mock_frame_callback_调用任意次,且对参数没有要求(参数为"_"),
*当其被调用后,mock_frame_callback_会执行Invoke来调用AddOutputFrame方法
*/
EXPECT_CALL(*mock_frame_callback_, DeliverFrame(_))
.WillRepeatedly(
WithArg<0>(Invoke(this, &VideoCaptureInputTest::AddOutputFrame)));
Config config;
//创建VideoCaptureInput对象,使用mock的对象
input_.reset(new internal::VideoCaptureInput(
mock_process_thread_.get(), mock_frame_callback_.get(), nullptr,
&stats_proxy_, nullptr, nullptr));
}</span>
1)通过EXPECT_CALL制定测试规则,当DeliverFrame(将数据发送给encoder)执行时,调用AddOutputFrame(输出video数据)2)创建VideoCaptureInput对象
frame_callback_(frame_callback),获取video数据3)EncoderThreadFunction创建后,会通过capture_event_->Wait(kThreadWaitTimeMs)等待video数据,
stats_proxy_(stats_proxy),跟新本地发送数据的信息(biterate,frame account,rtcp account .etc)
capture_event_(EventWrapper::Create()),控制encode线程的执行
overuse_detector_,根据发送端对video数据处理时间,来判断系统资源使用情况,
module_process_thread_->RegisterModule(overuse_detector_.get()),监听上述信息
创建EncoderThreadFunction线程并开始,主要作用是将本地Video数据送入编码器,
3.AddInputFrame
调用IncomingCapturedFrame,将video数据输入到VideoCaptureInput模块,通过执行DeliverFrame会将video数据发送到EncodeThread,与此同时4.AddOutputFrame
将encode输出的数据push到output frame队列,同时调用output_frame_event_->Set(),发送输出数据到达的消息5.WaitOutputFrame
EXPECT_EQ(kEventSignaled, output_frame_event_->Wait(FRAME_TIMEOUT_MS));通过output_frame_event_->Waite()等待output_frame,根据返回值是否等于kEventSignaled来判断,数据处理是否正常结束,进行测试
总结
创建mock对象,设定测试规则:当DeliverFrame(将数据发送给encoder)执行时,调用AddOutputFrame(输出video数据),模拟一次数据输入编码输出过程,并通过EXPECT_EQ来判断处理是否正常
6.测试用例分析
基于上述实现可以指定多种测试场景,代码中有多个TEST_F,以DropsFramesWithSameOrOldNtpTimestamp为例:<span style="font-family:Arial;">TEST_F(VideoCaptureInputTest, DropsFramesWithSameOrOldNtpTimestamp) {
input_frames_.push_back(CreateVideoFrame(0));//本地构造一帧图像,放入input_frame队列
input_frames_[0]->set_ntp_time_ms(17);//设置ts
AddInputFrame(input_frames_[0]);//调用AddInputFrame,输入到VideoCaptureInput模块
WaitOutputFrame();//当DeliverFrame执行时,会调用AddOutputFrame,解锁Wait
EXPECT_EQ(output_frames_[0]->timestamp(),//判断timestamp在处理前后是否相等,如不相等,说明处理逻辑有问题
input_frames_[0]->ntp_time_ms() * 90);
// Repeat frame with the same NTP timestamp should drop.
AddInputFrame(input_frames_[0]);//再将上一帧重复输入到VideoCaptureInput
EXPECT_EQ(kEventTimeout, output_frame_event_->Wait(FRAME_TIMEOUT_MS));//此时会返回ERROR
// As should frames with a decreased NTP timestamp.
input_frames_[0]->set_ntp_time_ms(input_frames_[0]->ntp_time_ms() - 1);//将时间减去1,模拟后接收到的帧,ts小于之前收到帧的ts
AddInputFrame(input_frames_[0]);
EXPECT_EQ(kEventTimeout, output_frame_event_->Wait(FRAME_TIMEOUT_MS));//此时也会返回ERROR
// But delivering with an increased NTP timestamp should succeed.
//如果后传入的帧ts大于之前的frame,则处理正常
input_frames_[0]->set_ntp_time_ms(4711);
AddInputFrame(input_frames_[0]);
WaitOutputFrame();
EXPECT_EQ(output_frames_[1]->timestamp(),
input_frames_[0]->ntp_time_ms() * 90);
}</span>