// 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 "base/bind.h"
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.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 kTestMessage1 = 1;
static const uint32 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 talk_base::MessageHandler {
public:
MOCK_METHOD1(OnMessage, void(talk_base::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() {
talk_base::MessageData* data = new talk_base::MessageData();
MockMessageHandler handler;
EXPECT_CALL(handler, OnMessage(
MatchMessage(&handler, kTestMessage2, data)))
.WillOnce(DeleteMessageData());
thread_->Send(&handler, kTestMessage2, data);
}
protected:
ThreadWrapperTest()
: thread_(NULL) {
}
virtual void SetUp() OVERRIDE {
JingleThreadWrapper::EnsureForCurrentMessageLoop();
thread_ = talk_base::Thread::Current();
}
// ThreadWrapper destroyes itself when |message_loop_| is destroyed.
base::MessageLoop message_loop_;
talk_base::Thread* thread_;
MockMessageHandler handler1_;
MockMessageHandler handler2_;
};
TEST_F(ThreadWrapperTest, Post) {
talk_base::MessageData* data1 = new talk_base::MessageData();
talk_base::MessageData* data2 = new talk_base::MessageData();
talk_base::MessageData* data3 = new talk_base::MessageData();
talk_base::MessageData* data4 = new talk_base::MessageData();
thread_->Post(&handler1_, kTestMessage1, data1);
thread_->Post(&handler1_, kTestMessage2, data2);
thread_->Post(&handler2_, kTestMessage1, data3);
thread_->Post(&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());
message_loop_.RunUntilIdle();
}
TEST_F(ThreadWrapperTest, PostDelayed) {
talk_base::MessageData* data1 = new talk_base::MessageData();
talk_base::MessageData* data2 = new talk_base::MessageData();
talk_base::MessageData* data3 = new talk_base::MessageData();
talk_base::MessageData* data4 = new talk_base::MessageData();
thread_->PostDelayed(kTestDelayMs1, &handler1_, kTestMessage1, data1);
thread_->PostDelayed(kTestDelayMs2, &handler1_, kTestMessage2, data2);
thread_->PostDelayed(kTestDelayMs3, &handler2_, kTestMessage1, data3);
thread_->PostDelayed(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());
message_loop_.PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
base::TimeDelta::FromMilliseconds(kMaxTestDelay));
message_loop_.Run();
}
TEST_F(ThreadWrapperTest, Clear) {
thread_->Post(&handler1_, kTestMessage1, NULL);
thread_->Post(&handler1_, kTestMessage2, NULL);
thread_->Post(&handler2_, kTestMessage1, NULL);
thread_->Post(&handler2_, kTestMessage2, NULL);
thread_->Clear(&handler1_, kTestMessage2);
InSequence in_seq;
talk_base::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());
message_loop_.RunUntilIdle();
}
TEST_F(ThreadWrapperTest, ClearDelayed) {
thread_->PostDelayed(kTestDelayMs1, &handler1_, kTestMessage1, NULL);
thread_->PostDelayed(kTestDelayMs2, &handler1_, kTestMessage2, NULL);
thread_->PostDelayed(kTestDelayMs3, &handler2_, kTestMessage1, NULL);
thread_->PostDelayed(kTestDelayMs4, &handler2_, kTestMessage1, NULL);
thread_->Clear(&handler1_, kTestMessage2);
InSequence in_seq;
talk_base::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());
message_loop_.PostDelayedTask(
FROM_HERE,
base::MessageLoop::QuitClosure(),
base::TimeDelta::FromMilliseconds(kMaxTestDelay));
message_loop_.Run();
}
// Verify that the queue is cleared when a handler is destroyed.
TEST_F(ThreadWrapperTest, ClearDestoroyed) {
MockMessageHandler* handler_ptr;
{
MockMessageHandler handler;
handler_ptr = &handler;
thread_->Post(&handler, kTestMessage1, NULL);
}
talk_base::MessageList removed;
thread_->Clear(handler_ptr, talk_base::MQID_ANY, &removed);
DCHECK_EQ(0U, removed.size());
}
// Verify that Send() calls handler synchronously when called on the
// same thread.
TEST_F(ThreadWrapperTest, SendSameThread) {
talk_base::MessageData* data = new talk_base::MessageData();
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data)))
.WillOnce(DeleteMessageData());
thread_->Send(&handler1_, kTestMessage1, data);
}
void InitializeWrapperForNewThread(talk_base::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(true, false);
talk_base::Thread* target;
second_thread.message_loop()->PostTask(
FROM_HERE, base::Bind(&InitializeWrapperForNewThread,
&target, &initialized_event));
initialized_event.Wait();
ASSERT_TRUE(target != NULL);
talk_base::MessageData* data = new talk_base::MessageData();
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data)))
.WillOnce(DeleteMessageData());
target->Send(&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(true, false);
talk_base::Thread* target;
second_thread.message_loop()->PostTask(
FROM_HERE, base::Bind(&InitializeWrapperForNewThread,
&target, &initialized_event));
initialized_event.Wait();
ASSERT_TRUE(target != NULL);
talk_base::MessageData* data = new talk_base::MessageData();
EXPECT_CALL(handler1_, OnMessage(
MatchMessage(&handler1_, kTestMessage1, data)))
.WillOnce(DoAll(
InvokeWithoutArgs(
this, &ThreadWrapperTest::PingMainThread),
DeleteMessageData()));
target->Send(&handler1_, kTestMessage1, data);
Mock::VerifyAndClearExpectations(&handler1_);
}
TEST_F(ThreadWrapperTest, Dispose) {
bool deleted_;
thread_->Dispose(new DeletableObject(&deleted_));
EXPECT_FALSE(deleted_);
message_loop_.RunUntilIdle();
EXPECT_TRUE(deleted_);
}
} // namespace jingle_glue