heartbeat_sender_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/heartbeat_sender.h"
  5. #include <stdint.h>
  6. #include <memory>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/memory/raw_ptr.h"
  10. #include "base/run_loop.h"
  11. #include "base/strings/string_number_conversions.h"
  12. #include "base/test/bind.h"
  13. #include "base/test/mock_callback.h"
  14. #include "base/test/task_environment.h"
  15. #include "base/threading/sequenced_task_runner_handle.h"
  16. #include "base/time/time.h"
  17. #include "build/build_config.h"
  18. #include "build/chromeos_buildflags.h"
  19. #include "remoting/base/fake_oauth_token_getter.h"
  20. #include "remoting/base/protobuf_http_status.h"
  21. #include "remoting/signaling/fake_signal_strategy.h"
  22. #include "remoting/signaling/signal_strategy.h"
  23. #include "remoting/signaling/signaling_address.h"
  24. #include "services/network/public/cpp/shared_url_loader_factory.h"
  25. #include "testing/gmock/include/gmock/gmock.h"
  26. #include "testing/gtest/include/gtest/gtest.h"
  27. namespace remoting {
  28. namespace {
  29. using testing::_;
  30. using testing::AtMost;
  31. using testing::InSequence;
  32. using testing::Return;
  33. using HeartbeatResponseCallback =
  34. base::OnceCallback<void(const ProtobufHttpStatus&,
  35. std::unique_ptr<apis::v1::HeartbeatResponse>)>;
  36. constexpr char kOAuthAccessToken[] = "fake_access_token";
  37. constexpr char kHostId[] = "fake_host_id";
  38. constexpr char kUserEmail[] = "fake_user@domain.com";
  39. constexpr char kFtlId[] = "fake_user@domain.com/chromoting_ftl_abc123";
  40. constexpr int32_t kGoodIntervalSeconds = 300;
  41. constexpr base::TimeDelta kWaitForAllStrategiesConnectedTimeout =
  42. base::Seconds(5.5);
  43. constexpr base::TimeDelta kOfflineReasonTimeout = base::Seconds(123);
  44. constexpr base::TimeDelta kTestHeartbeatDelay = base::Seconds(350);
  45. void ValidateHeartbeat(std::unique_ptr<apis::v1::HeartbeatRequest> request,
  46. bool expected_is_initial_heartbeat = false,
  47. const std::string& expected_host_offline_reason = {},
  48. bool is_googler = false) {
  49. ASSERT_TRUE(request->has_host_version());
  50. if (expected_host_offline_reason.empty()) {
  51. ASSERT_FALSE(request->has_host_offline_reason());
  52. } else {
  53. ASSERT_EQ(expected_host_offline_reason, request->host_offline_reason());
  54. }
  55. ASSERT_EQ(kHostId, request->host_id());
  56. ASSERT_EQ(kFtlId, request->tachyon_id());
  57. ASSERT_TRUE(request->has_host_version());
  58. ASSERT_TRUE(request->has_host_os_version());
  59. ASSERT_TRUE(request->has_host_os_name());
  60. ASSERT_TRUE(request->has_host_cpu_type());
  61. ASSERT_EQ(expected_is_initial_heartbeat, request->is_initial_heartbeat());
  62. // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
  63. // of lacros-chrome is complete.
  64. #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
  65. ASSERT_EQ(is_googler, request->has_hostname());
  66. #else
  67. ASSERT_FALSE(request->has_hostname());
  68. #endif
  69. }
  70. decltype(auto) DoValidateHeartbeatAndRespondOk(
  71. bool expected_is_initial_heartbeat = false,
  72. const std::string& expected_host_offline_reason = {},
  73. bool is_googler = false) {
  74. return [=](std::unique_ptr<apis::v1::HeartbeatRequest> request,
  75. HeartbeatResponseCallback callback) {
  76. ValidateHeartbeat(std::move(request), expected_is_initial_heartbeat,
  77. expected_host_offline_reason, is_googler);
  78. auto response = std::make_unique<apis::v1::HeartbeatResponse>();
  79. response->set_set_interval_seconds(kGoodIntervalSeconds);
  80. std::move(callback).Run(ProtobufHttpStatus::OK(), std::move(response));
  81. };
  82. }
  83. class MockDelegate : public HeartbeatSender::Delegate {
  84. public:
  85. MOCK_METHOD0(OnFirstHeartbeatSuccessful, void());
  86. MOCK_METHOD0(OnHostNotFound, void());
  87. MOCK_METHOD0(OnAuthFailed, void());
  88. };
  89. class MockObserver : public HeartbeatSender::Observer {
  90. public:
  91. MOCK_METHOD0(OnHeartbeatSent, void());
  92. };
  93. } // namespace
  94. class HeartbeatSenderTest : public testing::Test {
  95. public:
  96. HeartbeatSenderTest() {
  97. signal_strategy_ =
  98. std::make_unique<FakeSignalStrategy>(SignalingAddress(kFtlId));
  99. // Start in disconnected state.
  100. signal_strategy_->Disconnect();
  101. mock_observer_ = std::make_unique<MockObserver>();
  102. heartbeat_sender_ = std::make_unique<HeartbeatSender>(
  103. &mock_delegate_, kHostId, signal_strategy_.get(), &oauth_token_getter_,
  104. mock_observer_.get(), nullptr, false);
  105. auto heartbeat_client = std::make_unique<MockHeartbeatClient>();
  106. mock_client_ = heartbeat_client.get();
  107. heartbeat_sender_->client_ = std::move(heartbeat_client);
  108. }
  109. ~HeartbeatSenderTest() override {
  110. heartbeat_sender_.reset();
  111. signal_strategy_.reset();
  112. task_environment_.FastForwardUntilNoTasksRemain();
  113. }
  114. protected:
  115. class MockHeartbeatClient : public HeartbeatSender::HeartbeatClient {
  116. public:
  117. MOCK_METHOD2(Heartbeat,
  118. void(std::unique_ptr<apis::v1::HeartbeatRequest>,
  119. HeartbeatResponseCallback));
  120. void CancelPendingRequests() override {
  121. // We just don't care about this method being called.
  122. }
  123. };
  124. HeartbeatSender* heartbeat_sender() { return heartbeat_sender_.get(); }
  125. void set_is_googler() { heartbeat_sender()->is_googler_ = true; }
  126. const net::BackoffEntry& GetBackoff() const {
  127. return heartbeat_sender_->backoff_;
  128. }
  129. base::test::TaskEnvironment task_environment_{
  130. base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  131. raw_ptr<MockHeartbeatClient> mock_client_;
  132. std::unique_ptr<MockObserver> mock_observer_;
  133. std::unique_ptr<FakeSignalStrategy> signal_strategy_;
  134. MockDelegate mock_delegate_;
  135. private:
  136. // |heartbeat_sender_| must be deleted before |signal_strategy_|.
  137. std::unique_ptr<HeartbeatSender> heartbeat_sender_;
  138. FakeOAuthTokenGetter oauth_token_getter_{OAuthTokenGetter::Status::SUCCESS,
  139. kUserEmail, kOAuthAccessToken};
  140. };
  141. TEST_F(HeartbeatSenderTest, SendHeartbeat) {
  142. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  143. .WillOnce(DoValidateHeartbeatAndRespondOk(true));
  144. EXPECT_CALL(*mock_observer_, OnHeartbeatSent());
  145. EXPECT_CALL(mock_delegate_, OnFirstHeartbeatSuccessful()).Times(1);
  146. signal_strategy_->Connect();
  147. task_environment_.FastForwardBy(kWaitForAllStrategiesConnectedTimeout);
  148. }
  149. TEST_F(HeartbeatSenderTest, SignalingReconnect_NewHeartbeats) {
  150. base::RunLoop run_loop;
  151. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  152. .WillOnce(DoValidateHeartbeatAndRespondOk(true))
  153. .WillOnce(DoValidateHeartbeatAndRespondOk())
  154. .WillOnce(DoValidateHeartbeatAndRespondOk());
  155. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).Times(3);
  156. EXPECT_CALL(mock_delegate_, OnFirstHeartbeatSuccessful()).Times(1);
  157. signal_strategy_->Connect();
  158. signal_strategy_->Disconnect();
  159. signal_strategy_->Connect();
  160. signal_strategy_->Disconnect();
  161. signal_strategy_->Connect();
  162. }
  163. TEST_F(HeartbeatSenderTest, Signaling_MultipleHeartbeats) {
  164. base::RunLoop run_loop;
  165. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  166. .WillOnce(DoValidateHeartbeatAndRespondOk(true))
  167. .WillOnce(DoValidateHeartbeatAndRespondOk())
  168. .WillOnce(DoValidateHeartbeatAndRespondOk());
  169. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).Times(3);
  170. EXPECT_CALL(mock_delegate_, OnFirstHeartbeatSuccessful()).Times(1);
  171. signal_strategy_->Connect();
  172. task_environment_.FastForwardBy(kTestHeartbeatDelay * 2);
  173. }
  174. TEST_F(HeartbeatSenderTest, SetHostOfflineReason) {
  175. base::MockCallback<base::OnceCallback<void(bool success)>> mock_ack_callback;
  176. EXPECT_CALL(mock_ack_callback, Run(_)).Times(0);
  177. heartbeat_sender()->SetHostOfflineReason("test_error", kOfflineReasonTimeout,
  178. mock_ack_callback.Get());
  179. testing::Mock::VerifyAndClearExpectations(&mock_ack_callback);
  180. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  181. .WillOnce(DoValidateHeartbeatAndRespondOk(true, "test_error"));
  182. EXPECT_CALL(*mock_observer_, OnHeartbeatSent());
  183. // Callback should run once, when we get response to offline-reason.
  184. EXPECT_CALL(mock_ack_callback, Run(_)).Times(1);
  185. EXPECT_CALL(mock_delegate_, OnFirstHeartbeatSuccessful()).Times(1);
  186. signal_strategy_->Connect();
  187. }
  188. TEST_F(HeartbeatSenderTest, UnknownHostId) {
  189. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  190. .WillRepeatedly([](std::unique_ptr<apis::v1::HeartbeatRequest> request,
  191. HeartbeatResponseCallback callback) {
  192. ValidateHeartbeat(std::move(request), true);
  193. std::move(callback).Run(
  194. ProtobufHttpStatus(ProtobufHttpStatus::Code::NOT_FOUND,
  195. "not found"),
  196. nullptr);
  197. });
  198. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).WillRepeatedly(Return());
  199. EXPECT_CALL(mock_delegate_, OnHostNotFound()).Times(1);
  200. signal_strategy_->Connect();
  201. task_environment_.FastForwardUntilNoTasksRemain();
  202. }
  203. TEST_F(HeartbeatSenderTest, FailedToHeartbeat_Backoff) {
  204. {
  205. InSequence sequence;
  206. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  207. .Times(2)
  208. .WillRepeatedly([&](std::unique_ptr<apis::v1::HeartbeatRequest> request,
  209. HeartbeatResponseCallback callback) {
  210. ValidateHeartbeat(std::move(request), true);
  211. std::move(callback).Run(
  212. ProtobufHttpStatus(ProtobufHttpStatus::Code::UNAVAILABLE,
  213. "unavailable"),
  214. nullptr);
  215. });
  216. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  217. .WillOnce(DoValidateHeartbeatAndRespondOk(true));
  218. }
  219. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).WillRepeatedly(Return());
  220. ASSERT_EQ(0, GetBackoff().failure_count());
  221. signal_strategy_->Connect();
  222. ASSERT_EQ(1, GetBackoff().failure_count());
  223. task_environment_.FastForwardBy(GetBackoff().GetTimeUntilRelease());
  224. ASSERT_EQ(2, GetBackoff().failure_count());
  225. task_environment_.FastForwardBy(GetBackoff().GetTimeUntilRelease());
  226. ASSERT_EQ(0, GetBackoff().failure_count());
  227. }
  228. TEST_F(HeartbeatSenderTest, HostComesBackOnlineAfterServiceOutage) {
  229. // Each call will simulate ~10 minutes of time (at max backoff duration).
  230. // We want to simulate a long outage (~3 hours) so run through 20 iterations.
  231. int retry_attempts = 20;
  232. {
  233. InSequence sequence;
  234. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  235. .Times(retry_attempts)
  236. .WillRepeatedly([&](std::unique_ptr<apis::v1::HeartbeatRequest> request,
  237. HeartbeatResponseCallback callback) {
  238. ValidateHeartbeat(std::move(request), true);
  239. std::move(callback).Run(
  240. ProtobufHttpStatus(ProtobufHttpStatus::Code::UNAVAILABLE,
  241. "unavailable"),
  242. nullptr);
  243. });
  244. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  245. .WillOnce(DoValidateHeartbeatAndRespondOk(true));
  246. }
  247. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).WillRepeatedly(Return());
  248. ASSERT_EQ(0, GetBackoff().failure_count());
  249. signal_strategy_->Connect();
  250. for (int i = 1; i <= retry_attempts; i++) {
  251. ASSERT_EQ(i, GetBackoff().failure_count());
  252. task_environment_.FastForwardBy(GetBackoff().GetTimeUntilRelease());
  253. }
  254. // Host successfully back online.
  255. ASSERT_EQ(0, GetBackoff().failure_count());
  256. }
  257. TEST_F(HeartbeatSenderTest, Unauthenticated) {
  258. int heartbeat_count = 0;
  259. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  260. .WillRepeatedly([&](std::unique_ptr<apis::v1::HeartbeatRequest> request,
  261. HeartbeatResponseCallback callback) {
  262. ValidateHeartbeat(std::move(request), true);
  263. heartbeat_count++;
  264. std::move(callback).Run(
  265. ProtobufHttpStatus(ProtobufHttpStatus::Code::UNAUTHENTICATED,
  266. "unauthenticated"),
  267. nullptr);
  268. });
  269. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).WillRepeatedly(Return());
  270. EXPECT_CALL(mock_delegate_, OnAuthFailed()).Times(1);
  271. signal_strategy_->Connect();
  272. task_environment_.FastForwardUntilNoTasksRemain();
  273. // Should retry heartbeating at least once.
  274. ASSERT_LT(1, heartbeat_count);
  275. }
  276. TEST_F(HeartbeatSenderTest, GooglerHostname) {
  277. set_is_googler();
  278. EXPECT_CALL(*mock_client_, Heartbeat(_, _))
  279. .WillOnce(DoValidateHeartbeatAndRespondOk(true, "", true));
  280. EXPECT_CALL(*mock_observer_, OnHeartbeatSent()).Times(1);
  281. signal_strategy_->Connect();
  282. }
  283. } // namespace remoting