Home | History | Annotate | Download | only in notifier
      1 // Copyright (c) 2011 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 <string>
      6 
      7 #include "base/message_loop.h"
      8 #include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
      9 #include "chrome/browser/sync/notifier/state_writer.h"
     10 #include "chrome/browser/sync/syncable/model_type.h"
     11 #include "chrome/browser/sync/syncable/model_type_payload_map.h"
     12 #include "jingle/notifier/base/fake_base_task.h"
     13 #include "testing/gmock/include/gmock/gmock.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 namespace sync_notifier {
     17 
     18 using ::testing::_;
     19 using ::testing::Return;
     20 using ::testing::StrictMock;
     21 
     22 namespace {
     23 
     24 const char kClientId[] = "client_id";
     25 const char kClientInfo[] = "client_info";
     26 const char kState[] = "state";
     27 
     28 static const int64 kUnknownVersion =
     29     invalidation::InvalidationListener::UNKNOWN_OBJECT_VERSION;
     30 
     31 class MockListener : public ChromeInvalidationClient::Listener {
     32  public:
     33   MOCK_METHOD1(OnInvalidate, void(const syncable::ModelTypePayloadMap&));
     34   MOCK_METHOD1(OnSessionStatusChanged, void(bool));
     35 };
     36 
     37 class MockStateWriter : public StateWriter {
     38  public:
     39   MOCK_METHOD1(WriteState, void(const std::string&));
     40 };
     41 
     42 class MockCallback {
     43  public:
     44   MOCK_METHOD0(Run, void());
     45 
     46   invalidation::Closure* MakeClosure() {
     47     return invalidation::NewPermanentCallback(this, &MockCallback::Run);
     48   }
     49 };
     50 
     51 }  // namespace
     52 
     53 class ChromeInvalidationClientTest : public testing::Test {
     54  protected:
     55   virtual void SetUp() {
     56     client_.Start(kClientId, kClientInfo, kState,
     57                   &mock_listener_, &mock_state_writer_,
     58                   fake_base_task_.AsWeakPtr());
     59   }
     60 
     61   virtual void TearDown() {
     62     client_.Stop();
     63     message_loop_.RunAllPending();
     64   }
     65 
     66   // Simulates DoInformOutboundListener() from network-manager.cc.
     67   void SimulateInformOutboundListener() {
     68     // Explicitness hack here to work around broken callback
     69     // implementations.
     70     void (invalidation::NetworkCallback::*run_function)(
     71         invalidation::NetworkEndpoint* const&) =
     72         &invalidation::NetworkCallback::Run;
     73 
     74     client_.chrome_system_resources_.ScheduleOnListenerThread(
     75         invalidation::NewPermanentCallback(
     76             client_.handle_outbound_packet_callback_.get(), run_function,
     77             client_.invalidation_client_->network_endpoint()));
     78   }
     79 
     80   // |payload| can be NULL, but not |type_name|.
     81   void FireInvalidate(const char* type_name,
     82                       int64 version, const char* payload) {
     83     const invalidation::ObjectId object_id(
     84         invalidation::ObjectSource::CHROME_SYNC, type_name);
     85     std::string payload_tmp = payload ? payload : "";
     86     const invalidation::Invalidation invalidation(
     87         object_id, version, payload ? &payload_tmp : NULL, NULL);
     88     MockCallback mock_callback;
     89     EXPECT_CALL(mock_callback, Run());
     90     client_.Invalidate(invalidation, mock_callback.MakeClosure());
     91   }
     92 
     93   void FireInvalidateAll() {
     94     MockCallback mock_callback;
     95     EXPECT_CALL(mock_callback, Run());
     96     client_.InvalidateAll(mock_callback.MakeClosure());
     97   }
     98 
     99   MessageLoop message_loop_;
    100   StrictMock<MockListener> mock_listener_;
    101   StrictMock<MockStateWriter> mock_state_writer_;
    102   notifier::FakeBaseTask fake_base_task_;
    103   ChromeInvalidationClient client_;
    104 };
    105 
    106 namespace {
    107 
    108 syncable::ModelTypePayloadMap MakeMap(syncable::ModelType model_type,
    109                                       const std::string& payload) {
    110   syncable::ModelTypePayloadMap type_payloads;
    111   type_payloads[model_type] = payload;
    112   return type_payloads;
    113 }
    114 
    115 syncable::ModelTypePayloadMap MakeMapFromSet(syncable::ModelTypeSet types,
    116                                              const std::string& payload) {
    117   syncable::ModelTypePayloadMap type_payloads;
    118   for (syncable::ModelTypeSet::const_iterator it = types.begin();
    119        it != types.end(); ++it) {
    120     type_payloads[*it] = payload;
    121   }
    122   return type_payloads;
    123 }
    124 
    125 }  // namespace
    126 
    127 TEST_F(ChromeInvalidationClientTest, InvalidateBadObjectId) {
    128   syncable::ModelTypeSet types;
    129   types.insert(syncable::BOOKMARKS);
    130   types.insert(syncable::APPS);
    131   client_.RegisterTypes(types);
    132   EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
    133   FireInvalidate("bad", 1, NULL);
    134 }
    135 
    136 TEST_F(ChromeInvalidationClientTest, InvalidateNoPayload) {
    137   EXPECT_CALL(mock_listener_,
    138               OnInvalidate(MakeMap(syncable::BOOKMARKS, "")));
    139   FireInvalidate("BOOKMARK", 1, NULL);
    140 }
    141 
    142 TEST_F(ChromeInvalidationClientTest, InvalidateWithPayload) {
    143   EXPECT_CALL(mock_listener_,
    144               OnInvalidate(MakeMap(syncable::PREFERENCES, "payload")));
    145   FireInvalidate("PREFERENCE", 1, "payload");
    146 }
    147 
    148 TEST_F(ChromeInvalidationClientTest, InvalidateVersion) {
    149   using ::testing::Mock;
    150 
    151   EXPECT_CALL(mock_listener_,
    152               OnInvalidate(MakeMap(syncable::APPS, "")));
    153 
    154   // Should trigger.
    155   FireInvalidate("APP", 1, NULL);
    156 
    157   Mock::VerifyAndClearExpectations(&mock_listener_);
    158 
    159   // Should be dropped.
    160   FireInvalidate("APP", 1, NULL);
    161 }
    162 
    163 TEST_F(ChromeInvalidationClientTest, InvalidateUnknownVersion) {
    164   EXPECT_CALL(mock_listener_,
    165               OnInvalidate(MakeMap(syncable::EXTENSIONS, "")))
    166       .Times(2);
    167 
    168   // Should trigger twice.
    169   FireInvalidate("EXTENSION", kUnknownVersion, NULL);
    170   FireInvalidate("EXTENSION", kUnknownVersion, NULL);
    171 }
    172 
    173 TEST_F(ChromeInvalidationClientTest, InvalidateVersionMultipleTypes) {
    174   using ::testing::Mock;
    175 
    176   syncable::ModelTypeSet types;
    177   types.insert(syncable::BOOKMARKS);
    178   types.insert(syncable::APPS);
    179   client_.RegisterTypes(types);
    180 
    181   EXPECT_CALL(mock_listener_,
    182               OnInvalidate(MakeMap(syncable::APPS, "")));
    183   EXPECT_CALL(mock_listener_,
    184               OnInvalidate(MakeMap(syncable::EXTENSIONS, "")));
    185 
    186   // Should trigger both.
    187   FireInvalidate("APP", 3, NULL);
    188   FireInvalidate("EXTENSION", 2, NULL);
    189 
    190   Mock::VerifyAndClearExpectations(&mock_listener_);
    191 
    192   // Should both be dropped.
    193   FireInvalidate("APP", 1, NULL);
    194   FireInvalidate("EXTENSION", 1, NULL);
    195 
    196   Mock::VerifyAndClearExpectations(&mock_listener_);
    197 
    198   // InvalidateAll shouldn't change any version state.
    199   EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
    200   FireInvalidateAll();
    201 
    202   Mock::VerifyAndClearExpectations(&mock_listener_);
    203 
    204   EXPECT_CALL(mock_listener_,
    205               OnInvalidate(MakeMap(syncable::PREFERENCES, "")));
    206   EXPECT_CALL(mock_listener_,
    207               OnInvalidate(MakeMap(syncable::EXTENSIONS, "")));
    208   EXPECT_CALL(mock_listener_,
    209               OnInvalidate(MakeMap(syncable::APPS, "")));
    210 
    211   // Should trigger all three.
    212   FireInvalidate("PREFERENCE", 5, NULL);
    213   FireInvalidate("EXTENSION", 3, NULL);
    214   FireInvalidate("APP", 4, NULL);
    215 }
    216 
    217 TEST_F(ChromeInvalidationClientTest, InvalidateAll) {
    218   syncable::ModelTypeSet types;
    219   types.insert(syncable::PREFERENCES);
    220   types.insert(syncable::EXTENSIONS);
    221   client_.RegisterTypes(types);
    222   EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
    223   FireInvalidateAll();
    224 }
    225 
    226 TEST_F(ChromeInvalidationClientTest, RegisterTypes) {
    227   syncable::ModelTypeSet types;
    228   types.insert(syncable::PREFERENCES);
    229   types.insert(syncable::EXTENSIONS);
    230   client_.RegisterTypes(types);
    231   // Registered types should be preserved across Stop/Start.
    232   TearDown();
    233   SetUp();
    234   EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
    235   FireInvalidateAll();
    236 }
    237 
    238 // Outbound packet sending should be resilient to
    239 // changing/disappearing base tasks.
    240 TEST_F(ChromeInvalidationClientTest, OutboundPackets) {
    241   SimulateInformOutboundListener();
    242 
    243   notifier::FakeBaseTask fake_base_task;
    244   client_.ChangeBaseTask(fake_base_task.AsWeakPtr());
    245 
    246   SimulateInformOutboundListener();
    247 
    248   {
    249     notifier::FakeBaseTask fake_base_task2;
    250     client_.ChangeBaseTask(fake_base_task2.AsWeakPtr());
    251   }
    252 
    253   SimulateInformOutboundListener();
    254 }
    255 
    256 // TODO(akalin): Flesh out unit tests.
    257 
    258 }  // namespace sync_notifier
    259