Cleanup changes in gcm_network_channel
[chromium-blink-merge.git] / sync / notifier / gcm_network_channel_unittest.cc
blob2564d0b9d2fd63230df0059d2580f0e3880b6e9a
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 "google_apis/gaia/google_service_auth_error.h"
8 #include "net/url_request/test_url_fetcher_factory.h"
9 #include "net/url_request/url_request_test_util.h"
10 #include "sync/notifier/gcm_network_channel.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 virtual void Initialize() OVERRIDE {}
22 virtual void RequestToken(RequestTokenCallback callback) OVERRIDE {
23 request_token_callback = callback;
26 virtual void InvalidateToken(const std::string& token) OVERRIDE {
27 invalidated_token = token;
30 virtual void Register(RegisterCallback callback) OVERRIDE {
31 ++register_call_count_;
32 register_callback = callback;
35 virtual void SetMessageReceiver(MessageCallback callback) OVERRIDE {
36 message_callback = callback;
39 RequestTokenCallback request_token_callback;
40 std::string invalidated_token;
41 RegisterCallback register_callback;
42 int register_call_count_;
43 MessageCallback message_callback;
46 // Backoff policy for test. Run first 5 retries without delay.
47 const net::BackoffEntry::Policy kTestBackoffPolicy = {
48 // Number of initial errors (in sequence) to ignore before applying
49 // exponential back-off rules.
52 // Initial delay for exponential back-off in ms.
53 2000, // 2 seconds.
55 // Factor by which the waiting time will be multiplied.
58 // Fuzzing percentage. ex: 10% will spread requests randomly
59 // between 90%-100% of the calculated time.
60 0.2, // 20%.
62 // Maximum amount of time we are willing to delay our request in ms.
63 1000 * 3600 * 4, // 4 hours.
65 // Time to keep an entry from being discarded even when it
66 // has no significant state, -1 to never discard.
67 -1,
69 // Don't use initial delay unless the last request was an error.
70 false,
73 class TestGCMNetworkChannel : public GCMNetworkChannel {
74 public:
75 TestGCMNetworkChannel(
76 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
77 scoped_ptr<GCMNetworkChannelDelegate> delegate)
78 : GCMNetworkChannel(request_context_getter, delegate.Pass()) {
79 ResetRegisterBackoffEntryForTest(&kTestBackoffPolicy);
82 protected:
83 // On Android GCMNetworkChannel::BuildUrl hits NOTREACHED(). I still want
84 // tests to run.
85 virtual GURL BuildUrl(const std::string& registration_id) OVERRIDE {
86 return GURL("http://test.url.com");
90 class GCMNetworkChannelTest;
92 // Test needs to capture setting echo-token header on http request.
93 // This class is going to do that.
94 class TestNetworkChannelURLFetcher : public net::FakeURLFetcher {
95 public:
96 TestNetworkChannelURLFetcher(GCMNetworkChannelTest* test,
97 const GURL& url,
98 net::URLFetcherDelegate* delegate,
99 const std::string& response_data,
100 net::HttpStatusCode response_code,
101 net::URLRequestStatus::Status status)
102 : net::FakeURLFetcher(url,
103 delegate,
104 response_data,
105 response_code,
106 status),
107 test_(test) {}
109 virtual void AddExtraRequestHeader(const std::string& header_line) OVERRIDE;
111 private:
112 GCMNetworkChannelTest* test_;
115 class GCMNetworkChannelTest
116 : public ::testing::Test,
117 public SyncNetworkChannel::Observer {
118 public:
119 GCMNetworkChannelTest()
120 : delegate_(NULL),
121 url_fetchers_created_count_(0),
122 last_invalidator_state_(TRANSIENT_INVALIDATION_ERROR) {}
124 virtual ~GCMNetworkChannelTest() {
127 virtual void SetUp() {
128 request_context_getter_ = new net::TestURLRequestContextGetter(
129 base::MessageLoopProxy::current());
130 // Ownership of delegate goes to GCNMentworkChannel but test needs pointer
131 // to it.
132 delegate_ = new TestGCMNetworkChannelDelegate();
133 scoped_ptr<GCMNetworkChannelDelegate> delegate(delegate_);
134 gcm_network_channel_.reset(new TestGCMNetworkChannel(
135 request_context_getter_,
136 delegate.Pass()));
137 gcm_network_channel_->AddObserver(this);
138 gcm_network_channel_->SetMessageReceiver(
139 invalidation::NewPermanentCallback(
140 this, &GCMNetworkChannelTest::OnIncomingMessage));
141 url_fetcher_factory_.reset(new net::FakeURLFetcherFactory(NULL,
142 base::Bind(&GCMNetworkChannelTest::CreateURLFetcher,
143 base::Unretained(this))));
146 virtual void TearDown() {
147 gcm_network_channel_->RemoveObserver(this);
150 // Helper functions to call private methods from test
151 GURL BuildUrl(const std::string& registration_id) {
152 return gcm_network_channel_->GCMNetworkChannel::BuildUrl(registration_id);
155 static void Base64EncodeURLSafe(const std::string& input,
156 std::string* output) {
157 GCMNetworkChannel::Base64EncodeURLSafe(input, output);
160 static bool Base64DecodeURLSafe(const std::string& input,
161 std::string* output) {
162 return GCMNetworkChannel::Base64DecodeURLSafe(input, output);
165 virtual void OnNetworkChannelStateChanged(
166 InvalidatorState invalidator_state) OVERRIDE {
167 last_invalidator_state_ = invalidator_state;
170 void OnIncomingMessage(std::string incoming_message) {
173 GCMNetworkChannel* network_channel() {
174 return gcm_network_channel_.get();
177 TestGCMNetworkChannelDelegate* delegate() {
178 return delegate_;
181 int url_fetchers_created_count() {
182 return url_fetchers_created_count_;
185 net::FakeURLFetcherFactory* url_fetcher_factory() {
186 return url_fetcher_factory_.get();
189 scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
190 const GURL& url,
191 net::URLFetcherDelegate* delegate,
192 const std::string& response_data,
193 net::HttpStatusCode response_code,
194 net::URLRequestStatus::Status status) {
195 ++url_fetchers_created_count_;
196 return scoped_ptr<net::FakeURLFetcher>(new TestNetworkChannelURLFetcher(
197 this, url, delegate, response_data, response_code, status));
200 void set_last_echo_token(const std::string& echo_token) {
201 last_echo_token_ = echo_token;
204 const std::string& get_last_echo_token() {
205 return last_echo_token_;
208 InvalidatorState get_last_invalidator_state() {
209 return last_invalidator_state_;
212 void RunLoopUntilIdle() {
213 base::RunLoop run_loop;
214 run_loop.RunUntilIdle();
217 private:
218 base::MessageLoop message_loop_;
219 TestGCMNetworkChannelDelegate* delegate_;
220 scoped_ptr<GCMNetworkChannel> gcm_network_channel_;
221 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
222 scoped_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
223 int url_fetchers_created_count_;
224 std::string last_echo_token_;
225 InvalidatorState last_invalidator_state_;
228 void TestNetworkChannelURLFetcher::AddExtraRequestHeader(
229 const std::string& header_line) {
230 net::FakeURLFetcher::AddExtraRequestHeader(header_line);
231 std::string header_name("echo-token: ");
232 std::string echo_token;
233 if (StartsWithASCII(header_line, header_name, false)) {
234 echo_token = header_line;
235 ReplaceFirstSubstringAfterOffset(
236 &echo_token, 0, header_name, std::string());
237 test_->set_last_echo_token(echo_token);
241 TEST_F(GCMNetworkChannelTest, HappyCase) {
242 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
243 EXPECT_FALSE(delegate()->message_callback.is_null());
244 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
245 std::string(),
246 net::HTTP_NO_CONTENT,
247 net::URLRequestStatus::SUCCESS);
249 // After construction GCMNetworkChannel should have called Register.
250 EXPECT_FALSE(delegate()->register_callback.is_null());
251 // Return valid registration id.
252 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
254 network_channel()->SendMessage("abra.cadabra");
255 // SendMessage should have triggered RequestToken. No HTTP request should be
256 // started yet.
257 EXPECT_FALSE(delegate()->request_token_callback.is_null());
258 EXPECT_EQ(url_fetchers_created_count(), 0);
259 // Return valid access token. This should trigger HTTP request.
260 delegate()->request_token_callback.Run(
261 GoogleServiceAuthError::AuthErrorNone(), "access.token");
262 RunLoopUntilIdle();
263 EXPECT_EQ(url_fetchers_created_count(), 1);
265 // Return another access token. Message should be cleared by now and shouldn't
266 // be sent.
267 delegate()->request_token_callback.Run(
268 GoogleServiceAuthError::AuthErrorNone(), "access.token2");
269 RunLoopUntilIdle();
270 EXPECT_EQ(url_fetchers_created_count(), 1);
271 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
274 TEST_F(GCMNetworkChannelTest, FailedRegister) {
275 // After construction GCMNetworkChannel should have called Register.
276 EXPECT_FALSE(delegate()->register_callback.is_null());
277 EXPECT_EQ(1, delegate()->register_call_count_);
278 // Return transient error from Register call.
279 delegate()->register_callback.Run("", gcm::GCMClient::NETWORK_ERROR);
280 RunLoopUntilIdle();
281 // GcmNetworkChannel should have scheduled Register retry.
282 EXPECT_EQ(2, delegate()->register_call_count_);
283 // Return persistent error from Register call.
284 delegate()->register_callback.Run("", gcm::GCMClient::NOT_SIGNED_IN);
285 RunLoopUntilIdle();
286 // GcmNetworkChannel should give up trying.
287 EXPECT_EQ(2, delegate()->register_call_count_);
289 network_channel()->SendMessage("abra.cadabra");
290 // SendMessage shouldn't trigger RequestToken.
291 EXPECT_TRUE(delegate()->request_token_callback.is_null());
292 EXPECT_EQ(0, url_fetchers_created_count());
295 TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) {
296 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
298 net::HTTP_NO_CONTENT,
299 net::URLRequestStatus::SUCCESS);
301 // After construction GCMNetworkChannel should have called Register.
302 EXPECT_FALSE(delegate()->register_callback.is_null());
304 network_channel()->SendMessage("abra.cadabra");
305 // SendMessage shouldn't trigger RequestToken.
306 EXPECT_TRUE(delegate()->request_token_callback.is_null());
307 EXPECT_EQ(url_fetchers_created_count(), 0);
309 // Return valid registration id.
310 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
312 EXPECT_FALSE(delegate()->request_token_callback.is_null());
313 EXPECT_EQ(url_fetchers_created_count(), 0);
314 // Return valid access token. This should trigger HTTP request.
315 delegate()->request_token_callback.Run(
316 GoogleServiceAuthError::AuthErrorNone(), "access.token");
317 RunLoopUntilIdle();
318 EXPECT_EQ(url_fetchers_created_count(), 1);
321 TEST_F(GCMNetworkChannelTest, RequestTokenFailure) {
322 // After construction GCMNetworkChannel should have called Register.
323 EXPECT_FALSE(delegate()->register_callback.is_null());
324 // Return valid registration id.
325 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
327 network_channel()->SendMessage("abra.cadabra");
328 // SendMessage should have triggered RequestToken. No HTTP request should be
329 // started yet.
330 EXPECT_FALSE(delegate()->request_token_callback.is_null());
331 EXPECT_EQ(url_fetchers_created_count(), 0);
332 // RequestToken returns failure.
333 delegate()->request_token_callback.Run(
334 GoogleServiceAuthError::FromConnectionError(1), "");
336 // Should be no HTTP requests.
337 EXPECT_EQ(url_fetchers_created_count(), 0);
340 TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) {
341 // Setup fake response to return AUTH_ERROR.
342 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
344 net::HTTP_UNAUTHORIZED,
345 net::URLRequestStatus::SUCCESS);
347 // After construction GCMNetworkChannel should have called Register.
348 EXPECT_FALSE(delegate()->register_callback.is_null());
349 // Return valid registration id.
350 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
352 network_channel()->SendMessage("abra.cadabra");
353 // SendMessage should have triggered RequestToken. No HTTP request should be
354 // started yet.
355 EXPECT_FALSE(delegate()->request_token_callback.is_null());
356 EXPECT_EQ(url_fetchers_created_count(), 0);
357 // Return valid access token. This should trigger HTTP request.
358 delegate()->request_token_callback.Run(
359 GoogleServiceAuthError::AuthErrorNone(), "access.token");
360 RunLoopUntilIdle();
361 EXPECT_EQ(url_fetchers_created_count(), 1);
362 EXPECT_EQ(delegate()->invalidated_token, "access.token");
365 // Following two tests are to check for memory leaks/crashes when Register and
366 // RequestToken don't complete by the teardown.
367 TEST_F(GCMNetworkChannelTest, RegisterNeverCompletes) {
368 network_channel()->SendMessage("abra.cadabra");
369 // Register should be called by now. Let's not complete and see what happens.
370 EXPECT_FALSE(delegate()->register_callback.is_null());
373 TEST_F(GCMNetworkChannelTest, RequestTokenNeverCompletes) {
374 network_channel()->SendMessage("abra.cadabra");
375 // Return valid registration id.
376 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
377 // RequestToken should be called by now. Let's not complete and see what
378 // happens.
379 EXPECT_FALSE(delegate()->request_token_callback.is_null());
382 TEST_F(GCMNetworkChannelTest, Base64EncodeDecode) {
383 std::string input;
384 std::string plain;
385 std::string base64;
386 // Empty string.
387 Base64EncodeURLSafe(input, &base64);
388 EXPECT_TRUE(base64.empty());
389 EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
390 EXPECT_EQ(input, plain);
391 // String length: 1..7.
392 for (int length = 1; length < 8; length++) {
393 input = "abra.cadabra";
394 input.resize(length);
395 Base64EncodeURLSafe(input, &base64);
396 // Ensure no padding at the end.
397 EXPECT_NE(base64[base64.size() - 1], '=');
398 EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
399 EXPECT_EQ(input, plain);
401 // Presence of '-', '_'.
402 input = "\xfb\xff";
403 Base64EncodeURLSafe(input, &base64);
404 EXPECT_EQ("-_8", base64);
405 EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
406 EXPECT_EQ(input, plain);
409 TEST_F(GCMNetworkChannelTest, TransientError) {
410 EXPECT_FALSE(delegate()->message_callback.is_null());
411 // POST will fail.
412 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
413 std::string(),
414 net::HTTP_SERVICE_UNAVAILABLE,
415 net::URLRequestStatus::SUCCESS);
417 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
419 network_channel()->SendMessage("abra.cadabra");
420 EXPECT_FALSE(delegate()->request_token_callback.is_null());
421 delegate()->request_token_callback.Run(
422 GoogleServiceAuthError::AuthErrorNone(), "access.token");
423 RunLoopUntilIdle();
424 EXPECT_EQ(url_fetchers_created_count(), 1);
425 // Failing HTTP POST should cause TRANSIENT_INVALIDATION_ERROR.
426 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
427 // Network change to CONNECTION_NONE shouldn't affect invalidator state.
428 network_channel()->OnNetworkChanged(
429 net::NetworkChangeNotifier::CONNECTION_NONE);
430 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
431 // Network change to something else should trigger retry.
432 network_channel()->OnNetworkChanged(
433 net::NetworkChangeNotifier::CONNECTION_WIFI);
434 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
435 network_channel()->OnNetworkChanged(
436 net::NetworkChangeNotifier::CONNECTION_NONE);
437 EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
440 #if !defined(OS_ANDROID)
441 TEST_F(GCMNetworkChannelTest, BuildUrl) {
442 GURL url = BuildUrl("registration.id");
443 EXPECT_TRUE(url.SchemeIsHTTPOrHTTPS());
444 EXPECT_FALSE(url.host().empty());
445 EXPECT_FALSE(url.path().empty());
446 std::vector<std::string> parts;
447 Tokenize(url.path(), "/", &parts);
448 std::string buffer;
449 EXPECT_TRUE(Base64DecodeURLSafe(parts[parts.size() - 1], &buffer));
452 TEST_F(GCMNetworkChannelTest, EchoToken) {
453 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
454 std::string(),
455 net::HTTP_OK,
456 net::URLRequestStatus::SUCCESS);
457 // After construction GCMNetworkChannel should have called Register.
458 // Return valid registration id.
459 delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
461 network_channel()->SendMessage("abra.cadabra");
462 // Return valid access token. This should trigger HTTP request.
463 delegate()->request_token_callback.Run(
464 GoogleServiceAuthError::AuthErrorNone(), "access.token");
465 RunLoopUntilIdle();
466 EXPECT_EQ(url_fetchers_created_count(), 1);
467 EXPECT_TRUE(get_last_echo_token().empty());
469 // Trigger response.
470 delegate()->message_callback.Run("abra.cadabra", "echo.token");
471 // Send another message.
472 network_channel()->SendMessage("abra.cadabra");
473 // Return valid access token. This should trigger HTTP request.
474 delegate()->request_token_callback.Run(
475 GoogleServiceAuthError::AuthErrorNone(), "access.token");
476 RunLoopUntilIdle();
477 EXPECT_EQ(url_fetchers_created_count(), 2);
478 EXPECT_EQ("echo.token", get_last_echo_token());
480 // Trigger response with empty echo token.
481 delegate()->message_callback.Run("abra.cadabra", "");
482 // Send another message.
483 network_channel()->SendMessage("abra.cadabra");
484 // Return valid access token. This should trigger HTTP request.
485 delegate()->request_token_callback.Run(
486 GoogleServiceAuthError::AuthErrorNone(), "access.token");
487 RunLoopUntilIdle();
488 EXPECT_EQ(url_fetchers_created_count(), 3);
489 // Echo_token should be from second message.
490 EXPECT_EQ("echo.token", get_last_echo_token());
492 #endif
494 } // namespace syncer