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) {}
20 virtual void Initialize(
21 GCMNetworkChannelDelegate::ConnectionStateCallback callback
) OVERRIDE
{
22 connection_state_callback
= callback
;
25 virtual void RequestToken(RequestTokenCallback callback
) OVERRIDE
{
26 request_token_callback
= callback
;
29 virtual void InvalidateToken(const std::string
& token
) OVERRIDE
{
30 invalidated_token
= token
;
33 virtual void Register(RegisterCallback callback
) OVERRIDE
{
34 ++register_call_count_
;
35 register_callback
= callback
;
38 virtual 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 virtual 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 virtual 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 virtual ~GCMNetworkChannelTest() {
131 virtual void SetUp() {
132 request_context_getter_
= new net::TestURLRequestContextGetter(
133 base::MessageLoopProxy::current());
134 // Ownership of delegate goes to GCNMentworkChannel but test needs pointer
136 delegate_
= new TestGCMNetworkChannelDelegate();
137 scoped_ptr
<GCMNetworkChannelDelegate
> delegate(delegate_
);
138 gcm_network_channel_
.reset(new TestGCMNetworkChannel(
139 request_context_getter_
,
141 gcm_network_channel_
->AddObserver(this);
142 gcm_network_channel_
->SetMessageReceiver(
143 invalidation::NewPermanentCallback(
144 this, &GCMNetworkChannelTest::OnIncomingMessage
));
145 url_fetcher_factory_
.reset(new net::FakeURLFetcherFactory(NULL
,
146 base::Bind(&GCMNetworkChannelTest::CreateURLFetcher
,
147 base::Unretained(this))));
150 virtual void TearDown() {
151 gcm_network_channel_
->RemoveObserver(this);
154 // Helper functions to call private methods from test
155 GURL
BuildUrl(const std::string
& registration_id
) {
156 return gcm_network_channel_
->GCMNetworkChannel::BuildUrl(registration_id
);
159 static void Base64EncodeURLSafe(const std::string
& input
,
160 std::string
* output
) {
161 GCMNetworkChannel::Base64EncodeURLSafe(input
, output
);
164 static bool Base64DecodeURLSafe(const std::string
& input
,
165 std::string
* output
) {
166 return GCMNetworkChannel::Base64DecodeURLSafe(input
, output
);
169 virtual void OnNetworkChannelStateChanged(
170 InvalidatorState invalidator_state
) OVERRIDE
{
171 last_invalidator_state_
= invalidator_state
;
174 void OnIncomingMessage(std::string incoming_message
) {
177 GCMNetworkChannel
* network_channel() {
178 return gcm_network_channel_
.get();
181 TestGCMNetworkChannelDelegate
* delegate() {
185 int url_fetchers_created_count() {
186 return url_fetchers_created_count_
;
189 net::FakeURLFetcherFactory
* url_fetcher_factory() {
190 return url_fetcher_factory_
.get();
193 scoped_ptr
<net::FakeURLFetcher
> CreateURLFetcher(
195 net::URLFetcherDelegate
* delegate
,
196 const std::string
& response_data
,
197 net::HttpStatusCode response_code
,
198 net::URLRequestStatus::Status status
) {
199 ++url_fetchers_created_count_
;
200 return scoped_ptr
<net::FakeURLFetcher
>(new TestNetworkChannelURLFetcher(
201 this, url
, delegate
, response_data
, response_code
, status
));
204 void set_last_echo_token(const std::string
& echo_token
) {
205 last_echo_token_
= echo_token
;
208 const std::string
& get_last_echo_token() {
209 return last_echo_token_
;
212 InvalidatorState
get_last_invalidator_state() {
213 return last_invalidator_state_
;
216 void RunLoopUntilIdle() {
217 base::RunLoop run_loop
;
218 run_loop
.RunUntilIdle();
222 base::MessageLoop message_loop_
;
223 TestGCMNetworkChannelDelegate
* delegate_
;
224 scoped_ptr
<GCMNetworkChannel
> gcm_network_channel_
;
225 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
226 scoped_ptr
<net::FakeURLFetcherFactory
> url_fetcher_factory_
;
227 int url_fetchers_created_count_
;
228 std::string last_echo_token_
;
229 InvalidatorState last_invalidator_state_
;
232 void TestNetworkChannelURLFetcher::AddExtraRequestHeader(
233 const std::string
& header_line
) {
234 net::FakeURLFetcher::AddExtraRequestHeader(header_line
);
235 std::string
header_name("echo-token: ");
236 std::string echo_token
;
237 if (StartsWithASCII(header_line
, header_name
, false)) {
238 echo_token
= header_line
;
239 ReplaceFirstSubstringAfterOffset(
240 &echo_token
, 0, header_name
, std::string());
241 test_
->set_last_echo_token(echo_token
);
245 TEST_F(GCMNetworkChannelTest
, HappyCase
) {
246 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR
, get_last_invalidator_state());
247 EXPECT_FALSE(delegate()->message_callback
.is_null());
248 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
250 net::HTTP_NO_CONTENT
,
251 net::URLRequestStatus::SUCCESS
);
253 // Emulate gcm connection state to be online.
254 delegate()->connection_state_callback
.Run(true);
255 // After construction GCMNetworkChannel should have called Register.
256 EXPECT_FALSE(delegate()->register_callback
.is_null());
257 // Return valid registration id.
258 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
260 network_channel()->SendMessage("abra.cadabra");
261 // SendMessage should have triggered RequestToken. No HTTP request should be
263 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
264 EXPECT_EQ(url_fetchers_created_count(), 0);
265 // Return valid access token. This should trigger HTTP request.
266 delegate()->request_token_callback
.Run(
267 GoogleServiceAuthError::AuthErrorNone(), "access.token");
269 EXPECT_EQ(url_fetchers_created_count(), 1);
271 // Return another access token. Message should be cleared by now and shouldn't
273 delegate()->request_token_callback
.Run(
274 GoogleServiceAuthError::AuthErrorNone(), "access.token2");
276 EXPECT_EQ(url_fetchers_created_count(), 1);
277 EXPECT_EQ(INVALIDATIONS_ENABLED
, get_last_invalidator_state());
280 TEST_F(GCMNetworkChannelTest
, FailedRegister
) {
281 // After construction GCMNetworkChannel should have called Register.
282 EXPECT_FALSE(delegate()->register_callback
.is_null());
283 EXPECT_EQ(1, delegate()->register_call_count_
);
284 // Return transient error from Register call.
285 delegate()->register_callback
.Run("", gcm::GCMClient::NETWORK_ERROR
);
287 // GcmNetworkChannel should have scheduled Register retry.
288 EXPECT_EQ(2, delegate()->register_call_count_
);
289 // Return persistent error from Register call.
290 delegate()->register_callback
.Run("", gcm::GCMClient::NOT_SIGNED_IN
);
292 // GcmNetworkChannel should give up trying.
293 EXPECT_EQ(2, delegate()->register_call_count_
);
295 network_channel()->SendMessage("abra.cadabra");
296 // SendMessage shouldn't trigger RequestToken.
297 EXPECT_TRUE(delegate()->request_token_callback
.is_null());
298 EXPECT_EQ(0, url_fetchers_created_count());
301 TEST_F(GCMNetworkChannelTest
, RegisterFinishesAfterSendMessage
) {
302 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
304 net::HTTP_NO_CONTENT
,
305 net::URLRequestStatus::SUCCESS
);
307 // After construction GCMNetworkChannel should have called Register.
308 EXPECT_FALSE(delegate()->register_callback
.is_null());
310 network_channel()->SendMessage("abra.cadabra");
311 // SendMessage shouldn't trigger RequestToken.
312 EXPECT_TRUE(delegate()->request_token_callback
.is_null());
313 EXPECT_EQ(url_fetchers_created_count(), 0);
315 // Return valid registration id.
316 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
318 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
319 EXPECT_EQ(url_fetchers_created_count(), 0);
320 // Return valid access token. This should trigger HTTP request.
321 delegate()->request_token_callback
.Run(
322 GoogleServiceAuthError::AuthErrorNone(), "access.token");
324 EXPECT_EQ(url_fetchers_created_count(), 1);
327 TEST_F(GCMNetworkChannelTest
, RequestTokenFailure
) {
328 // After construction GCMNetworkChannel should have called Register.
329 EXPECT_FALSE(delegate()->register_callback
.is_null());
330 // Return valid registration id.
331 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
333 network_channel()->SendMessage("abra.cadabra");
334 // SendMessage should have triggered RequestToken. No HTTP request should be
336 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
337 EXPECT_EQ(url_fetchers_created_count(), 0);
338 // RequestToken returns failure.
339 delegate()->request_token_callback
.Run(
340 GoogleServiceAuthError::FromConnectionError(1), "");
342 // Should be no HTTP requests.
343 EXPECT_EQ(url_fetchers_created_count(), 0);
346 TEST_F(GCMNetworkChannelTest
, AuthErrorFromServer
) {
347 // Setup fake response to return AUTH_ERROR.
348 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
350 net::HTTP_UNAUTHORIZED
,
351 net::URLRequestStatus::SUCCESS
);
353 // After construction GCMNetworkChannel should have called Register.
354 EXPECT_FALSE(delegate()->register_callback
.is_null());
355 // Return valid registration id.
356 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
358 network_channel()->SendMessage("abra.cadabra");
359 // SendMessage should have triggered RequestToken. No HTTP request should be
361 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
362 EXPECT_EQ(url_fetchers_created_count(), 0);
363 // Return valid access token. This should trigger HTTP request.
364 delegate()->request_token_callback
.Run(
365 GoogleServiceAuthError::AuthErrorNone(), "access.token");
367 EXPECT_EQ(url_fetchers_created_count(), 1);
368 EXPECT_EQ(delegate()->invalidated_token
, "access.token");
371 // Following two tests are to check for memory leaks/crashes when Register and
372 // RequestToken don't complete by the teardown.
373 TEST_F(GCMNetworkChannelTest
, RegisterNeverCompletes
) {
374 network_channel()->SendMessage("abra.cadabra");
375 // Register should be called by now. Let's not complete and see what happens.
376 EXPECT_FALSE(delegate()->register_callback
.is_null());
379 TEST_F(GCMNetworkChannelTest
, RequestTokenNeverCompletes
) {
380 network_channel()->SendMessage("abra.cadabra");
381 // Return valid registration id.
382 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
383 // RequestToken should be called by now. Let's not complete and see what
385 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
388 TEST_F(GCMNetworkChannelTest
, Base64EncodeDecode
) {
393 Base64EncodeURLSafe(input
, &base64
);
394 EXPECT_TRUE(base64
.empty());
395 EXPECT_TRUE(Base64DecodeURLSafe(base64
, &plain
));
396 EXPECT_EQ(input
, plain
);
397 // String length: 1..7.
398 for (int length
= 1; length
< 8; length
++) {
399 input
= "abra.cadabra";
400 input
.resize(length
);
401 Base64EncodeURLSafe(input
, &base64
);
402 // Ensure no padding at the end.
403 EXPECT_NE(base64
[base64
.size() - 1], '=');
404 EXPECT_TRUE(Base64DecodeURLSafe(base64
, &plain
));
405 EXPECT_EQ(input
, plain
);
407 // Presence of '-', '_'.
409 Base64EncodeURLSafe(input
, &base64
);
410 EXPECT_EQ("-_8", base64
);
411 EXPECT_TRUE(Base64DecodeURLSafe(base64
, &plain
));
412 EXPECT_EQ(input
, plain
);
415 TEST_F(GCMNetworkChannelTest
, ChannelState
) {
416 EXPECT_FALSE(delegate()->message_callback
.is_null());
418 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
420 net::HTTP_SERVICE_UNAVAILABLE
,
421 net::URLRequestStatus::SUCCESS
);
423 delegate()->connection_state_callback
.Run(true);
424 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
426 network_channel()->SendMessage("abra.cadabra");
427 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
428 delegate()->request_token_callback
.Run(
429 GoogleServiceAuthError::AuthErrorNone(), "access.token");
431 EXPECT_EQ(url_fetchers_created_count(), 1);
432 // Failing HTTP POST should cause TRANSIENT_INVALIDATION_ERROR.
433 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR
, get_last_invalidator_state());
435 // Setup POST to succeed.
436 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
438 net::HTTP_NO_CONTENT
,
439 net::URLRequestStatus::SUCCESS
);
440 network_channel()->SendMessage("abra.cadabra");
441 EXPECT_FALSE(delegate()->request_token_callback
.is_null());
442 delegate()->request_token_callback
.Run(
443 GoogleServiceAuthError::AuthErrorNone(), "access.token");
445 EXPECT_EQ(url_fetchers_created_count(), 2);
446 // Successful post should set invalidator state to enabled.
447 EXPECT_EQ(INVALIDATIONS_ENABLED
, get_last_invalidator_state());
448 // Network changed event shouldn't affect invalidator state.
449 network_channel()->OnNetworkChanged(
450 net::NetworkChangeNotifier::CONNECTION_NONE
);
451 EXPECT_EQ(INVALIDATIONS_ENABLED
, get_last_invalidator_state());
453 // GCM connection state should affect invalidator state.
454 delegate()->connection_state_callback
.Run(false);
455 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR
, get_last_invalidator_state());
456 delegate()->connection_state_callback
.Run(true);
457 EXPECT_EQ(INVALIDATIONS_ENABLED
, get_last_invalidator_state());
460 #if !defined(OS_ANDROID)
461 TEST_F(GCMNetworkChannelTest
, BuildUrl
) {
462 GURL url
= BuildUrl("registration.id");
463 EXPECT_TRUE(url
.SchemeIsHTTPOrHTTPS());
464 EXPECT_FALSE(url
.host().empty());
465 EXPECT_FALSE(url
.path().empty());
466 std::vector
<std::string
> parts
;
467 Tokenize(url
.path(), "/", &parts
);
469 EXPECT_TRUE(Base64DecodeURLSafe(parts
[parts
.size() - 1], &buffer
));
472 TEST_F(GCMNetworkChannelTest
, EchoToken
) {
473 url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
476 net::URLRequestStatus::SUCCESS
);
477 // After construction GCMNetworkChannel should have called Register.
478 // Return valid registration id.
479 delegate()->register_callback
.Run("registration.id", gcm::GCMClient::SUCCESS
);
481 network_channel()->SendMessage("abra.cadabra");
482 // Return valid access token. This should trigger HTTP request.
483 delegate()->request_token_callback
.Run(
484 GoogleServiceAuthError::AuthErrorNone(), "access.token");
486 EXPECT_EQ(url_fetchers_created_count(), 1);
487 EXPECT_TRUE(get_last_echo_token().empty());
490 delegate()->message_callback
.Run("abra.cadabra", "echo.token");
491 // Send another message.
492 network_channel()->SendMessage("abra.cadabra");
493 // Return valid access token. This should trigger HTTP request.
494 delegate()->request_token_callback
.Run(
495 GoogleServiceAuthError::AuthErrorNone(), "access.token");
497 EXPECT_EQ(url_fetchers_created_count(), 2);
498 EXPECT_EQ("echo.token", get_last_echo_token());
500 // Trigger response with empty echo token.
501 delegate()->message_callback
.Run("abra.cadabra", "");
502 // Send another message.
503 network_channel()->SendMessage("abra.cadabra");
504 // Return valid access token. This should trigger HTTP request.
505 delegate()->request_token_callback
.Run(
506 GoogleServiceAuthError::AuthErrorNone(), "access.token");
508 EXPECT_EQ(url_fetchers_created_count(), 3);
509 // Echo_token should be from second message.
510 EXPECT_EQ("echo.token", get_last_echo_token());
514 } // namespace syncer