Home | History | Annotate | Download | only in invalidation
      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.
      4 
      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"
     12 
     13 namespace syncer {
     14 
     15 class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate {
     16  public:
     17   TestGCMNetworkChannelDelegate()
     18       : register_call_count_(0) {}
     19 
     20   virtual void Initialize(
     21       GCMNetworkChannelDelegate::ConnectionStateCallback callback) OVERRIDE {
     22     connection_state_callback = callback;
     23   }
     24 
     25   virtual void RequestToken(RequestTokenCallback callback) OVERRIDE {
     26     request_token_callback = callback;
     27   }
     28 
     29   virtual void InvalidateToken(const std::string& token) OVERRIDE {
     30     invalidated_token = token;
     31   }
     32 
     33   virtual void Register(RegisterCallback callback) OVERRIDE {
     34     ++register_call_count_;
     35     register_callback = callback;
     36   }
     37 
     38   virtual void SetMessageReceiver(MessageCallback callback) OVERRIDE {
     39     message_callback = callback;
     40   }
     41 
     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;
     48 };
     49 
     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.
     54   5,
     55 
     56   // Initial delay for exponential back-off in ms.
     57   2000, // 2 seconds.
     58 
     59   // Factor by which the waiting time will be multiplied.
     60   2,
     61 
     62   // Fuzzing percentage. ex: 10% will spread requests randomly
     63   // between 90%-100% of the calculated time.
     64   0.2, // 20%.
     65 
     66   // Maximum amount of time we are willing to delay our request in ms.
     67   1000 * 3600 * 4, // 4 hours.
     68 
     69   // Time to keep an entry from being discarded even when it
     70   // has no significant state, -1 to never discard.
     71   -1,
     72 
     73   // Don't use initial delay unless the last request was an error.
     74   false,
     75 };
     76 
     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);
     84   }
     85 
     86  protected:
     87   // On Android GCMNetworkChannel::BuildUrl hits NOTREACHED(). I still want
     88   // tests to run.
     89   virtual GURL BuildUrl(const std::string& registration_id) OVERRIDE {
     90     return GURL("http://test.url.com");
     91   }
     92 };
     93 
     94 class GCMNetworkChannelTest;
     95 
     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) {}
    112 
    113   virtual void AddExtraRequestHeader(const std::string& header_line) OVERRIDE;
    114 
    115  private:
    116   GCMNetworkChannelTest* test_;
    117 };
    118 
    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) {}
    127 
    128   virtual ~GCMNetworkChannelTest() {
    129   }
    130 
    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
    135     // to it.
    136     delegate_ = new TestGCMNetworkChannelDelegate();
    137     scoped_ptr<GCMNetworkChannelDelegate> delegate(delegate_);
    138     gcm_network_channel_.reset(new TestGCMNetworkChannel(
    139         request_context_getter_,
    140         delegate.Pass()));
    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))));
    148   }
    149 
    150   virtual void TearDown() {
    151     gcm_network_channel_->RemoveObserver(this);
    152   }
    153 
    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);
    157   }
    158 
    159   static void Base64EncodeURLSafe(const std::string& input,
    160                                   std::string* output) {
    161     GCMNetworkChannel::Base64EncodeURLSafe(input, output);
    162   }
    163 
    164   static bool Base64DecodeURLSafe(const std::string& input,
    165                                   std::string* output) {
    166     return GCMNetworkChannel::Base64DecodeURLSafe(input, output);
    167   }
    168 
    169   virtual void OnNetworkChannelStateChanged(
    170       InvalidatorState invalidator_state) OVERRIDE {
    171     last_invalidator_state_ = invalidator_state;
    172   }
    173 
    174   void OnIncomingMessage(std::string incoming_message) {
    175   }
    176 
    177   GCMNetworkChannel* network_channel() {
    178     return gcm_network_channel_.get();
    179   }
    180 
    181   TestGCMNetworkChannelDelegate* delegate() {
    182     return delegate_;
    183   }
    184 
    185   int url_fetchers_created_count() {
    186     return url_fetchers_created_count_;
    187   }
    188 
    189   net::FakeURLFetcherFactory* url_fetcher_factory() {
    190     return url_fetcher_factory_.get();
    191   }
    192 
    193   scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
    194       const GURL& url,
    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));
    202   }
    203 
    204   void set_last_echo_token(const std::string& echo_token) {
    205     last_echo_token_ = echo_token;
    206   }
    207 
    208   const std::string& get_last_echo_token() {
    209     return last_echo_token_;
    210   }
    211 
    212   InvalidatorState get_last_invalidator_state() {
    213     return last_invalidator_state_;
    214   }
    215 
    216   void RunLoopUntilIdle() {
    217     base::RunLoop run_loop;
    218     run_loop.RunUntilIdle();
    219   }
    220 
    221  private:
    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_;
    230 };
    231 
    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);
    242   }
    243 }
    244 
    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"),
    249                                          std::string(),
    250                                          net::HTTP_NO_CONTENT,
    251                                          net::URLRequestStatus::SUCCESS);
    252 
    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);
    259 
    260   network_channel()->SendMessage("abra.cadabra");
    261   // SendMessage should have triggered RequestToken. No HTTP request should be
    262   // started yet.
    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");
    268   RunLoopUntilIdle();
    269   EXPECT_EQ(url_fetchers_created_count(), 1);
    270 
    271   // Return another access token. Message should be cleared by now and shouldn't
    272   // be sent.
    273   delegate()->request_token_callback.Run(
    274       GoogleServiceAuthError::AuthErrorNone(), "access.token2");
    275   RunLoopUntilIdle();
    276   EXPECT_EQ(url_fetchers_created_count(), 1);
    277   EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
    278 }
    279 
    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);
    286   RunLoopUntilIdle();
    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);
    291   RunLoopUntilIdle();
    292   // GcmNetworkChannel should give up trying.
    293   EXPECT_EQ(2, delegate()->register_call_count_);
    294 
    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());
    299 }
    300 
    301 TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) {
    302   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
    303                                          "",
    304                                          net::HTTP_NO_CONTENT,
    305                                          net::URLRequestStatus::SUCCESS);
    306 
    307   // After construction GCMNetworkChannel should have called Register.
    308   EXPECT_FALSE(delegate()->register_callback.is_null());
    309 
    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);
    314 
    315   // Return valid registration id.
    316   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
    317 
    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");
    323   RunLoopUntilIdle();
    324   EXPECT_EQ(url_fetchers_created_count(), 1);
    325 }
    326 
    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);
    332 
    333   network_channel()->SendMessage("abra.cadabra");
    334   // SendMessage should have triggered RequestToken. No HTTP request should be
    335   // started yet.
    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), "");
    341 
    342   // Should be no HTTP requests.
    343   EXPECT_EQ(url_fetchers_created_count(), 0);
    344 }
    345 
    346 TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) {
    347   // Setup fake response to return AUTH_ERROR.
    348   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
    349                                          "",
    350                                          net::HTTP_UNAUTHORIZED,
    351                                          net::URLRequestStatus::SUCCESS);
    352 
    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);
    357 
    358   network_channel()->SendMessage("abra.cadabra");
    359   // SendMessage should have triggered RequestToken. No HTTP request should be
    360   // started yet.
    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");
    366   RunLoopUntilIdle();
    367   EXPECT_EQ(url_fetchers_created_count(), 1);
    368   EXPECT_EQ(delegate()->invalidated_token, "access.token");
    369 }
    370 
    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());
    377 }
    378 
    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
    384   // happens.
    385   EXPECT_FALSE(delegate()->request_token_callback.is_null());
    386 }
    387 
    388 TEST_F(GCMNetworkChannelTest, Base64EncodeDecode) {
    389   std::string input;
    390   std::string plain;
    391   std::string base64;
    392   // Empty string.
    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);
    406   }
    407   // Presence of '-', '_'.
    408   input = "\xfb\xff";
    409   Base64EncodeURLSafe(input, &base64);
    410   EXPECT_EQ("-_8", base64);
    411   EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
    412   EXPECT_EQ(input, plain);
    413 }
    414 
    415 TEST_F(GCMNetworkChannelTest, ChannelState) {
    416   EXPECT_FALSE(delegate()->message_callback.is_null());
    417   // POST will fail.
    418   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
    419                                          std::string(),
    420                                          net::HTTP_SERVICE_UNAVAILABLE,
    421                                          net::URLRequestStatus::SUCCESS);
    422 
    423   delegate()->connection_state_callback.Run(true);
    424   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
    425 
    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");
    430   RunLoopUntilIdle();
    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());
    434 
    435   // Setup POST to succeed.
    436   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
    437                                          "",
    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");
    444   RunLoopUntilIdle();
    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());
    452 
    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());
    458 }
    459 
    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);
    468   std::string buffer;
    469   EXPECT_TRUE(Base64DecodeURLSafe(parts[parts.size() - 1], &buffer));
    470 }
    471 
    472 TEST_F(GCMNetworkChannelTest, EchoToken) {
    473   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
    474                                          std::string(),
    475                                          net::HTTP_OK,
    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);
    480 
    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");
    485   RunLoopUntilIdle();
    486   EXPECT_EQ(url_fetchers_created_count(), 1);
    487   EXPECT_TRUE(get_last_echo_token().empty());
    488 
    489   // Trigger response.
    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");
    496   RunLoopUntilIdle();
    497   EXPECT_EQ(url_fetchers_created_count(), 2);
    498   EXPECT_EQ("echo.token", get_last_echo_token());
    499 
    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");
    507   RunLoopUntilIdle();
    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());
    511 }
    512 #endif
    513 
    514 }  // namespace syncer
    515