• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MakeClosure()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:
SetUp()55   virtual void SetUp() {
56     client_.Start(kClientId, kClientInfo, kState,
57                   &mock_listener_, &mock_state_writer_,
58                   fake_base_task_.AsWeakPtr());
59   }
60 
TearDown()61   virtual void TearDown() {
62     client_.Stop();
63     message_loop_.RunAllPending();
64   }
65 
66   // Simulates DoInformOutboundListener() from network-manager.cc.
SimulateInformOutboundListener()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|.
FireInvalidate(const char * type_name,int64 version,const char * payload)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 
FireInvalidateAll()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 
MakeMap(syncable::ModelType model_type,const std::string & payload)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 
MakeMapFromSet(syncable::ModelTypeSet types,const std::string & payload)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 
TEST_F(ChromeInvalidationClientTest,InvalidateBadObjectId)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 
TEST_F(ChromeInvalidationClientTest,InvalidateNoPayload)136 TEST_F(ChromeInvalidationClientTest, InvalidateNoPayload) {
137   EXPECT_CALL(mock_listener_,
138               OnInvalidate(MakeMap(syncable::BOOKMARKS, "")));
139   FireInvalidate("BOOKMARK", 1, NULL);
140 }
141 
TEST_F(ChromeInvalidationClientTest,InvalidateWithPayload)142 TEST_F(ChromeInvalidationClientTest, InvalidateWithPayload) {
143   EXPECT_CALL(mock_listener_,
144               OnInvalidate(MakeMap(syncable::PREFERENCES, "payload")));
145   FireInvalidate("PREFERENCE", 1, "payload");
146 }
147 
TEST_F(ChromeInvalidationClientTest,InvalidateVersion)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 
TEST_F(ChromeInvalidationClientTest,InvalidateUnknownVersion)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 
TEST_F(ChromeInvalidationClientTest,InvalidateVersionMultipleTypes)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 
TEST_F(ChromeInvalidationClientTest,InvalidateAll)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 
TEST_F(ChromeInvalidationClientTest,RegisterTypes)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.
TEST_F(ChromeInvalidationClientTest,OutboundPackets)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