Returning fetching back.
[chromium-blink-merge.git] / components / invalidation / gcm_network_channel_unittest.cc
blob6c1bac68758ed9db087c592ba6ca7271b71db843
1 // Copyright 2014 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.
5 #include "base/run_loop.h"
6 #include "base/strings/string_util.h"
7 #include "components/invalidation/gcm_network_channel.h"
8 #include "google_apis/gaia/google_service_auth_error.h"
9 #include "net/url_request/test_url_fetcher_factory.h"
10 #include "net/url_request/url_request_test_util.h"
11 #include "testing/gtest/include/gtest/gtest.h"
13 namespace syncer {
15 class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate {
16 public:
17 TestGCMNetworkChannelDelegate()
18 : register_call_count_(0) {}
20 void Initialize(
21 GCMNetworkChannelDelegate::ConnectionStateCallback callback) override {
22 connection_state_callback = callback;
25 void RequestToken(RequestTokenCallback callback) override {
26 request_token_callback = callback;
29 void InvalidateToken(const std::string& token) override {
30 invalidated_token = token;
33 void Register(RegisterCallback callback) override {
34 ++register_call_count_;
35 register_callback = callback;
38 void SetMessageReceiver(MessageCallback callback) override {
39 message_callback = callback;
42 RequestTokenCallback request_token_callback;
43 std::string invalidated_token;
44 RegisterCallback register_callback;
45 int register_call_count_;
46 MessageCallback message_callback;
47 ConnectionStateCallback connection_state_callback;
50 // Backoff policy for test. Run first 5 retries without delay.
51 const net::BackoffEntry::Policy kTestBackoffPolicy = {
52 // Number of initial errors (in sequence) to ignore before applying
53 // exponential back-off rules.
56 // Initial delay for exponential back-off in ms.
57 2000, // 2 seconds.
59 // Factor by which the waiting time will be multiplied.
62 // Fuzzing percentage. ex: 10% will spread requests randomly
63 // between 90%-100% of the calculated time.
64 0.2, // 20%.
66 // Maximum amount of time we are willing to delay our request in ms.
67 1000 * 3600 * 4, // 4 hours.
69 // Time to keep an entry from being discarded even when it
70 // has no significant state, -1 to never discard.
71 -1,
73 // Don't use initial delay unless the last request was an error.
74 false,
77 class TestGCMNetworkChannel : public GCMNetworkChannel {
78 public:
79 TestGCMNetworkChannel(
80 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
81 scoped_ptr<GCMNetworkChannelDelegate> delegate)
82 : GCMNetworkChannel(request_context_getter, delegate.Pass()) {
83 ResetRegisterBackoffEntryForTest(&kTestBackoffPolicy);
86 protected:
87 // On Android GCMNetworkChannel::BuildUrl hits NOTREACHED(). I still want
88 // tests to run.
89 GURL BuildUrl(const std::string& registration_id) override {
90 return GURL("http://test.url.com");
94 class GCMNetworkChannelTest;
96 // Test needs to capture setting echo-token header on http request.
97 // This class is going to do that.
98 class TestNetworkChannelURLFetcher : public net::FakeURLFetcher {
99 public:
100 TestNetworkChannelURLFetcher(GCMNetworkChannelTest* test,
101 const GURL& url,
102 net::URLFetcherDelegate* delegate,
103 const std::string& response_data,
104 net::HttpStatusCode response_code,
105 net::URLRequestStatus::Status status)
106 : net::FakeURLFetcher(url,
107 delegate,
108 response_data,
109 response_code,
110 status),
111 test_(test) {}
113 void AddExtraRequestHeader(const std::string& header_line) override;
115 private:
116 GCMNetworkChannelTest* test_;
119 class GCMNetworkChannelTest
120 : public ::testing::Test,
121 public SyncNetworkChannel::Observer {
122 public:
123 GCMNetworkChannelTest()
124 : delegate_(NULL),
125 url_fetchers_created_count_(0),
126 last_invalidator_state_(TRANSIENT_INVALIDATION_ERROR) {}
128 ~GCMNetworkChannelTest() override {}
130 void SetUp() override {
131 request_context_getter_ = new net::TestURLRequestContextGetter(
132 base::MessageLoopProxy::current());
133 // Ownership of delegate goes to GCNMentworkChannel but test needs pointer
134 // to it.
135 delegate_ = new TestGCMNetworkChannelDelegate();
136 scoped_ptr<GCMNetworkChannelDelegate> delegate(delegate_);
137 gcm_network_channel_.reset(new TestGCMNetworkChannel(
138 request_context_getter_,
139 delegate.Pass()));
140 gcm_network_channel_->AddObserver(this);
141 gcm_network_channel_->SetMessageReceiver(
142 invalidation::NewPermanentCallback(
143 this, &GCMNetworkChannelTest::OnIncomingMessage));
144 url_fetcher_factory_.reset(new net::FakeURLFetcherFactory(NULL,
145 base::Bind(&GCMNetworkChannelTest::CreateURLFetcher,
146 base::Unretained(this))));
149 void TearDown() override { gcm_network_channel_->RemoveObserver(this); }
151 // Helper functions to call private methods from test
152 GURL BuildUrl(const std::string& registration_id) {
153 return gcm_network_channel_->GCMNetworkChannel::BuildUrl(registration_id);
156 static void Base64EncodeURLSafe(const std::string& input,
157 std::string* output) {
158 GCMNetworkChannel::Base64EncodeURLSafe(input, output);
161 static bool Base64DecodeURLSafe(const std::string& input,
162 std::string* output) {
163 return GCMNetworkChannel::Base64DecodeURLSafe(input, output);
166 void OnNetworkChannelStateChanged(
167 InvalidatorState invalidator_state) override {
168 last_invalidator_state_ = invalidator_state;
171 void OnIncomingMessage(std::string incoming_message) {
174 GCMNetworkChannel* network_channel() {
175 return gcm_network_channel_.get();
178 TestGCMNetworkChannelDelegate* delegate() {
179 return delegate_;
182 int url_fetchers_created_count() {
183 return url_fetchers_created_count_;
186 net::FakeURLFetcherFactory* url_fetcher_factory() {
187 return url_fetcher_factory_.get();
190 scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
191 const GURL& url,
192 net::URLFetcherDelegate* delegate,
193 const std::string& response_data,
194 net::HttpStatusCode response_code,
195 net::URLRequestStatus::Status status) {
196 ++url_fetchers_created_count_;
197 return scoped_ptr<net::FakeURLFetcher>(new TestNetworkChannelURLFetcher(
198 this, url, delegate, response_data, response_code, status));
201 void set_last_echo_token(const std::string& echo_token) {
202 last_echo_token_ = echo_token;
205 const std::string& get_last_echo_token() {
206 return last_echo_token_;
209 InvalidatorState get_last_invalidator_state() {
210 return last_invalidator_state_;
213 void RunLoopUntilIdle() {
214 base::RunLoop run_loop;
215 run_loop.RunUntilIdle();
218 private:
219 base::MessageLoop message_loop_;
220 TestGCMNetworkChannelDelegate* delegate_;
221 scoped_ptr<GCMNetworkChannel> gcm_network_channel_;
222 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
223 scoped_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
224 int url_fetchers_created_count_;
225 std::string last_echo_token_;
226 InvalidatorState last_invalidator_state_;
229 void TestNetworkChannelURLFetcher::AddExtraRequestHeader(
230 const std::string& header_line) {
231 net::FakeURLFetcher::AddExtraRequestHeader(header_line);
232 std::string header_name("echo-token: ");
233 std::string echo_token;
234 if (StartsWithASCII(header_line, header_name, false)) {
235 echo_token = header_line;
236 ReplaceFirstSubstringAfterOffset(
237 &echo_token, 0, header_name, std::string());
238 test_->set_last_echo_token(echo_token);
242 TEST_F(GCMNetworkChannelTest, HappyCase) {
243 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
244 EXPECT_FALSE(delegate()->message_callback.is_null());
245 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
246 std::string(),
247 net::HTTP_NO_CONTENT,
248 net::URLRequestStatus::SUCCESS);
250 // Emulate gcm connection state to be online.
251 delegate()->connection_state_callback.Run(true);
252 // After construction GCMNetworkChannel should have called Register.
253 EXPECT_FALSE(delegate()->register_callback.is_null());
254 // Return valid registration id.
255 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
257 network_channel()->SendMessage("abra.cadabra");
258 // SendMessage should have triggered RequestToken. No HTTP request should be
259 // started yet.
260 EXPECT_FALSE(delegate()->request_token_callback.is_null());
261 EXPECT_EQ(url_fetchers_created_count(), 0);
262 // Return valid access token. This should trigger HTTP request.
263 delegate()->request_token_callback.Run(
264 GoogleServiceAuthError::AuthErrorNone(), "access.token");
265 RunLoopUntilIdle();
266 EXPECT_EQ(url_fetchers_created_count(), 1);
268 // Return another access token. Message should be cleared by now and shouldn't
269 // be sent.
270 delegate()->request_token_callback.Run(
271 GoogleServiceAuthError::AuthErrorNone(), "access.token2");
272 RunLoopUntilIdle();
273 EXPECT_EQ(url_fetchers_created_count(), 1);
274 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
277 TEST_F(GCMNetworkChannelTest, FailedRegister) {
278 // After construction GCMNetworkChannel should have called Register.
279 EXPECT_FALSE(delegate()->register_callback.is_null());
280 EXPECT_EQ(1, delegate()->register_call_count_);
281 // Return transient error from Register call.
282 delegate()->register_callback.Run("", gcm::GCMClient::NETWORK_ERROR);
283 RunLoopUntilIdle();
284 // GcmNetworkChannel should have scheduled Register retry.
285 EXPECT_EQ(2, delegate()->register_call_count_);
286 // Return persistent error from Register call.
287 delegate()->register_callback.Run("", gcm::GCMClient::GCM_DISABLED);
288 RunLoopUntilIdle();
289 // GcmNetworkChannel should give up trying.
290 EXPECT_EQ(2, delegate()->register_call_count_);
292 network_channel()->SendMessage("abra.cadabra");
293 // SendMessage shouldn't trigger RequestToken.
294 EXPECT_TRUE(delegate()->request_token_callback.is_null());
295 EXPECT_EQ(0, url_fetchers_created_count());
298 TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) {
299 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
301 net::HTTP_NO_CONTENT,
302 net::URLRequestStatus::SUCCESS);
304 // After construction GCMNetworkChannel should have called Register.
305 EXPECT_FALSE(delegate()->register_callback.is_null());
307 network_channel()->SendMessage("abra.cadabra");
308 // SendMessage shouldn't trigger RequestToken.
309 EXPECT_TRUE(delegate()->request_token_callback.is_null());
310 EXPECT_EQ(url_fetchers_created_count(), 0);
312 // Return valid registration id.
313 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
315 EXPECT_FALSE(delegate()->request_token_callback.is_null());
316 EXPECT_EQ(url_fetchers_created_count(), 0);
317 // Return valid access token. This should trigger HTTP request.
318 delegate()->request_token_callback.Run(
319 GoogleServiceAuthError::AuthErrorNone(), "access.token");
320 RunLoopUntilIdle();
321 EXPECT_EQ(url_fetchers_created_count(), 1);
324 TEST_F(GCMNetworkChannelTest, RequestTokenFailure) {
325 // After construction GCMNetworkChannel should have called Register.
326 EXPECT_FALSE(delegate()->register_callback.is_null());
327 // Return valid registration id.
328 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
330 network_channel()->SendMessage("abra.cadabra");
331 // SendMessage should have triggered RequestToken. No HTTP request should be
332 // started yet.
333 EXPECT_FALSE(delegate()->request_token_callback.is_null());
334 EXPECT_EQ(url_fetchers_created_count(), 0);
335 // RequestToken returns failure.
336 delegate()->request_token_callback.Run(
337 GoogleServiceAuthError::FromConnectionError(1), "");
339 // Should be no HTTP requests.
340 EXPECT_EQ(url_fetchers_created_count(), 0);
343 TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) {
344 // Setup fake response to return AUTH_ERROR.
345 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
347 net::HTTP_UNAUTHORIZED,
348 net::URLRequestStatus::SUCCESS);
350 // After construction GCMNetworkChannel should have called Register.
351 EXPECT_FALSE(delegate()->register_callback.is_null());
352 // Return valid registration id.
353 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
355 network_channel()->SendMessage("abra.cadabra");
356 // SendMessage should have triggered RequestToken. No HTTP request should be
357 // started yet.
358 EXPECT_FALSE(delegate()->request_token_callback.is_null());
359 EXPECT_EQ(url_fetchers_created_count(), 0);
360 // Return valid access token. This should trigger HTTP request.
361 delegate()->request_token_callback.Run(
362 GoogleServiceAuthError::AuthErrorNone(), "access.token");
363 RunLoopUntilIdle();
364 EXPECT_EQ(url_fetchers_created_count(), 1);
365 EXPECT_EQ(delegate()->invalidated_token, "access.token");
368 // Following two tests are to check for memory leaks/crashes when Register and
369 // RequestToken don't complete by the teardown.
370 TEST_F(GCMNetworkChannelTest, RegisterNeverCompletes) {
371 network_channel()->SendMessage("abra.cadabra");
372 // Register should be called by now. Let's not complete and see what happens.
373 EXPECT_FALSE(delegate()->register_callback.is_null());
376 TEST_F(GCMNetworkChannelTest, RequestTokenNeverCompletes) {
377 network_channel()->SendMessage("abra.cadabra");
378 // Return valid registration id.
379 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
380 // RequestToken should be called by now. Let's not complete and see what
381 // happens.
382 EXPECT_FALSE(delegate()->request_token_callback.is_null());
385 TEST_F(GCMNetworkChannelTest, Base64EncodeDecode) {
386 std::string input;
387 std::string plain;
388 std::string base64;
389 // Empty string.
390 Base64EncodeURLSafe(input, &base64);
391 EXPECT_TRUE(base64.empty());
392 EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
393 EXPECT_EQ(input, plain);
394 // String length: 1..7.
395 for (int length = 1; length < 8; length++) {
396 input = "abra.cadabra";
397 input.resize(length);
398 Base64EncodeURLSafe(input, &base64);
399 // Ensure no padding at the end.
400 EXPECT_NE(base64[base64.size() - 1], '=');
401 EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
402 EXPECT_EQ(input, plain);
404 // Presence of '-', '_'.
405 input = "\xfb\xff";
406 Base64EncodeURLSafe(input, &base64);
407 EXPECT_EQ("-_8", base64);
408 EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
409 EXPECT_EQ(input, plain);
412 TEST_F(GCMNetworkChannelTest, ChannelState) {
413 EXPECT_FALSE(delegate()->message_callback.is_null());
414 // POST will fail.
415 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
416 std::string(),
417 net::HTTP_SERVICE_UNAVAILABLE,
418 net::URLRequestStatus::SUCCESS);
420 delegate()->connection_state_callback.Run(true);
421 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
423 network_channel()->SendMessage("abra.cadabra");
424 EXPECT_FALSE(delegate()->request_token_callback.is_null());
425 delegate()->request_token_callback.Run(
426 GoogleServiceAuthError::AuthErrorNone(), "access.token");
427 RunLoopUntilIdle();
428 EXPECT_EQ(url_fetchers_created_count(), 1);
429 // Failing HTTP POST should cause TRANSIENT_INVALIDATION_ERROR.
430 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
432 // Setup POST to succeed.
433 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
435 net::HTTP_NO_CONTENT,
436 net::URLRequestStatus::SUCCESS);
437 network_channel()->SendMessage("abra.cadabra");
438 EXPECT_FALSE(delegate()->request_token_callback.is_null());
439 delegate()->request_token_callback.Run(
440 GoogleServiceAuthError::AuthErrorNone(), "access.token");
441 RunLoopUntilIdle();
442 EXPECT_EQ(url_fetchers_created_count(), 2);
443 // Successful post should set invalidator state to enabled.
444 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
445 // Network changed event shouldn't affect invalidator state.
446 network_channel()->OnNetworkChanged(
447 net::NetworkChangeNotifier::CONNECTION_NONE);
448 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
450 // GCM connection state should affect invalidator state.
451 delegate()->connection_state_callback.Run(false);
452 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
453 delegate()->connection_state_callback.Run(true);
454 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
457 #if !defined(OS_ANDROID)
458 TEST_F(GCMNetworkChannelTest, BuildUrl) {
459 GURL url = BuildUrl("registration.id");
460 EXPECT_TRUE(url.SchemeIsHTTPOrHTTPS());
461 EXPECT_FALSE(url.host().empty());
462 EXPECT_FALSE(url.path().empty());
463 std::vector<std::string> parts;
464 Tokenize(url.path(), "/", &parts);
465 std::string buffer;
466 EXPECT_TRUE(Base64DecodeURLSafe(parts[parts.size() - 1], &buffer));
469 TEST_F(GCMNetworkChannelTest, EchoToken) {
470 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
471 std::string(),
472 net::HTTP_OK,
473 net::URLRequestStatus::SUCCESS);
474 // After construction GCMNetworkChannel should have called Register.
475 // Return valid registration id.
476 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
478 network_channel()->SendMessage("abra.cadabra");
479 // Return valid access token. This should trigger HTTP request.
480 delegate()->request_token_callback.Run(
481 GoogleServiceAuthError::AuthErrorNone(), "access.token");
482 RunLoopUntilIdle();
483 EXPECT_EQ(url_fetchers_created_count(), 1);
484 EXPECT_TRUE(get_last_echo_token().empty());
486 // Trigger response.
487 delegate()->message_callback.Run("abra.cadabra", "echo.token");
488 // Send another message.
489 network_channel()->SendMessage("abra.cadabra");
490 // Return valid access token. This should trigger HTTP request.
491 delegate()->request_token_callback.Run(
492 GoogleServiceAuthError::AuthErrorNone(), "access.token");
493 RunLoopUntilIdle();
494 EXPECT_EQ(url_fetchers_created_count(), 2);
495 EXPECT_EQ("echo.token", get_last_echo_token());
497 // Trigger response with empty echo token.
498 delegate()->message_callback.Run("abra.cadabra", "");
499 // Send another message.
500 network_channel()->SendMessage("abra.cadabra");
501 // Return valid access token. This should trigger HTTP request.
502 delegate()->request_token_callback.Run(
503 GoogleServiceAuthError::AuthErrorNone(), "access.token");
504 RunLoopUntilIdle();
505 EXPECT_EQ(url_fetchers_created_count(), 3);
506 // Echo_token should be from second message.
507 EXPECT_EQ("echo.token", get_last_echo_token());
509 #endif
511 } // namespace syncer