/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/interface/module.h" #include "webrtc/modules/utility/source/process_thread_impl.h" #include "webrtc/system_wrappers/include/tick_util.h" namespace webrtc { using ::testing::_; using ::testing::DoAll; using ::testing::InSequence; using ::testing::Invoke; using ::testing::Return; using ::testing::SetArgPointee; class MockModule : public Module { public: MOCK_METHOD0(TimeUntilNextProcess, int64_t()); MOCK_METHOD0(Process, int32_t()); MOCK_METHOD1(ProcessThreadAttached, void(ProcessThread*)); }; class RaiseEventTask : public ProcessTask { public: RaiseEventTask(EventWrapper* event) : event_(event) {} void Run() override { event_->Set(); } private: EventWrapper* event_; }; ACTION_P(SetEvent, event) { event->Set(); } ACTION_P(Increment, counter) { ++(*counter); } ACTION_P(SetTimestamp, ptr) { *ptr = TickTime::MillisecondTimestamp(); } TEST(ProcessThreadImpl, StartStop) { ProcessThreadImpl thread("ProcessThread"); thread.Start(); thread.Stop(); } TEST(ProcessThreadImpl, MultipleStartStop) { ProcessThreadImpl thread("ProcessThread"); for (int i = 0; i < 5; ++i) { thread.Start(); thread.Stop(); } } // Verifies that we get at least call back to Process() on the worker thread. TEST(ProcessThreadImpl, ProcessCall) { ProcessThreadImpl thread("ProcessThread"); thread.Start(); rtc::scoped_ptr event(EventWrapper::Create()); MockModule module; EXPECT_CALL(module, TimeUntilNextProcess()).WillRepeatedly(Return(0)); EXPECT_CALL(module, Process()) .WillOnce(DoAll(SetEvent(event.get()), Return(0))) .WillRepeatedly(Return(0)); EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); thread.RegisterModule(&module); EXPECT_EQ(kEventSignaled, event->Wait(100)); EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); thread.Stop(); } // Same as ProcessCall except the module is registered before the // call to Start(). TEST(ProcessThreadImpl, ProcessCall2) { ProcessThreadImpl thread("ProcessThread"); rtc::scoped_ptr event(EventWrapper::Create()); MockModule module; EXPECT_CALL(module, TimeUntilNextProcess()).WillRepeatedly(Return(0)); EXPECT_CALL(module, Process()) .WillOnce(DoAll(SetEvent(event.get()), Return(0))) .WillRepeatedly(Return(0)); thread.RegisterModule(&module); EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); thread.Start(); EXPECT_EQ(kEventSignaled, event->Wait(100)); EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); thread.Stop(); } // Tests setting up a module for callbacks and then unregister that module. // After unregistration, we should not receive any further callbacks. TEST(ProcessThreadImpl, Deregister) { ProcessThreadImpl thread("ProcessThread"); rtc::scoped_ptr event(EventWrapper::Create()); int process_count = 0; MockModule module; EXPECT_CALL(module, TimeUntilNextProcess()).WillRepeatedly(Return(0)); EXPECT_CALL(module, Process()) .WillOnce(DoAll(SetEvent(event.get()), Increment(&process_count), Return(0))) .WillRepeatedly(DoAll(Increment(&process_count), Return(0))); thread.RegisterModule(&module); EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); thread.Start(); EXPECT_EQ(kEventSignaled, event->Wait(100)); EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); thread.DeRegisterModule(&module); EXPECT_GE(process_count, 1); int count_after_deregister = process_count; // We shouldn't get any more callbacks. EXPECT_EQ(kEventTimeout, event->Wait(20)); EXPECT_EQ(count_after_deregister, process_count); thread.Stop(); } // Helper function for testing receiving a callback after a certain amount of // time. There's some variance of timing built into it to reduce chance of // flakiness on bots. void ProcessCallAfterAFewMs(int64_t milliseconds) { ProcessThreadImpl thread("ProcessThread"); thread.Start(); rtc::scoped_ptr event(EventWrapper::Create()); MockModule module; int64_t start_time = 0; int64_t called_time = 0; EXPECT_CALL(module, TimeUntilNextProcess()) .WillOnce(DoAll(SetTimestamp(&start_time), Return(milliseconds))) .WillRepeatedly(Return(milliseconds)); EXPECT_CALL(module, Process()) .WillOnce(DoAll(SetTimestamp(&called_time), SetEvent(event.get()), Return(0))) .WillRepeatedly(Return(0)); EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); thread.RegisterModule(&module); // Add a buffer of 50ms due to slowness of some trybots // (e.g. win_drmemory_light) EXPECT_EQ(kEventSignaled, event->Wait(milliseconds + 50)); EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); thread.Stop(); ASSERT_GT(start_time, 0); ASSERT_GT(called_time, 0); // Use >= instead of > since due to rounding and timer accuracy (or lack // thereof), can make the test run in "0"ms time. EXPECT_GE(called_time, start_time); // Check for an acceptable range. uint32_t diff = called_time - start_time; EXPECT_GE(diff, milliseconds - 15); EXPECT_LT(diff, milliseconds + 15); } // DISABLED for now since the virtual build bots are too slow :( // TODO(tommi): Fix. TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter5ms) { ProcessCallAfterAFewMs(5); } // DISABLED for now since the virtual build bots are too slow :( // TODO(tommi): Fix. TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter50ms) { ProcessCallAfterAFewMs(50); } // DISABLED for now since the virtual build bots are too slow :( // TODO(tommi): Fix. TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter200ms) { ProcessCallAfterAFewMs(200); } // Runs callbacks with the goal of getting up to 50 callbacks within a second // (on average 1 callback every 20ms). On real hardware, we're usually pretty // close to that, but the test bots that run on virtual machines, will // typically be in the range 30-40 callbacks. // DISABLED for now since this can take up to 2 seconds to run on the slowest // build bots. // TODO(tommi): Fix. TEST(ProcessThreadImpl, DISABLED_Process50Times) { ProcessThreadImpl thread("ProcessThread"); thread.Start(); rtc::scoped_ptr event(EventWrapper::Create()); MockModule module; int callback_count = 0; // Ask for a callback after 20ms. EXPECT_CALL(module, TimeUntilNextProcess()) .WillRepeatedly(Return(20)); EXPECT_CALL(module, Process()) .WillRepeatedly(DoAll(Increment(&callback_count), Return(0))); EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); thread.RegisterModule(&module); EXPECT_EQ(kEventTimeout, event->Wait(1000)); EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); thread.Stop(); printf("Callback count: %i\n", callback_count); // Check that we got called back up to 50 times. // Some of the try bots run on slow virtual machines, so the lower bound // is much more relaxed to avoid flakiness. EXPECT_GE(callback_count, 25); EXPECT_LE(callback_count, 50); } // Tests that we can wake up the worker thread to give us a callback right // away when we know the thread is sleeping. TEST(ProcessThreadImpl, WakeUp) { ProcessThreadImpl thread("ProcessThread"); thread.Start(); rtc::scoped_ptr started(EventWrapper::Create()); rtc::scoped_ptr called(EventWrapper::Create()); MockModule module; int64_t start_time = 0; int64_t called_time = 0; // Ask for a callback after 1000ms. // TimeUntilNextProcess will be called twice. // The first time we use it to get the thread into a waiting state. // Then we wake the thread and there should not be another call made to // TimeUntilNextProcess before Process() is called. // The second time TimeUntilNextProcess is then called, is after Process // has been called and we don't expect any more calls. EXPECT_CALL(module, TimeUntilNextProcess()) .WillOnce(DoAll(SetTimestamp(&start_time), SetEvent(started.get()), Return(1000))) .WillOnce(Return(1000)); EXPECT_CALL(module, Process()) .WillOnce(DoAll(SetTimestamp(&called_time), SetEvent(called.get()), Return(0))) .WillRepeatedly(Return(0)); EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1); thread.RegisterModule(&module); EXPECT_EQ(kEventSignaled, started->Wait(100)); thread.WakeUp(&module); EXPECT_EQ(kEventSignaled, called->Wait(100)); EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1); thread.Stop(); ASSERT_GT(start_time, 0); ASSERT_GT(called_time, 0); EXPECT_GE(called_time, start_time); uint32_t diff = called_time - start_time; // We should have been called back much quicker than 1sec. EXPECT_LE(diff, 100u); } // Tests that we can post a task that gets run straight away on the worker // thread. TEST(ProcessThreadImpl, PostTask) { ProcessThreadImpl thread("ProcessThread"); rtc::scoped_ptr task_ran(EventWrapper::Create()); rtc::scoped_ptr task(new RaiseEventTask(task_ran.get())); thread.Start(); thread.PostTask(task.Pass()); EXPECT_EQ(kEventSignaled, task_ran->Wait(100)); thread.Stop(); } } // namespace webrtc