| Index: webrtc/system_wrappers/source/event_timer_posix_unittest.cc
|
| diff --git a/webrtc/system_wrappers/source/event_timer_posix_unittest.cc b/webrtc/system_wrappers/source/event_timer_posix_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5674e539c9e63bbb0a73d659b359bc8406c07e2d
|
| --- /dev/null
|
| +++ b/webrtc/system_wrappers/source/event_timer_posix_unittest.cc
|
| @@ -0,0 +1,176 @@
|
| +/*
|
| + * Copyright (c) 2016 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 "webrtc/system_wrappers/source/event_timer_posix.h"
|
| +
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "webrtc/base/event.h"
|
| +#include "webrtc/base/criticalsection.h"
|
| +
|
| +namespace webrtc {
|
| +
|
| +enum class ThreadState {
|
| + kNotStarted,
|
| + kWaiting,
|
| + kRequestProcessCall,
|
| + kCallingProcess,
|
| + kProcessDone,
|
| + kContinue,
|
| + kExiting,
|
| + kDead
|
| +};
|
| +
|
| +class EventTimerPosixTest : public testing::Test, public EventTimerPosix {
|
| + public:
|
| + EventTimerPosixTest()
|
| + : thread_state_(ThreadState::kNotStarted),
|
| + process_event_(false, true),
|
| + main_event_(false, true),
|
| + process_thread_id_(0),
|
| + process_thread_(nullptr) {}
|
| + virtual ~EventTimerPosixTest() {}
|
| +
|
| + rtc::PlatformThread* CreateThread() override {
|
| + EXPECT_TRUE(process_thread_ == nullptr);
|
| + process_thread_ =
|
| + new rtc::PlatformThread(Run, this, "EventTimerPosixTestThread");
|
| + return process_thread_;
|
| + }
|
| +
|
| + static bool Run(void* obj) {
|
| + return static_cast<EventTimerPosixTest*>(obj)->Process();
|
| + }
|
| +
|
| + bool Process() {
|
| + {
|
| + rtc::CritScope cs(&lock_);
|
| + process_thread_id_ = rtc::CurrentThreadId();
|
| + }
|
| +
|
| + if (!SetThreadState(ThreadState::kWaiting))
|
| + return false;
|
| +
|
| + if (!AwaitThreadState(ThreadState::kRequestProcessCall,
|
| + rtc::Event::kForever)) {
|
| + SetThreadState(ThreadState::kDead);
|
| + return false;
|
| + }
|
| +
|
| + if (!SetThreadState(ThreadState::kCallingProcess))
|
| + return false;
|
| +
|
| + EventTimerPosix::Process();
|
| +
|
| + if (!SetThreadState(ThreadState::kProcessDone))
|
| + return false;
|
| +
|
| + if (!AwaitThreadState(ThreadState::kContinue, rtc::Event::kForever)) {
|
| + SetThreadState(ThreadState::kDead);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + bool IsProcessThread() {
|
| + rtc::CritScope cs(&lock_);
|
| + return process_thread_id_ == rtc::CurrentThreadId();
|
| + }
|
| +
|
| + bool SetThreadState(ThreadState state) {
|
| + rtc::CritScope cs(&lock_);
|
| + if (thread_state_ == ThreadState::kDead)
|
| + return false;
|
| + thread_state_ = state;
|
| + if (IsProcessThread()) {
|
| + main_event_.Set();
|
| + } else {
|
| + process_event_.Set();
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + bool AwaitThreadState(ThreadState state, int timeout) {
|
| + rtc::Event* event = IsProcessThread() ? &process_event_ : &main_event_;
|
| + do {
|
| + rtc::CritScope cs(&lock_);
|
| + if (state != ThreadState::kDead && thread_state_ == ThreadState::kExiting)
|
| + return false;
|
| + if (thread_state_ == state)
|
| + return true;
|
| + } while (event->Wait(timeout));
|
| + return false;
|
| + }
|
| +
|
| + bool CallProcess(int timeout_ms) {
|
| + return AwaitThreadState(ThreadState::kWaiting, timeout_ms) &&
|
| + SetThreadState(ThreadState::kRequestProcessCall);
|
| + }
|
| +
|
| + bool AwaitProcessDone(int timeout_ms) {
|
| + return AwaitThreadState(ThreadState::kProcessDone, timeout_ms) &&
|
| + SetThreadState(ThreadState::kContinue);
|
| + }
|
| +
|
| + void TearDown() override {
|
| + if (process_thread_) {
|
| + SetThreadState(ThreadState::kExiting);
|
| + AwaitThreadState(ThreadState::kDead, 5000);
|
| + }
|
| + }
|
| +
|
| + ThreadState thread_state_;
|
| + rtc::CriticalSection lock_;
|
| + rtc::Event process_event_;
|
| + rtc::Event main_event_;
|
| + rtc::PlatformThreadId process_thread_id_;
|
| + rtc::PlatformThread* process_thread_;
|
| +};
|
| +
|
| +TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeout) {
|
| + const int kTimerIntervalMs = 100;
|
| + const int kTimeoutMs = 5000;
|
| + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
|
| + ASSERT_TRUE(CallProcess(kTimeoutMs));
|
| + EventTypeWrapper res = Wait(kTimeoutMs);
|
| + EXPECT_EQ(kEventSignaled, res);
|
| + ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
|
| +}
|
| +
|
| +TEST_F(EventTimerPosixTest, WaiterWakesImmediatelyAfterTimeout) {
|
| + const int kTimerIntervalMs = 100;
|
| + const int kTimeoutMs = 5000;
|
| + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
|
| + ASSERT_TRUE(CallProcess(kTimeoutMs));
|
| + ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
|
| + EventTypeWrapper res = Wait(0);
|
| + EXPECT_EQ(kEventSignaled, res);
|
| +}
|
| +
|
| +TEST_F(EventTimerPosixTest, WaiterBlocksUntilTimeoutProcessInactiveOnStart) {
|
| + const int kTimerIntervalMs = 100;
|
| + const int kTimeoutMs = 5000;
|
| + // First call to StartTimer initializes thread.
|
| + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
|
| +
|
| + // Process thread currently _not_ blocking on Process() call.
|
| + ASSERT_TRUE(AwaitThreadState(ThreadState::kWaiting, kTimeoutMs));
|
| +
|
| + // Start new one-off timer, then call Process().
|
| + ASSERT_TRUE(StartTimer(false, kTimerIntervalMs));
|
| + ASSERT_TRUE(CallProcess(kTimeoutMs));
|
| +
|
| + EventTypeWrapper res = Wait(kTimeoutMs);
|
| + EXPECT_EQ(kEventSignaled, res);
|
| +
|
| + ASSERT_TRUE(AwaitProcessDone(kTimeoutMs));
|
| +}
|
| +
|
| +} // namespace webrtc
|
|
|