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"
15 class TestGCMNetworkChannelDelegate
: public GCMNetworkChannelDelegate
{
17 TestGCMNetworkChannelDelegate()
18 : register_call_count_(0) {}
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.
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.
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.
73 // Don't use initial delay unless the last request was an error.
77 class TestGCMNetworkChannel
: public GCMNetworkChannel
{
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
);
87 // On Android GCMNetworkChannel::BuildUrl hits NOTREACHED(). I still want
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
{
100 TestNetworkChannelURLFetcher(GCMNetworkChannelTest
* test
,
102 net::URLFetcherDelegate
* delegate
,
103 const std::string
& response_data
,
104 net::HttpStatusCode response_code
,
105 net::URLRequestStatus::Status status
)
106 : net::FakeURLFetcher(url
,
113 void AddExtraRequestHeader(const std::string
& header_line
) override
;
116 GCMNetworkChannelTest
* test_
;
119 class GCMNetworkChannelTest
120 : public ::testing::Test
,
121 public SyncNetworkChannel::Observer
{
123 GCMNetworkChannelTest()
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
135 delegate_
= new TestGCMNetworkChannelDelegate();
136 scoped_ptr
<GCMNetworkChannelDelegate
> delegate(delegate_
);
137 gcm_network_channel_
.reset(new TestGCMNetworkChannel(
138 request_context_getter_
,
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() {
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(
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();
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"),
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
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");
266 EXPECT_EQ(url_fetchers_created_count(), 1);
268 // Return another access token. Message should be cleared by now and shouldn't
270 delegate()->request_token_callback
.Run(
271 GoogleServiceAuthError::AuthErrorNone(), "access.token2");
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
);
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::NOT_SIGNED_IN
);
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");
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
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
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");
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
382 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
385 TEST_F(GCMNetworkChannelTest
, Base64EncodeDecode
) {
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 '-', '_'.
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());
415 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
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");
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");
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
);
466 EXPECT_TRUE(Base64DecodeURLSafe(parts
[parts
.size() - 1], &buffer
));
469 TEST_F(GCMNetworkChannelTest
, EchoToken
) {
470 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
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");
483 EXPECT_EQ(url_fetchers_created_count(), 1);
484 EXPECT_TRUE(get_last_echo_token().empty());
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");
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");
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());
511 } // namespace syncer