Index: extensions/browser/api/cast_channel/keep_alive_delegate_unittest.cc |
diff --git a/extensions/browser/api/cast_channel/keep_alive_delegate_unittest.cc b/extensions/browser/api/cast_channel/keep_alive_delegate_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cc46dd5db22d8f50a53492ecba3c76a679e5f7bb |
--- /dev/null |
+++ b/extensions/browser/api/cast_channel/keep_alive_delegate_unittest.cc |
@@ -0,0 +1,219 @@ |
+// Copyright 2015 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 "extensions/browser/api/cast_channel/keep_alive_delegate.h" |
+ |
+#include <stdint.h> |
+ |
+#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
+#include "base/timer/mock_timer.h" |
+#include "extensions/browser/api/cast_channel/cast_test_util.h" |
+#include "net/base/net_errors.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using testing::_; |
+using testing::Sequence; |
+ |
+namespace extensions { |
+namespace api { |
+namespace cast_channel { |
+namespace { |
+ |
+const int64_t kTestPingTimeoutMillis = 1000; |
+const int64_t kTestLivenessTimeoutMillis = 10000; |
+ |
+// Extends MockTimer with a mockable method ResetTriggered() which permits |
+// test code to set GMock expectations for Timer::Reset(). |
+class MockTimerWithMonitoredReset : public base::MockTimer { |
+ public: |
+ MockTimerWithMonitoredReset(bool retain_user_task, bool is_repeating) |
+ : base::MockTimer(retain_user_task, is_repeating) {} |
+ ~MockTimerWithMonitoredReset() override {} |
+ |
+ // Instrumentation point for determining how many times Reset() was called. |
+ MOCK_METHOD0(ResetTriggered, void(void)); |
+ MOCK_METHOD0(Stop, void(void)); |
+ |
+ // Passes through the Reset call to the base MockTimer and visits the mock |
+ // ResetTriggered method. |
+ void Reset() override { |
+ base::MockTimer::Reset(); |
+ ResetTriggered(); |
+ } |
+}; |
+ |
+class KeepAliveDelegateTest : public testing::Test { |
+ public: |
+ using ChannelError = ::cast_channel::ChannelError; |
+ |
+ KeepAliveDelegateTest() {} |
+ ~KeepAliveDelegateTest() override {} |
+ |
+ protected: |
+ void SetUp() override { |
+ inner_delegate_ = new MockCastTransportDelegate; |
+ logger_ = new Logger(); |
+ keep_alive_.reset(new KeepAliveDelegate( |
+ &socket_, logger_, base::WrapUnique(inner_delegate_), |
+ base::TimeDelta::FromMilliseconds(kTestPingTimeoutMillis), |
+ base::TimeDelta::FromMilliseconds(kTestLivenessTimeoutMillis))); |
+ liveness_timer_ = new MockTimerWithMonitoredReset(true, false); |
+ ping_timer_ = new MockTimerWithMonitoredReset(true, false); |
+ EXPECT_CALL(*liveness_timer_, Stop()).Times(0); |
+ EXPECT_CALL(*ping_timer_, Stop()).Times(0); |
+ keep_alive_->SetTimersForTest(base::WrapUnique(ping_timer_), |
+ base::WrapUnique(liveness_timer_)); |
+ } |
+ |
+ // Runs all pending tasks in the message loop. |
+ void RunPendingTasks() { |
+ base::RunLoop run_loop; |
+ run_loop.RunUntilIdle(); |
+ } |
+ |
+ base::MessageLoop message_loop_; |
+ MockCastSocket socket_; |
+ std::unique_ptr<KeepAliveDelegate> keep_alive_; |
+ scoped_refptr<Logger> logger_; |
+ MockCastTransportDelegate* inner_delegate_; |
+ MockTimerWithMonitoredReset* liveness_timer_; |
+ MockTimerWithMonitoredReset* ping_timer_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(KeepAliveDelegateTest); |
+}; |
+ |
+TEST_F(KeepAliveDelegateTest, TestErrorHandledBeforeStarting) { |
+ keep_alive_->OnError(ChannelError::CONNECT_ERROR); |
+} |
+ |
+TEST_F(KeepAliveDelegateTest, TestPing) { |
+ EXPECT_CALL(*socket_.mock_transport(), |
+ SendMessage(EqualsProto(KeepAliveDelegate::CreateKeepAliveMessage( |
+ KeepAliveDelegate::kHeartbeatPingType)), |
+ _)) |
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK)); |
+ EXPECT_CALL(*inner_delegate_, Start()); |
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(2); |
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(2); |
+ EXPECT_CALL(*ping_timer_, Stop()); |
+ |
+ keep_alive_->Start(); |
+ ping_timer_->Fire(); |
+ keep_alive_->OnMessage(KeepAliveDelegate::CreateKeepAliveMessage( |
+ KeepAliveDelegate::kHeartbeatPongType)); |
+ RunPendingTasks(); |
+} |
+ |
+TEST_F(KeepAliveDelegateTest, TestPingFailed) { |
+ EXPECT_CALL(*socket_.mock_transport(), |
+ SendMessage(EqualsProto(KeepAliveDelegate::CreateKeepAliveMessage( |
+ KeepAliveDelegate::kHeartbeatPingType)), |
+ _)) |
+ .WillOnce(PostCompletionCallbackTask<1>(net::ERR_CONNECTION_RESET)); |
+ EXPECT_CALL(*inner_delegate_, Start()); |
+ EXPECT_CALL(*inner_delegate_, OnError(ChannelError::CAST_SOCKET_ERROR)); |
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(1); |
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(1); |
+ EXPECT_CALL(*liveness_timer_, Stop()); |
+ EXPECT_CALL(*ping_timer_, Stop()).Times(2); |
+ |
+ keep_alive_->Start(); |
+ ping_timer_->Fire(); |
+ RunPendingTasks(); |
+ EXPECT_EQ(proto::PING_WRITE_ERROR, |
+ logger_->GetLastErrors(socket_.id()).event_type); |
+ EXPECT_EQ(net::ERR_CONNECTION_RESET, |
+ logger_->GetLastErrors(socket_.id()).net_return_value); |
+} |
+ |
+TEST_F(KeepAliveDelegateTest, TestPingAndLivenessTimeout) { |
+ EXPECT_CALL(*socket_.mock_transport(), |
+ SendMessage(EqualsProto(KeepAliveDelegate::CreateKeepAliveMessage( |
+ KeepAliveDelegate::kHeartbeatPingType)), |
+ _)) |
+ .WillOnce(PostCompletionCallbackTask<1>(net::OK)); |
+ EXPECT_CALL(*inner_delegate_, OnError(ChannelError::PING_TIMEOUT)); |
+ EXPECT_CALL(*inner_delegate_, Start()); |
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(1); |
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(1); |
+ EXPECT_CALL(*liveness_timer_, Stop()).Times(2); |
+ EXPECT_CALL(*ping_timer_, Stop()).Times(2); |
+ |
+ keep_alive_->Start(); |
+ ping_timer_->Fire(); |
+ liveness_timer_->Fire(); |
+ RunPendingTasks(); |
+} |
+ |
+TEST_F(KeepAliveDelegateTest, TestResetTimersAndPassthroughAllOtherTraffic) { |
+ CastMessage other_message = |
+ KeepAliveDelegate::CreateKeepAliveMessage("NEITHER_PING_NOR_PONG"); |
+ |
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(other_message))); |
+ EXPECT_CALL(*inner_delegate_, Start()); |
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(2); |
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(2); |
+ |
+ keep_alive_->Start(); |
+ keep_alive_->OnMessage(other_message); |
+ RunPendingTasks(); |
+} |
+ |
+TEST_F(KeepAliveDelegateTest, TestPassthroughMessagesAfterError) { |
+ CastMessage message = |
+ KeepAliveDelegate::CreateKeepAliveMessage("NEITHER_PING_NOR_PONG"); |
+ CastMessage message_after_error = |
+ KeepAliveDelegate::CreateKeepAliveMessage("ANOTHER_NOT_PING_NOR_PONG"); |
+ CastMessage late_ping_message = KeepAliveDelegate::CreateKeepAliveMessage( |
+ KeepAliveDelegate::kHeartbeatPingType); |
+ |
+ EXPECT_CALL(*inner_delegate_, Start()).Times(1); |
+ EXPECT_CALL(*ping_timer_, ResetTriggered()).Times(2); |
+ EXPECT_CALL(*liveness_timer_, ResetTriggered()).Times(2); |
+ EXPECT_CALL(*liveness_timer_, Stop()).Times(1); |
+ EXPECT_CALL(*ping_timer_, Stop()).Times(1); |
+ |
+ Sequence message_and_error_sequence; |
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(message))) |
+ .Times(1) |
+ .InSequence(message_and_error_sequence) |
+ .RetiresOnSaturation(); |
+ EXPECT_CALL(*inner_delegate_, OnError(ChannelError::INVALID_MESSAGE)) |
+ .Times(1) |
+ .InSequence(message_and_error_sequence); |
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(message_after_error))) |
+ .Times(1) |
+ .InSequence(message_and_error_sequence) |
+ .RetiresOnSaturation(); |
+ EXPECT_CALL(*inner_delegate_, OnMessage(EqualsProto(late_ping_message))) |
+ .Times(0) |
+ .InSequence(message_and_error_sequence) |
+ .RetiresOnSaturation(); |
+ |
+ // Start, process one message, then error-out. KeepAliveDelegate will |
+ // automatically stop itself. |
+ keep_alive_->Start(); |
+ keep_alive_->OnMessage(message); |
+ RunPendingTasks(); |
+ keep_alive_->OnError(ChannelError::INVALID_MESSAGE); |
+ RunPendingTasks(); |
+ |
+ // Process a non-PING/PONG message and expect it to pass through. |
+ keep_alive_->OnMessage(message_after_error); |
+ RunPendingTasks(); |
+ |
+ // Process a late-arriving PING/PONG message, which should have no effect. |
+ keep_alive_->OnMessage(late_ping_message); |
+ RunPendingTasks(); |
+} |
+ |
+} // namespace |
+} // namespace cast_channel |
+} // namespace api |
+} // namespace extensions |