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 // This class defines tests that implementations of InvalidationService should
6 // pass in order to be conformant. Here's how you use it to test your
7 // implementation.
8 //
9 // Say your class is called MyInvalidationService. Then you need to define a
10 // class called MyInvalidationServiceTestDelegate in
11 // my_invalidation_frontend_unittest.cc like this:
12 //
13 // class MyInvalidationServiceTestDelegate {
14 // public:
15 // MyInvalidationServiceTestDelegate() ...
16 //
17 // ~MyInvalidationServiceTestDelegate() {
18 // // DestroyInvalidator() may not be explicitly called by tests.
19 // DestroyInvalidator();
20 // }
21 //
22 // // Create the InvalidationService implementation with the given params.
23 // void CreateInvalidationService() {
24 // ...
25 // }
26 //
27 // // Should return the InvalidationService implementation. Only called
28 // // after CreateInvalidator and before DestroyInvalidator.
29 // MyInvalidationService* GetInvalidationService() {
30 // ...
31 // }
32 //
33 // // Destroy the InvalidationService implementation.
34 // void DestroyInvalidationService() {
35 // ...
36 // }
37 //
38 // // The Trigger* functions below should block until the effects of
39 // // the call are visible on the current thread.
40 //
41 // // Should cause OnInvalidatorStateChange() to be called on all
42 // // observers of the InvalidationService implementation with the given
43 // // parameters.
44 // void TriggerOnInvalidatorStateChange(InvalidatorState state) {
45 // ...
46 // }
47 //
48 // // Should cause OnIncomingInvalidation() to be called on all
49 // // observers of the InvalidationService implementation with the given
50 // // parameters.
51 // void TriggerOnIncomingInvalidation(
52 // const TopicInvalidationMap& invalidation_map) {
53 // ...
54 // }
55 // };
56 //
57 // The InvalidationServiceTest test harness will have a member variable of
58 // this delegate type and will call its functions in the various
59 // tests.
60 //
61 // Then you simply #include this file as well as gtest.h and add the
62 // following statement to my_sync_notifier_unittest.cc:
63 //
64 // INSTANTIATE_TYPED_TEST_SUITE_P(
65 // MyInvalidationService,
66 // InvalidationServiceTest,
67 // MyInvalidatorTestDelegate);
68 //
69 // Easy!
70
71 #ifndef COMPONENTS_INVALIDATION_IMPL_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
72 #define COMPONENTS_INVALIDATION_IMPL_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
73
74 #include "base/compiler_specific.h"
75 #include "base/macros.h"
76 #include "components/invalidation/impl/fake_invalidation_handler.h"
77 #include "components/invalidation/impl/topic_invalidation_map_test_util.h"
78 #include "components/invalidation/public/ack_handle.h"
79 #include "components/invalidation/public/invalidation.h"
80 #include "components/invalidation/public/invalidation_service.h"
81 #include "components/invalidation/public/invalidation_util.h"
82 #include "components/invalidation/public/topic_invalidation_map.h"
83 #include "testing/gtest/include/gtest/gtest.h"
84
85 template <typename InvalidatorTestDelegate>
86 class InvalidationServiceTest : public testing::Test {
87 protected:
88 InvalidationServiceTest() = default;
89
90 invalidation::InvalidationService*
CreateAndInitializeInvalidationService()91 CreateAndInitializeInvalidationService() {
92 this->delegate_.CreateInvalidationService();
93 return this->delegate_.GetInvalidationService();
94 }
95
96 InvalidatorTestDelegate delegate_;
97
98 const syncer::Topic topic1 = "BOOKMARK";
99 const syncer::Topic topic2 = "PREFERENCE";
100 const syncer::Topic topic3 = "AUTOFILL";
101 const syncer::Topic topic4 = "PUSH_MESSAGE";
102 };
103
104 TYPED_TEST_SUITE_P(InvalidationServiceTest);
105
106 // Initialize the invalidator, register a handler, register some IDs for that
107 // handler, and then unregister the handler, dispatching invalidations in
108 // between. The handler should only see invalidations when its registered and
109 // its IDs are registered.
TYPED_TEST_P(InvalidationServiceTest,Basic)110 TYPED_TEST_P(InvalidationServiceTest, Basic) {
111 invalidation::InvalidationService* const invalidator =
112 this->CreateAndInitializeInvalidationService();
113
114 syncer::FakeInvalidationHandler handler;
115
116 invalidator->RegisterInvalidationHandler(&handler);
117
118 syncer::TopicInvalidationMap invalidation_map;
119 invalidation_map.Insert(syncer::Invalidation::Init(this->topic1, 1, "1"));
120 invalidation_map.Insert(syncer::Invalidation::Init(this->topic2, 2, "2"));
121 invalidation_map.Insert(syncer::Invalidation::Init(this->topic3, 3, "3"));
122
123 // Should be ignored since no IDs are registered to |handler|.
124 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
125 EXPECT_EQ(0, handler.GetInvalidationCount());
126
127 syncer::TopicSet topics;
128 topics.insert(this->topic1);
129 topics.insert(this->topic2);
130 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler, topics));
131
132 this->delegate_.TriggerOnInvalidatorStateChange(
133 syncer::INVALIDATIONS_ENABLED);
134 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
135
136 syncer::TopicInvalidationMap expected_invalidations;
137 expected_invalidations.Insert(
138 syncer::Invalidation::Init(this->topic1, 1, "1"));
139 expected_invalidations.Insert(
140 syncer::Invalidation::Init(this->topic2, 2, "2"));
141
142 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
143 EXPECT_EQ(1, handler.GetInvalidationCount());
144 EXPECT_THAT(expected_invalidations, Eq(handler.GetLastInvalidationMap()));
145
146 topics.erase(this->topic1);
147 topics.insert(this->topic3);
148 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler, topics));
149
150 expected_invalidations = syncer::TopicInvalidationMap();
151 expected_invalidations.Insert(
152 syncer::Invalidation::Init(this->topic2, 2, "2"));
153 expected_invalidations.Insert(
154 syncer::Invalidation::Init(this->topic3, 3, "3"));
155
156 // Removed Topics should not be notified, newly-added ones should.
157 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
158 EXPECT_EQ(2, handler.GetInvalidationCount());
159 EXPECT_THAT(expected_invalidations, Eq(handler.GetLastInvalidationMap()));
160
161 this->delegate_.TriggerOnInvalidatorStateChange(
162 syncer::TRANSIENT_INVALIDATION_ERROR);
163 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
164 handler.GetInvalidatorState());
165
166 this->delegate_.TriggerOnInvalidatorStateChange(
167 syncer::INVALIDATIONS_ENABLED);
168 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED,
169 handler.GetInvalidatorState());
170
171 invalidator->UnregisterInvalidationHandler(&handler);
172
173 // Should be ignored since |handler| isn't registered anymore.
174 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
175 EXPECT_EQ(2, handler.GetInvalidationCount());
176 }
177
178 // Register handlers and some topics for those handlers, register a handler
179 // with no topics, and register a handler with some topics but unregister it.
180 // Then, dispatch some invalidations and invalidations. Handlers that are
181 // registered should get invalidations, and the ones that have registered
182 // topics should receive invalidations for those topics.
TYPED_TEST_P(InvalidationServiceTest,MultipleHandlers)183 TYPED_TEST_P(InvalidationServiceTest, MultipleHandlers) {
184 invalidation::InvalidationService* const invalidator =
185 this->CreateAndInitializeInvalidationService();
186
187 syncer::FakeInvalidationHandler handler1;
188 syncer::FakeInvalidationHandler handler2;
189 syncer::FakeInvalidationHandler handler3;
190 syncer::FakeInvalidationHandler handler4;
191
192 invalidator->RegisterInvalidationHandler(&handler1);
193 invalidator->RegisterInvalidationHandler(&handler2);
194 invalidator->RegisterInvalidationHandler(&handler3);
195 invalidator->RegisterInvalidationHandler(&handler4);
196
197 {
198 syncer::TopicSet topics;
199 topics.insert(this->topic1);
200 topics.insert(this->topic2);
201 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, topics));
202 }
203
204 {
205 syncer::TopicSet topics;
206 topics.insert(this->topic3);
207 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler2, topics));
208 }
209
210 // Don't register any topics for handler3.
211
212 {
213 syncer::TopicSet topics;
214 topics.insert(this->topic4);
215 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler4, topics));
216 }
217
218 invalidator->UnregisterInvalidationHandler(&handler4);
219
220 this->delegate_.TriggerOnInvalidatorStateChange(
221 syncer::INVALIDATIONS_ENABLED);
222 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
223 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
224 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler3.GetInvalidatorState());
225 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
226 handler4.GetInvalidatorState());
227
228 {
229 syncer::TopicInvalidationMap invalidation_map;
230 invalidation_map.Insert(syncer::Invalidation::Init(this->topic1, 1, "1"));
231 invalidation_map.Insert(syncer::Invalidation::Init(this->topic2, 2, "2"));
232 invalidation_map.Insert(syncer::Invalidation::Init(this->topic3, 3, "3"));
233 invalidation_map.Insert(syncer::Invalidation::Init(this->topic4, 4, "4"));
234 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
235
236 syncer::TopicInvalidationMap expected_invalidations;
237 expected_invalidations.Insert(
238 syncer::Invalidation::Init(this->topic1, 1, "1"));
239 expected_invalidations.Insert(
240 syncer::Invalidation::Init(this->topic2, 2, "2"));
241
242 EXPECT_EQ(1, handler1.GetInvalidationCount());
243 EXPECT_THAT(expected_invalidations, Eq(handler1.GetLastInvalidationMap()));
244
245 expected_invalidations = syncer::TopicInvalidationMap();
246 expected_invalidations.Insert(
247 syncer::Invalidation::Init(this->topic3, 3, "3"));
248
249 EXPECT_EQ(1, handler2.GetInvalidationCount());
250 EXPECT_THAT(expected_invalidations, Eq(handler2.GetLastInvalidationMap()));
251
252 EXPECT_EQ(0, handler3.GetInvalidationCount());
253 EXPECT_EQ(0, handler4.GetInvalidationCount());
254 }
255
256 this->delegate_.TriggerOnInvalidatorStateChange(
257 syncer::TRANSIENT_INVALIDATION_ERROR);
258 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
259 handler1.GetInvalidatorState());
260 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
261 handler2.GetInvalidatorState());
262 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
263 handler3.GetInvalidatorState());
264 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
265 handler4.GetInvalidatorState());
266
267 invalidator->UnregisterInvalidationHandler(&handler3);
268 invalidator->UnregisterInvalidationHandler(&handler2);
269 invalidator->UnregisterInvalidationHandler(&handler1);
270 }
271
272 // Multiple registrations by different handlers on the same Topic should return
273 // false.
TYPED_TEST_P(InvalidationServiceTest,MultipleRegistrations)274 TYPED_TEST_P(InvalidationServiceTest, MultipleRegistrations) {
275 invalidation::InvalidationService* const invalidator =
276 this->CreateAndInitializeInvalidationService();
277
278 syncer::FakeInvalidationHandler handler1;
279 syncer::FakeInvalidationHandler handler2;
280
281 invalidator->RegisterInvalidationHandler(&handler1);
282 invalidator->RegisterInvalidationHandler(&handler2);
283
284 // Registering both handlers for the same topic. First call should succeed,
285 // second should fail.
286 syncer::TopicSet topics;
287 topics.insert(this->topic1);
288 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, topics));
289 EXPECT_FALSE(invalidator->UpdateInterestedTopics(&handler2, topics));
290
291 invalidator->UnregisterInvalidationHandler(&handler2);
292 invalidator->UnregisterInvalidationHandler(&handler1);
293 }
294
295 // Make sure that passing an empty set to UpdateInterestedTopics clears
296 // the corresponding entries for the handler.
TYPED_TEST_P(InvalidationServiceTest,EmptySetUnregisters)297 TYPED_TEST_P(InvalidationServiceTest, EmptySetUnregisters) {
298 invalidation::InvalidationService* const invalidator =
299 this->CreateAndInitializeInvalidationService();
300
301 syncer::FakeInvalidationHandler handler1;
302
303 // Control observer.
304 syncer::FakeInvalidationHandler handler2;
305
306 invalidator->RegisterInvalidationHandler(&handler1);
307 invalidator->RegisterInvalidationHandler(&handler2);
308
309 {
310 syncer::TopicSet topics;
311 topics.insert(this->topic1);
312 topics.insert(this->topic2);
313 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler1, topics));
314 }
315
316 {
317 syncer::TopicSet topics;
318 topics.insert(this->topic3);
319 EXPECT_TRUE(invalidator->UpdateInterestedTopics(&handler2, topics));
320 }
321
322 // Unregister the topics for the first observer. It should not receive any
323 // further invalidations.
324 EXPECT_TRUE(
325 invalidator->UpdateInterestedTopics(&handler1, syncer::TopicSet()));
326
327 this->delegate_.TriggerOnInvalidatorStateChange(
328 syncer::INVALIDATIONS_ENABLED);
329 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
330 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
331
332 {
333 syncer::TopicInvalidationMap invalidation_map;
334 invalidation_map.Insert(syncer::Invalidation::Init(this->topic1, 1, "1"));
335 invalidation_map.Insert(syncer::Invalidation::Init(this->topic2, 2, "2"));
336 invalidation_map.Insert(syncer::Invalidation::Init(this->topic3, 3, "3"));
337 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map);
338 EXPECT_EQ(0, handler1.GetInvalidationCount());
339 EXPECT_EQ(1, handler2.GetInvalidationCount());
340 }
341
342 this->delegate_.TriggerOnInvalidatorStateChange(
343 syncer::TRANSIENT_INVALIDATION_ERROR);
344 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
345 handler1.GetInvalidatorState());
346 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
347 handler2.GetInvalidatorState());
348
349 invalidator->UnregisterInvalidationHandler(&handler2);
350 invalidator->UnregisterInvalidationHandler(&handler1);
351 }
352
353 namespace internal {
354
355 // A FakeInvalidationHandler that is "bound" to a specific
356 // InvalidationService. This is for cross-referencing state information with
357 // the bound InvalidationService.
358 class BoundFakeInvalidationHandler : public syncer::FakeInvalidationHandler {
359 public:
360 explicit BoundFakeInvalidationHandler(
361 const invalidation::InvalidationService& invalidator);
362 ~BoundFakeInvalidationHandler() override;
363
364 // Returns the last return value of GetInvalidatorState() on the
365 // bound invalidator from the last time the invalidator state
366 // changed.
367 syncer::InvalidatorState GetLastRetrievedState() const;
368
369 // InvalidationHandler implementation.
370 void OnInvalidatorStateChange(syncer::InvalidatorState state) override;
371
372 private:
373 const invalidation::InvalidationService& invalidator_;
374 syncer::InvalidatorState last_retrieved_state_;
375
376 DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler);
377 };
378
379 } // namespace internal
380
TYPED_TEST_P(InvalidationServiceTest,GetInvalidatorStateAlwaysCurrent)381 TYPED_TEST_P(InvalidationServiceTest, GetInvalidatorStateAlwaysCurrent) {
382 invalidation::InvalidationService* const invalidator =
383 this->CreateAndInitializeInvalidationService();
384
385 internal::BoundFakeInvalidationHandler handler(*invalidator);
386 invalidator->RegisterInvalidationHandler(&handler);
387
388 this->delegate_.TriggerOnInvalidatorStateChange(
389 syncer::INVALIDATIONS_ENABLED);
390 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
391 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetLastRetrievedState());
392
393 this->delegate_.TriggerOnInvalidatorStateChange(
394 syncer::TRANSIENT_INVALIDATION_ERROR);
395 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
396 handler.GetInvalidatorState());
397 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
398 handler.GetLastRetrievedState());
399
400 invalidator->UnregisterInvalidationHandler(&handler);
401 }
402
403 REGISTER_TYPED_TEST_SUITE_P(InvalidationServiceTest,
404 Basic,
405 MultipleHandlers,
406 MultipleRegistrations,
407 EmptySetUnregisters,
408 GetInvalidatorStateAlwaysCurrent);
409
410 #endif // COMPONENTS_INVALIDATION_IMPL_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
411