blob: 41d902e75958ee26902065a59bf04245910a4033 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "jingle/glue/thread_wrapper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::InvokeWithoutArgs;
using ::testing::Mock;
namespace jingle_glue {
static const uint32_t kTestMessage1 = 1;
static const uint32_t kTestMessage2 = 2;
static const int kTestDelayMs1 = 10;
static const int kTestDelayMs2 = 20;
static const int kTestDelayMs3 = 30;
static const int kTestDelayMs4 = 40;
static const int kMaxTestDelay = 40;
namespace {
class MockMessageHandler : public rtc::MessageHandler {
public:
MOCK_METHOD1(OnMessage, void(rtc::Message* msg));
};
MATCHER_P3(MatchMessage, handler, message_id, data, "") {
return arg->phandler == handler &&
arg->message_id == message_id &&
arg->pdata == data;
}
ACTION(DeleteMessageData) {
delete arg0->pdata;
}
// Helper class used in the Dispose test.
class DeletableObject {
public:
DeletableObject(bool* deleted)
: deleted_(deleted) {
*deleted = false;
}
~DeletableObject() {
*deleted_ = true;
}
private:
bool* deleted_;
};
} // namespace
class ThreadWrapperTest : public testing::Test {
public:
// This method is used by the SendDuringSend test. It sends message to the
// main thread synchronously using Send().
void PingMainThread() {
rtc::MessageData* data = new rtc::MessageData();
MockMessageHandler handler;
EXPECT_CALL(handler, OnMessage(
MatchMessage(&handler, kTestMessage2, data)))
.WillOnce(DeleteMessageData());
thread_->Send(RTC_FROM_HERE, &handler, kTestMessage2, data);
}
protected:
ThreadWrapperTest()
: thread_(NULL) {
}
void SetUp() override {
JingleThreadWrapper::EnsureForCurrentMessageLoop();
thread_ = rtc::Thread::Current();
}
// ThreadWrapper destroyes itself when |message_loop_| is destroyed.
base::MessageLoop message_loop_;
rtc::Thread* thread_;
MockMessageHandler handler1_;
MockMessageHandler handler2_;
};
TEST_F(ThreadWrapperTest, Post) {
rtc::MessageData* data1 = new rtc::MessageData();
rtc::MessageData* data2 = new rtc::MessageData();
rtc::MessageData* data3 = new rtc::MessageData();
rtc::MessageData* data4 = new rtc::MessageData();
thread_->Post(RTC_FROM_HERE, &handler1_, kTestMessage1, data1);
thread_->Post(RTC_FROM_HERE, &handler1_, kTestMessage2, data2);
thread_->Post(RTC_FROM_HERE, &handler2_, kTestMessage1, data3);
thread_->Post(RTC_FROM_HERE, &handler2_, kTestMessage1, data4);
InSequence in_seq;
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data1)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage2, data2)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, data3)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, data4)))
.WillOnce(DeleteMessageData());
base::RunLoop().RunUntilIdle();
}
TEST_F(ThreadWrapperTest, PostDelayed) {
rtc::MessageData* data1 = new rtc::MessageData();
rtc::MessageData* data2 = new rtc::MessageData();
rtc::MessageData* data3 = new rtc::MessageData();
rtc::MessageData* data4 = new rtc::MessageData();
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs1, &handler1_, kTestMessage1,
data1);
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs2, &handler1_, kTestMessage2,
data2);
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs3, &handler2_, kTestMessage1,
data3);
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs4, &handler2_, kTestMessage1,
data4);
InSequence in_seq;
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data1)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage2, data2)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, data3)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, data4)))
.WillOnce(DeleteMessageData());
base::RunLoop run_loop;
message_loop_.task_runner()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(kMaxTestDelay));
run_loop.Run();
}
TEST_F(ThreadWrapperTest, Clear) {
thread_->Post(RTC_FROM_HERE, &handler1_, kTestMessage1, NULL);
thread_->Post(RTC_FROM_HERE, &handler1_, kTestMessage2, NULL);
thread_->Post(RTC_FROM_HERE, &handler2_, kTestMessage1, NULL);
thread_->Post(RTC_FROM_HERE, &handler2_, kTestMessage2, NULL);
thread_->Clear(&handler1_, kTestMessage2);
InSequence in_seq;
rtc::MessageData* null_data = NULL;
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, null_data)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, null_data)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage2, null_data)))
.WillOnce(DeleteMessageData());
base::RunLoop().RunUntilIdle();
}
TEST_F(ThreadWrapperTest, ClearDelayed) {
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs1, &handler1_, kTestMessage1,
NULL);
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs2, &handler1_, kTestMessage2,
NULL);
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs3, &handler2_, kTestMessage1,
NULL);
thread_->PostDelayed(RTC_FROM_HERE, kTestDelayMs4, &handler2_, kTestMessage1,
NULL);
thread_->Clear(&handler1_, kTestMessage2);
InSequence in_seq;
rtc::MessageData* null_data = NULL;
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, null_data)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, null_data)))
.WillOnce(DeleteMessageData());
EXPECT_CALL(handler2_, OnMessage(
MatchMessage(&handler2_, kTestMessage1, null_data)))
.WillOnce(DeleteMessageData());
base::RunLoop run_loop;
message_loop_.task_runner()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(kMaxTestDelay));
run_loop.Run();
}
// Verify that the queue is cleared when a handler is destroyed.
TEST_F(ThreadWrapperTest, ClearDestroyed) {
MockMessageHandler* handler_ptr;
{
MockMessageHandler handler;
handler_ptr = &handler;
thread_->Post(RTC_FROM_HERE, &handler, kTestMessage1, NULL);
}
rtc::MessageList removed;
thread_->Clear(handler_ptr, rtc::MQID_ANY, &removed);
DCHECK_EQ(0U, removed.size());
}
// Verify that Send() calls handler synchronously when called on the
// same thread.
TEST_F(ThreadWrapperTest, SendSameThread) {
rtc::MessageData* data = new rtc::MessageData();
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data)))
.WillOnce(DeleteMessageData());
thread_->Send(RTC_FROM_HERE, &handler1_, kTestMessage1, data);
}
void InitializeWrapperForNewThread(rtc::Thread** thread,
base::WaitableEvent* done_event) {
JingleThreadWrapper::EnsureForCurrentMessageLoop();
JingleThreadWrapper::current()->set_send_allowed(true);
*thread = JingleThreadWrapper::current();
done_event->Signal();
}
// Verify that Send() calls handler synchronously when called for a
// different thread.
TEST_F(ThreadWrapperTest, SendToOtherThread) {
JingleThreadWrapper::current()->set_send_allowed(true);
base::Thread second_thread("JingleThreadWrapperTest");
second_thread.Start();
base::WaitableEvent initialized_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
rtc::Thread* target;
second_thread.task_runner()->PostTask(
FROM_HERE,
base::Bind(&InitializeWrapperForNewThread, &target, &initialized_event));
initialized_event.Wait();
ASSERT_TRUE(target != NULL);
rtc::MessageData* data = new rtc::MessageData();
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data)))
.WillOnce(DeleteMessageData());
target->Send(RTC_FROM_HERE, &handler1_, kTestMessage1, data);
Mock::VerifyAndClearExpectations(&handler1_);
}
// Verify that thread handles Send() while another Send() is
// pending. The test creates second thread and Send()s kTestMessage1
// to that thread. kTestMessage1 handler calls PingMainThread() which
// tries to Send() kTestMessage2 to the main thread.
TEST_F(ThreadWrapperTest, SendDuringSend) {
JingleThreadWrapper::current()->set_send_allowed(true);
base::Thread second_thread("JingleThreadWrapperTest");
second_thread.Start();
base::WaitableEvent initialized_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
rtc::Thread* target;
second_thread.task_runner()->PostTask(
FROM_HERE,
base::Bind(&InitializeWrapperForNewThread, &target, &initialized_event));
initialized_event.Wait();
ASSERT_TRUE(target != NULL);
rtc::MessageData* data = new rtc::MessageData();
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data)))
.WillOnce(DoAll(
InvokeWithoutArgs(
this, &ThreadWrapperTest::PingMainThread),
DeleteMessageData()));
target->Send(RTC_FROM_HERE, &handler1_, kTestMessage1, data);
Mock::VerifyAndClearExpectations(&handler1_);
}
TEST_F(ThreadWrapperTest, Dispose) {
bool deleted_ = false;
thread_->Dispose(new DeletableObject(&deleted_));
EXPECT_FALSE(deleted_);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(deleted_);
}
} // namespace jingle_glue