blob: 411da8c353b291ab3e2ef37d9e5afe01926e7cec [file] [log] [blame]
[email protected]33596da2012-08-31 23:39:251// Copyright (c) 2012 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 Invalidator should pass in
6// order to be conformant. Here's how you use it to test your implementation.
7//
8// Say your class is called MyInvalidator. Then you need to define a class
9// called MyInvalidatorTestDelegate in my_sync_notifier_unittest.cc like this:
10//
11// class MyInvalidatorTestDelegate {
12// public:
13// MyInvalidatorTestDelegate() ...
14//
15// ~MyInvalidatorTestDelegate() {
16// // DestroyInvalidator() may not be explicitly called by tests.
17// DestroyInvalidator();
18// }
19//
20// // Create the Invalidator implementation with the given parameters.
21// void CreateInvalidator(
22// const std::string& initial_state,
23// const base::WeakPtr<InvalidationStateTracker>&
24// invalidation_state_tracker) {
25// ...
26// }
27//
28// // Should return the Invalidator implementation. Only called after
29// // CreateInvalidator and before DestroyInvalidator.
30// MyInvalidator* GetInvalidator() {
31// ...
32// }
33//
34// // Destroy the Invalidator implementation.
35// void DestroyInvalidator() {
36// ...
37// }
38//
39// // Called after a call to SetStateDeprecated(), SetUniqueId(), or
40// // UpdateCredentials() on the Invalidator implementation. Should block
41// // until the effects of the call are visible on the current thread.
42// void WaitForInvalidator() {
43// ...
44// }
45//
46// // The Trigger* functions below should block until the effects of
47// // the call are visible on the current thread.
48//
[email protected]08a6f9992012-09-07 19:19:1649// // Should cause OnInvalidatorStateChange() to be called on all
50// // observers of the Invalidator implementation with the given
51// // parameters.
52// void TriggerOnInvalidatorStateChange(InvalidatorState state) {
[email protected]33596da2012-08-31 23:39:2553// ...
54// }
55//
[email protected]08a6f9992012-09-07 19:19:1656// // Should cause OnIncomingInvalidation() to be called on all
[email protected]33596da2012-08-31 23:39:2557// // observers of the Invalidator implementation with the given
58// // parameters.
[email protected]3e31fa42012-10-04 03:53:0959// void TriggerOnIncomingInvalidation(
60// const ObjectIdInvalidationMap& invalidation_map,
61// IncomingInvalidationSource source) {
[email protected]33596da2012-08-31 23:39:2562// ...
63// }
64//
65// // Returns whether or not the notifier handles storing the old
66// // (deprecated) notifier state.
67// static bool InvalidatorHandlesDeprecatedState() {
68// return false;
69// }
70// };
71//
72// The InvalidatorTest test harness will have a member variable of
73// this delegate type and will call its functions in the various
74// tests.
75//
76// Then you simply #include this file as well as gtest.h and add the
77// following statement to my_sync_notifier_unittest.cc:
78//
79// INSTANTIATE_TYPED_TEST_CASE_P(
80// MyInvalidator, InvalidatorTest, MyInvalidatorTestDelegate);
81//
82// Easy!
83
84#ifndef SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_
85#define SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_
86
[email protected]08a6f9992012-09-07 19:19:1687#include "base/basictypes.h"
88#include "base/compiler_specific.h"
[email protected]33596da2012-08-31 23:39:2589#include "google/cacheinvalidation/include/types.h"
90#include "google/cacheinvalidation/types.pb.h"
91#include "sync/notifier/fake_invalidation_handler.h"
92#include "sync/notifier/fake_invalidation_state_tracker.h"
93#include "sync/notifier/invalidator.h"
[email protected]3e31fa42012-10-04 03:53:0994#include "sync/notifier/object_id_invalidation_map.h"
95#include "sync/notifier/object_id_invalidation_map_test_util.h"
[email protected]33596da2012-08-31 23:39:2596#include "testing/gtest/include/gtest/gtest.h"
97
98namespace syncer {
99
100template <typename InvalidatorTestDelegate>
101class InvalidatorTest : public testing::Test {
102 protected:
103 InvalidatorTest()
104 : id1(ipc::invalidation::ObjectSource::TEST, "a"),
105 id2(ipc::invalidation::ObjectSource::TEST, "b"),
106 id3(ipc::invalidation::ObjectSource::TEST, "c"),
107 id4(ipc::invalidation::ObjectSource::TEST, "d") {
108 }
109
110 Invalidator* CreateAndInitializeInvalidator() {
111 this->delegate_.CreateInvalidator("fake_initial_state",
112 this->fake_tracker_.AsWeakPtr());
113 Invalidator* const invalidator = this->delegate_.GetInvalidator();
114
115 // TODO(tim): This call should be a no-op. Remove once bug 124140 and
116 // associated issues are fixed.
117 invalidator->SetStateDeprecated("fake_state");
118 this->delegate_.WaitForInvalidator();
[email protected]8cdb6892012-10-03 05:54:40119 // We don't expect |fake_tracker_|'s bootstrap data to change, as we
120 // initialized with a non-empty value previously.
121 EXPECT_TRUE(this->fake_tracker_.GetBootstrapData().empty());
[email protected]33596da2012-08-31 23:39:25122 invalidator->SetUniqueId("fake_id");
123 this->delegate_.WaitForInvalidator();
124 invalidator->UpdateCredentials("[email protected]", "fake_token");
125 this->delegate_.WaitForInvalidator();
126
127 return invalidator;
128 }
129
130 FakeInvalidationStateTracker fake_tracker_;
131 InvalidatorTestDelegate delegate_;
132
133 const invalidation::ObjectId id1;
134 const invalidation::ObjectId id2;
135 const invalidation::ObjectId id3;
136 const invalidation::ObjectId id4;
137};
138
139TYPED_TEST_CASE_P(InvalidatorTest);
140
141// Initialize the invalidator, register a handler, register some IDs for that
142// handler, and then unregister the handler, dispatching invalidations in
143// between. The handler should only see invalidations when its registered and
144// its IDs are registered.
145TYPED_TEST_P(InvalidatorTest, Basic) {
146 Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
147
148 FakeInvalidationHandler handler;
149
150 invalidator->RegisterHandler(&handler);
151
[email protected]3e31fa42012-10-04 03:53:09152 ObjectIdInvalidationMap states;
[email protected]33596da2012-08-31 23:39:25153 states[this->id1].payload = "1";
154 states[this->id2].payload = "2";
155 states[this->id3].payload = "3";
156
157 // Should be ignored since no IDs are registered to |handler|.
[email protected]08a6f9992012-09-07 19:19:16158 this->delegate_.TriggerOnIncomingInvalidation(states, REMOTE_INVALIDATION);
159 EXPECT_EQ(0, handler.GetInvalidationCount());
[email protected]33596da2012-08-31 23:39:25160
161 ObjectIdSet ids;
162 ids.insert(this->id1);
163 ids.insert(this->id2);
164 invalidator->UpdateRegisteredIds(&handler, ids);
165
[email protected]08a6f9992012-09-07 19:19:16166 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
167 EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
[email protected]33596da2012-08-31 23:39:25168
[email protected]3e31fa42012-10-04 03:53:09169 ObjectIdInvalidationMap expected_states;
[email protected]33596da2012-08-31 23:39:25170 expected_states[this->id1].payload = "1";
171 expected_states[this->id2].payload = "2";
172
[email protected]08a6f9992012-09-07 19:19:16173 this->delegate_.TriggerOnIncomingInvalidation(states, REMOTE_INVALIDATION);
174 EXPECT_EQ(1, handler.GetInvalidationCount());
[email protected]3e31fa42012-10-04 03:53:09175 EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
[email protected]08a6f9992012-09-07 19:19:16176 EXPECT_EQ(REMOTE_INVALIDATION, handler.GetLastInvalidationSource());
[email protected]33596da2012-08-31 23:39:25177
178 ids.erase(this->id1);
179 ids.insert(this->id3);
180 invalidator->UpdateRegisteredIds(&handler, ids);
181
182 expected_states.erase(this->id1);
183 expected_states[this->id3].payload = "3";
184
185 // Removed object IDs should not be notified, newly-added ones should.
[email protected]08a6f9992012-09-07 19:19:16186 this->delegate_.TriggerOnIncomingInvalidation(states, REMOTE_INVALIDATION);
187 EXPECT_EQ(2, handler.GetInvalidationCount());
[email protected]3e31fa42012-10-04 03:53:09188 EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
[email protected]08a6f9992012-09-07 19:19:16189 EXPECT_EQ(REMOTE_INVALIDATION, handler.GetLastInvalidationSource());
[email protected]33596da2012-08-31 23:39:25190
[email protected]08a6f9992012-09-07 19:19:16191 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
192 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR,
193 handler.GetInvalidatorState());
[email protected]33596da2012-08-31 23:39:25194
[email protected]08a6f9992012-09-07 19:19:16195 this->delegate_.TriggerOnInvalidatorStateChange(
196 INVALIDATION_CREDENTIALS_REJECTED);
197 EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED,
198 handler.GetInvalidatorState());
[email protected]33596da2012-08-31 23:39:25199
200 invalidator->UnregisterHandler(&handler);
201
202 // Should be ignored since |handler| isn't registered anymore.
[email protected]08a6f9992012-09-07 19:19:16203 this->delegate_.TriggerOnIncomingInvalidation(states, REMOTE_INVALIDATION);
204 EXPECT_EQ(2, handler.GetInvalidationCount());
[email protected]33596da2012-08-31 23:39:25205}
206
207// Register handlers and some IDs for those handlers, register a handler with
208// no IDs, and register a handler with some IDs but unregister it. Then,
[email protected]08a6f9992012-09-07 19:19:16209// dispatch some invalidations and invalidations. Handlers that are registered
210// should get invalidations, and the ones that have registered IDs should
[email protected]33596da2012-08-31 23:39:25211// receive invalidations for those IDs.
212TYPED_TEST_P(InvalidatorTest, MultipleHandlers) {
213 Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
214
215 FakeInvalidationHandler handler1;
216 FakeInvalidationHandler handler2;
217 FakeInvalidationHandler handler3;
218 FakeInvalidationHandler handler4;
219
220 invalidator->RegisterHandler(&handler1);
221 invalidator->RegisterHandler(&handler2);
222 invalidator->RegisterHandler(&handler3);
223 invalidator->RegisterHandler(&handler4);
224
225 {
226 ObjectIdSet ids;
227 ids.insert(this->id1);
228 ids.insert(this->id2);
229 invalidator->UpdateRegisteredIds(&handler1, ids);
230 }
231
232 {
233 ObjectIdSet ids;
234 ids.insert(this->id3);
235 invalidator->UpdateRegisteredIds(&handler2, ids);
236 }
237
238 // Don't register any IDs for handler3.
239
240 {
241 ObjectIdSet ids;
242 ids.insert(this->id4);
243 invalidator->UpdateRegisteredIds(&handler4, ids);
244 }
245
246 invalidator->UnregisterHandler(&handler4);
247
[email protected]08a6f9992012-09-07 19:19:16248 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
249 EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
250 EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
251 EXPECT_EQ(INVALIDATIONS_ENABLED, handler3.GetInvalidatorState());
252 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState());
[email protected]33596da2012-08-31 23:39:25253
254 {
[email protected]3e31fa42012-10-04 03:53:09255 ObjectIdInvalidationMap states;
[email protected]33596da2012-08-31 23:39:25256 states[this->id1].payload = "1";
257 states[this->id2].payload = "2";
258 states[this->id3].payload = "3";
259 states[this->id4].payload = "4";
[email protected]08a6f9992012-09-07 19:19:16260 this->delegate_.TriggerOnIncomingInvalidation(states, REMOTE_INVALIDATION);
[email protected]33596da2012-08-31 23:39:25261
[email protected]3e31fa42012-10-04 03:53:09262 ObjectIdInvalidationMap expected_states;
[email protected]33596da2012-08-31 23:39:25263 expected_states[this->id1].payload = "1";
264 expected_states[this->id2].payload = "2";
265
[email protected]08a6f9992012-09-07 19:19:16266 EXPECT_EQ(1, handler1.GetInvalidationCount());
[email protected]3e31fa42012-10-04 03:53:09267 EXPECT_THAT(expected_states, Eq(handler1.GetLastInvalidationMap()));
[email protected]08a6f9992012-09-07 19:19:16268 EXPECT_EQ(REMOTE_INVALIDATION, handler1.GetLastInvalidationSource());
[email protected]33596da2012-08-31 23:39:25269
270 expected_states.clear();
271 expected_states[this->id3].payload = "3";
272
[email protected]08a6f9992012-09-07 19:19:16273 EXPECT_EQ(1, handler2.GetInvalidationCount());
[email protected]3e31fa42012-10-04 03:53:09274 EXPECT_THAT(expected_states, Eq(handler2.GetLastInvalidationMap()));
[email protected]08a6f9992012-09-07 19:19:16275 EXPECT_EQ(REMOTE_INVALIDATION, handler2.GetLastInvalidationSource());
[email protected]33596da2012-08-31 23:39:25276
[email protected]08a6f9992012-09-07 19:19:16277 EXPECT_EQ(0, handler3.GetInvalidationCount());
278 EXPECT_EQ(0, handler4.GetInvalidationCount());
[email protected]33596da2012-08-31 23:39:25279 }
280
[email protected]08a6f9992012-09-07 19:19:16281 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
282 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState());
283 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState());
284 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler3.GetInvalidatorState());
285 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState());
[email protected]33596da2012-08-31 23:39:25286}
287
288// Make sure that passing an empty set to UpdateRegisteredIds clears the
289// corresponding entries for the handler.
290TYPED_TEST_P(InvalidatorTest, EmptySetUnregisters) {
291 Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
292
293 FakeInvalidationHandler handler1;
294
295 // Control observer.
296 FakeInvalidationHandler handler2;
297
298 invalidator->RegisterHandler(&handler1);
299 invalidator->RegisterHandler(&handler2);
300
301 {
302 ObjectIdSet ids;
303 ids.insert(this->id1);
304 ids.insert(this->id2);
305 invalidator->UpdateRegisteredIds(&handler1, ids);
306 }
307
308 {
309 ObjectIdSet ids;
310 ids.insert(this->id3);
311 invalidator->UpdateRegisteredIds(&handler2, ids);
312 }
313
314 // Unregister the IDs for the first observer. It should not receive any
315 // further invalidations.
316 invalidator->UpdateRegisteredIds(&handler1, ObjectIdSet());
317
[email protected]08a6f9992012-09-07 19:19:16318 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
319 EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
320 EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
[email protected]33596da2012-08-31 23:39:25321
322 {
[email protected]3e31fa42012-10-04 03:53:09323 ObjectIdInvalidationMap states;
[email protected]33596da2012-08-31 23:39:25324 states[this->id1].payload = "1";
325 states[this->id2].payload = "2";
326 states[this->id3].payload = "3";
[email protected]08a6f9992012-09-07 19:19:16327 this->delegate_.TriggerOnIncomingInvalidation(states, REMOTE_INVALIDATION);
328 EXPECT_EQ(0, handler1.GetInvalidationCount());
329 EXPECT_EQ(1, handler2.GetInvalidationCount());
[email protected]33596da2012-08-31 23:39:25330 }
331
[email protected]08a6f9992012-09-07 19:19:16332 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
333 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState());
334 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState());
335}
336
337namespace internal {
338
339// A FakeInvalidationHandler that is "bound" to a specific
340// Invalidator. This is for cross-referencing state information with
341// the bound Invalidator.
342class BoundFakeInvalidationHandler : public FakeInvalidationHandler {
343 public:
344 explicit BoundFakeInvalidationHandler(const Invalidator& invalidator);
345 virtual ~BoundFakeInvalidationHandler();
346
347 // Returns the last return value of GetInvalidatorState() on the
348 // bound invalidator from the last time the invalidator state
349 // changed.
350 InvalidatorState GetLastRetrievedState() const;
351
352 // InvalidationHandler implementation.
353 virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE;
354
355 private:
356 const Invalidator& invalidator_;
357 InvalidatorState last_retrieved_state_;
358
359 DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler);
360};
361
362} // namespace internal
363
364TYPED_TEST_P(InvalidatorTest, GetInvalidatorStateAlwaysCurrent) {
365 Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
366
367 internal::BoundFakeInvalidationHandler handler(*invalidator);
368 invalidator->RegisterHandler(&handler);
369
370 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
371 EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
372 EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetLastRetrievedState());
373
374 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
375 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetInvalidatorState());
376 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetLastRetrievedState());
[email protected]33596da2012-08-31 23:39:25377}
378
[email protected]8cdb6892012-10-03 05:54:40379// Initialize the invalidator with no bootstrap data. Call the deprecated
[email protected]33596da2012-08-31 23:39:25380// state setter function a number of times, destroying and re-creating the
[email protected]8cdb6892012-10-03 05:54:40381// invalidator in between. Only the first one should take effect (i.e.,
382// migration of bootstrap data should only happen once)
[email protected]33596da2012-08-31 23:39:25383TYPED_TEST_P(InvalidatorTest, MigrateState) {
384 if (!this->delegate_.InvalidatorHandlesDeprecatedState()) {
385 DLOG(INFO) << "This Invalidator doesn't handle deprecated state; "
386 << "skipping";
387 return;
388 }
389
390 this->delegate_.CreateInvalidator(std::string(),
391 this->fake_tracker_.AsWeakPtr());
392 Invalidator* invalidator = this->delegate_.GetInvalidator();
393
394 invalidator->SetStateDeprecated("fake_state");
395 this->delegate_.WaitForInvalidator();
[email protected]8cdb6892012-10-03 05:54:40396 EXPECT_EQ("fake_state", this->fake_tracker_.GetBootstrapData());
[email protected]33596da2012-08-31 23:39:25397
398 // Should do nothing.
399 invalidator->SetStateDeprecated("spurious_fake_state");
400 this->delegate_.WaitForInvalidator();
[email protected]8cdb6892012-10-03 05:54:40401 EXPECT_EQ("fake_state", this->fake_tracker_.GetBootstrapData());
[email protected]33596da2012-08-31 23:39:25402
403 // Pretend that Chrome has shut down.
404 this->delegate_.DestroyInvalidator();
405 this->delegate_.CreateInvalidator("fake_state",
[email protected]08a6f9992012-09-07 19:19:16406 this->fake_tracker_.AsWeakPtr());
[email protected]33596da2012-08-31 23:39:25407 invalidator = this->delegate_.GetInvalidator();
408
409 // Should do nothing.
410 invalidator->SetStateDeprecated("more_spurious_fake_state");
411 this->delegate_.WaitForInvalidator();
[email protected]8cdb6892012-10-03 05:54:40412 EXPECT_EQ("fake_state", this->fake_tracker_.GetBootstrapData());
[email protected]33596da2012-08-31 23:39:25413}
414
415REGISTER_TYPED_TEST_CASE_P(InvalidatorTest,
416 Basic, MultipleHandlers, EmptySetUnregisters,
[email protected]08a6f9992012-09-07 19:19:16417 GetInvalidatorStateAlwaysCurrent, MigrateState);
[email protected]33596da2012-08-31 23:39:25418
419} // namespace syncer
420
421#endif // SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_