Home | History | Annotate | Download | only in sessions
      1 // 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 #include "sync/sessions/nudge_tracker.h"
      6 
      7 #include "sync/internal_api/public/base/model_type_invalidation_map.h"
      8 #include "testing/gtest/include/gtest/gtest.h"
      9 
     10 namespace syncer {
     11 
     12 namespace {
     13 
     14 testing::AssertionResult ModelTypeSetEquals(ModelTypeSet a, ModelTypeSet b) {
     15   if (a.Equals(b)) {
     16     return testing::AssertionSuccess();
     17   } else {
     18     return testing::AssertionFailure()
     19         << "Left side " << ModelTypeSetToString(a)
     20         << ", does not match rigth side: " << ModelTypeSetToString(b);
     21   }
     22 }
     23 
     24 }  // namespace
     25 
     26 namespace sessions {
     27 
     28 class NudgeTrackerTest : public ::testing::Test {
     29  public:
     30   static size_t GetHintBufferSize() {
     31     // Assumes that no test has adjusted this size.
     32     return NudgeTracker::kDefaultMaxPayloadsPerType;
     33   }
     34 
     35   bool InvalidationsOutOfSync(const NudgeTracker& nudge_tracker) {
     36     // We don't currently track invalidations out of sync on a per-type basis.
     37     sync_pb::GetUpdateTriggers gu_trigger;
     38     nudge_tracker.FillProtoMessage(BOOKMARKS, &gu_trigger);
     39     return gu_trigger.invalidations_out_of_sync();
     40   }
     41 
     42   int ProtoLocallyModifiedCount(const NudgeTracker& nudge_tracker,
     43                                 ModelType type) {
     44     sync_pb::GetUpdateTriggers gu_trigger;
     45     nudge_tracker.FillProtoMessage(type, &gu_trigger);
     46     return gu_trigger.local_modification_nudges();
     47   }
     48 
     49   int ProtoRefreshRequestedCount(const NudgeTracker& nudge_tracker,
     50                                  ModelType type) {
     51     sync_pb::GetUpdateTriggers gu_trigger;
     52     nudge_tracker.FillProtoMessage(type, &gu_trigger);
     53     return gu_trigger.datatype_refresh_nudges();
     54   }
     55 };
     56 
     57 // Exercise an empty NudgeTracker.
     58 // Use with valgrind to detect uninitialized members.
     59 TEST_F(NudgeTrackerTest, EmptyNudgeTracker) {
     60   NudgeTracker nudge_tracker;
     61 
     62   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
     63   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
     64   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::UNKNOWN,
     65             nudge_tracker.updates_source());
     66 
     67   sync_pb::GetUpdateTriggers gu_trigger;
     68   nudge_tracker.FillProtoMessage(BOOKMARKS, &gu_trigger);
     69 
     70   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::UNKNOWN,
     71             nudge_tracker.updates_source());
     72 }
     73 
     74 // Verify that nudges override each other based on a priority order.
     75 // LOCAL < DATATYPE_REFRESH < NOTIFICATION
     76 TEST_F(NudgeTrackerTest, SourcePriorities) {
     77   NudgeTracker nudge_tracker;
     78 
     79   // Track a local nudge.
     80   nudge_tracker.RecordLocalChange(ModelTypeSet(BOOKMARKS));
     81   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::LOCAL,
     82             nudge_tracker.updates_source());
     83 
     84   // A refresh request will override it.
     85   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(TYPED_URLS));
     86   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH,
     87             nudge_tracker.updates_source());
     88 
     89   // Another local nudge will not be enough to change it.
     90   nudge_tracker.RecordLocalChange(ModelTypeSet(BOOKMARKS));
     91   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH,
     92             nudge_tracker.updates_source());
     93 
     94   // An invalidation will override the refresh request source.
     95   ModelTypeInvalidationMap invalidation_map =
     96       ModelTypeSetToInvalidationMap(ModelTypeSet(PREFERENCES),
     97                                     std::string("hint"));
     98   nudge_tracker.RecordRemoteInvalidation(invalidation_map);
     99   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::NOTIFICATION,
    100             nudge_tracker.updates_source());
    101 
    102   // Neither local nudges nor refresh requests will override it.
    103   nudge_tracker.RecordLocalChange(ModelTypeSet(BOOKMARKS));
    104   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::NOTIFICATION,
    105             nudge_tracker.updates_source());
    106   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(TYPED_URLS));
    107   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::NOTIFICATION,
    108             nudge_tracker.updates_source());
    109 }
    110 
    111 TEST_F(NudgeTrackerTest, HintCoalescing) {
    112   NudgeTracker nudge_tracker;
    113 
    114   // Easy case: record one hint.
    115   {
    116     ModelTypeInvalidationMap invalidation_map =
    117         ModelTypeSetToInvalidationMap(ModelTypeSet(BOOKMARKS),
    118                                       std::string("bm_hint_1"));
    119     nudge_tracker.RecordRemoteInvalidation(invalidation_map);
    120 
    121     sync_pb::GetUpdateTriggers gu_trigger;
    122     nudge_tracker.FillProtoMessage(BOOKMARKS, &gu_trigger);
    123     ASSERT_EQ(1, gu_trigger.notification_hint_size());
    124     EXPECT_EQ("bm_hint_1", gu_trigger.notification_hint(0));
    125     EXPECT_FALSE(gu_trigger.client_dropped_hints());
    126   }
    127 
    128   // Record a second hint for the same type.
    129   {
    130     ModelTypeInvalidationMap invalidation_map =
    131         ModelTypeSetToInvalidationMap(ModelTypeSet(BOOKMARKS),
    132                                       std::string("bm_hint_2"));
    133     nudge_tracker.RecordRemoteInvalidation(invalidation_map);
    134 
    135     sync_pb::GetUpdateTriggers gu_trigger;
    136     nudge_tracker.FillProtoMessage(BOOKMARKS, &gu_trigger);
    137     ASSERT_EQ(2, gu_trigger.notification_hint_size());
    138 
    139     // Expect the most hint recent is last in the list.
    140     EXPECT_EQ("bm_hint_1", gu_trigger.notification_hint(0));
    141     EXPECT_EQ("bm_hint_2", gu_trigger.notification_hint(1));
    142     EXPECT_FALSE(gu_trigger.client_dropped_hints());
    143   }
    144 
    145   // Record a hint for a different type.
    146   {
    147     ModelTypeInvalidationMap invalidation_map =
    148         ModelTypeSetToInvalidationMap(ModelTypeSet(PASSWORDS),
    149                                       std::string("pw_hint_1"));
    150     nudge_tracker.RecordRemoteInvalidation(invalidation_map);
    151 
    152     // Re-verify the bookmarks to make sure they're unaffected.
    153     sync_pb::GetUpdateTriggers bm_gu_trigger;
    154     nudge_tracker.FillProtoMessage(BOOKMARKS, &bm_gu_trigger);
    155     ASSERT_EQ(2, bm_gu_trigger.notification_hint_size());
    156     EXPECT_EQ("bm_hint_1", bm_gu_trigger.notification_hint(0));
    157     EXPECT_EQ("bm_hint_2",
    158               bm_gu_trigger.notification_hint(1)); // most recent last.
    159     EXPECT_FALSE(bm_gu_trigger.client_dropped_hints());
    160 
    161     // Verify the new type, too.
    162     sync_pb::GetUpdateTriggers pw_gu_trigger;
    163     nudge_tracker.FillProtoMessage(PASSWORDS, &pw_gu_trigger);
    164     ASSERT_EQ(1, pw_gu_trigger.notification_hint_size());
    165     EXPECT_EQ("pw_hint_1", pw_gu_trigger.notification_hint(0));
    166     EXPECT_FALSE(pw_gu_trigger.client_dropped_hints());
    167   }
    168 }
    169 
    170 TEST_F(NudgeTrackerTest, DropHintsLocally) {
    171   NudgeTracker nudge_tracker;
    172   ModelTypeInvalidationMap invalidation_map =
    173       ModelTypeSetToInvalidationMap(ModelTypeSet(BOOKMARKS),
    174                                     std::string("hint"));
    175 
    176   for (size_t i = 0; i < GetHintBufferSize(); ++i) {
    177     nudge_tracker.RecordRemoteInvalidation(invalidation_map);
    178   }
    179   {
    180     sync_pb::GetUpdateTriggers gu_trigger;
    181     nudge_tracker.FillProtoMessage(BOOKMARKS, &gu_trigger);
    182     EXPECT_EQ(GetHintBufferSize(),
    183               static_cast<size_t>(gu_trigger.notification_hint_size()));
    184     EXPECT_FALSE(gu_trigger.client_dropped_hints());
    185   }
    186 
    187   // Force an overflow.
    188   ModelTypeInvalidationMap invalidation_map2 =
    189       ModelTypeSetToInvalidationMap(ModelTypeSet(BOOKMARKS),
    190                                     std::string("new_hint"));
    191   nudge_tracker.RecordRemoteInvalidation(invalidation_map2);
    192 
    193   {
    194     sync_pb::GetUpdateTriggers gu_trigger;
    195     nudge_tracker.FillProtoMessage(BOOKMARKS, &gu_trigger);
    196     EXPECT_EQ(GetHintBufferSize(),
    197               static_cast<size_t>(gu_trigger.notification_hint_size()));
    198     EXPECT_TRUE(gu_trigger.client_dropped_hints());
    199 
    200     // Verify the newest hint was not dropped and is the last in the list.
    201     EXPECT_EQ("new_hint", gu_trigger.notification_hint(GetHintBufferSize()-1));
    202 
    203     // Verify the oldest hint, too.
    204     EXPECT_EQ("hint", gu_trigger.notification_hint(0));
    205   }
    206 }
    207 
    208 // TODO(rlarocque): Add trickles support.  See crbug.com/223437.
    209 // TEST_F(NudgeTrackerTest, DropHintsAtServer);
    210 
    211 // Checks the behaviour of the invalidations-out-of-sync flag.
    212 TEST_F(NudgeTrackerTest, EnableDisableInvalidations) {
    213   NudgeTracker nudge_tracker;
    214 
    215   // By default, assume we're out of sync with the invalidation server.
    216   EXPECT_TRUE(InvalidationsOutOfSync(nudge_tracker));
    217 
    218   // Simply enabling invalidations does not bring us back into sync.
    219   nudge_tracker.OnInvalidationsEnabled();
    220   EXPECT_TRUE(InvalidationsOutOfSync(nudge_tracker));
    221 
    222   // We must successfully complete a sync cycle while invalidations are enabled
    223   // to be sure that we're in sync.
    224   nudge_tracker.RecordSuccessfulSyncCycle();
    225   EXPECT_FALSE(InvalidationsOutOfSync(nudge_tracker));
    226 
    227   // If the invalidator malfunctions, we go become unsynced again.
    228   nudge_tracker.OnInvalidationsDisabled();
    229   EXPECT_TRUE(InvalidationsOutOfSync(nudge_tracker));
    230 
    231   // A sync cycle while invalidations are disabled won't reset the flag.
    232   nudge_tracker.RecordSuccessfulSyncCycle();
    233   EXPECT_TRUE(InvalidationsOutOfSync(nudge_tracker));
    234 
    235   // Nor will the re-enabling of invalidations be sufficient, even now that
    236   // we've had a successful sync cycle.
    237   nudge_tracker.RecordSuccessfulSyncCycle();
    238   EXPECT_TRUE(InvalidationsOutOfSync(nudge_tracker));
    239 }
    240 
    241 // Tests that locally modified types are correctly written out to the
    242 // GetUpdateTriggers proto.
    243 TEST_F(NudgeTrackerTest, WriteLocallyModifiedTypesToProto) {
    244   NudgeTracker nudge_tracker;
    245 
    246   // Should not be locally modified by default.
    247   EXPECT_EQ(0, ProtoLocallyModifiedCount(nudge_tracker, PREFERENCES));
    248 
    249   // Record a local bookmark change.  Verify it was registered correctly.
    250   nudge_tracker.RecordLocalChange(ModelTypeSet(PREFERENCES));
    251   EXPECT_EQ(1, ProtoLocallyModifiedCount(nudge_tracker, PREFERENCES));
    252 
    253   // Record a successful sync cycle.  Verify the count is cleared.
    254   nudge_tracker.RecordSuccessfulSyncCycle();
    255   EXPECT_EQ(0, ProtoLocallyModifiedCount(nudge_tracker, PREFERENCES));
    256 }
    257 
    258 // Tests that refresh requested types are correctly written out to the
    259 // GetUpdateTriggers proto.
    260 TEST_F(NudgeTrackerTest, WriteRefreshRequestedTypesToProto) {
    261   NudgeTracker nudge_tracker;
    262 
    263   // There should be no refresh requested by default.
    264   EXPECT_EQ(0, ProtoRefreshRequestedCount(nudge_tracker, SESSIONS));
    265 
    266   // Record a local refresh request.  Verify it was registered correctly.
    267   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(SESSIONS));
    268   EXPECT_EQ(1, ProtoRefreshRequestedCount(nudge_tracker, SESSIONS));
    269 
    270   // Record a successful sync cycle.  Verify the count is cleared.
    271   nudge_tracker.RecordSuccessfulSyncCycle();
    272   EXPECT_EQ(0, ProtoRefreshRequestedCount(nudge_tracker, SESSIONS));
    273 }
    274 
    275 // Basic tests for the IsSyncRequired() flag.
    276 TEST_F(NudgeTrackerTest, IsSyncRequired) {
    277   NudgeTracker nudge_tracker;
    278   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    279 
    280   // Local changes.
    281   nudge_tracker.RecordLocalChange(ModelTypeSet(SESSIONS));
    282   EXPECT_TRUE(nudge_tracker.IsSyncRequired());
    283   nudge_tracker.RecordSuccessfulSyncCycle();
    284   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    285 
    286   // Refresh requests.
    287   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(SESSIONS));
    288   EXPECT_TRUE(nudge_tracker.IsSyncRequired());
    289   nudge_tracker.RecordSuccessfulSyncCycle();
    290   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    291 
    292   // Invalidations.
    293   ModelTypeInvalidationMap invalidation_map =
    294       ModelTypeSetToInvalidationMap(ModelTypeSet(PREFERENCES),
    295                                     std::string("hint"));
    296   nudge_tracker.RecordRemoteInvalidation(invalidation_map);
    297   EXPECT_TRUE(nudge_tracker.IsSyncRequired());
    298   nudge_tracker.RecordSuccessfulSyncCycle();
    299   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    300 }
    301 
    302 // Basic tests for the IsGetUpdatesRequired() flag.
    303 TEST_F(NudgeTrackerTest, IsGetUpdatesRequired) {
    304   NudgeTracker nudge_tracker;
    305   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    306 
    307   // Local changes.
    308   nudge_tracker.RecordLocalChange(ModelTypeSet(SESSIONS));
    309   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    310   nudge_tracker.RecordSuccessfulSyncCycle();
    311   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    312 
    313   // Refresh requests.
    314   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(SESSIONS));
    315   EXPECT_TRUE(nudge_tracker.IsGetUpdatesRequired());
    316   nudge_tracker.RecordSuccessfulSyncCycle();
    317   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    318 
    319   // Invalidations.
    320   ModelTypeInvalidationMap invalidation_map =
    321       ModelTypeSetToInvalidationMap(ModelTypeSet(PREFERENCES),
    322                                     std::string("hint"));
    323   nudge_tracker.RecordRemoteInvalidation(invalidation_map);
    324   EXPECT_TRUE(nudge_tracker.IsGetUpdatesRequired());
    325   nudge_tracker.RecordSuccessfulSyncCycle();
    326   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    327 }
    328 
    329 // Test IsSyncRequired() responds correctly to data type throttling.
    330 TEST_F(NudgeTrackerTest, IsSyncRequired_Throttling) {
    331   NudgeTracker nudge_tracker;
    332   const base::TimeTicks t0 = base::TimeTicks::FromInternalValue(1234);
    333   const base::TimeDelta throttle_length = base::TimeDelta::FromMinutes(10);
    334   const base::TimeTicks t1 = t0 + throttle_length;
    335 
    336   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    337 
    338   // A local change to sessions enables the flag.
    339   nudge_tracker.RecordLocalChange(ModelTypeSet(SESSIONS));
    340   EXPECT_TRUE(nudge_tracker.IsSyncRequired());
    341 
    342   // But the throttling of sessions unsets it.
    343   nudge_tracker.SetTypesThrottledUntil(ModelTypeSet(SESSIONS),
    344                                        throttle_length,
    345                                        t0);
    346   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    347 
    348   // A refresh request for bookmarks means we have reason to sync again.
    349   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(BOOKMARKS));
    350   EXPECT_TRUE(nudge_tracker.IsSyncRequired());
    351 
    352   // A successful sync cycle means we took care of bookmarks.
    353   nudge_tracker.RecordSuccessfulSyncCycle();
    354   EXPECT_FALSE(nudge_tracker.IsSyncRequired());
    355 
    356   // But we still haven't dealt with sessions.  We'll need to remember
    357   // that sessions are out of sync and re-enable the flag when their
    358   // throttling interval expires.
    359   nudge_tracker.UpdateTypeThrottlingState(t1);
    360   EXPECT_FALSE(nudge_tracker.IsTypeThrottled(SESSIONS));
    361   EXPECT_TRUE(nudge_tracker.IsSyncRequired());
    362 }
    363 
    364 // Test IsGetUpdatesRequired() responds correctly to data type throttling.
    365 TEST_F(NudgeTrackerTest, IsGetUpdatesRequired_Throttling) {
    366   NudgeTracker nudge_tracker;
    367   const base::TimeTicks t0 = base::TimeTicks::FromInternalValue(1234);
    368   const base::TimeDelta throttle_length = base::TimeDelta::FromMinutes(10);
    369   const base::TimeTicks t1 = t0 + throttle_length;
    370 
    371   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    372 
    373   // A refresh request to sessions enables the flag.
    374   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(SESSIONS));
    375   EXPECT_TRUE(nudge_tracker.IsGetUpdatesRequired());
    376 
    377   // But the throttling of sessions unsets it.
    378   nudge_tracker.SetTypesThrottledUntil(ModelTypeSet(SESSIONS),
    379                                        throttle_length,
    380                                        t0);
    381   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    382 
    383   // A refresh request for bookmarks means we have reason to sync again.
    384   nudge_tracker.RecordLocalRefreshRequest(ModelTypeSet(BOOKMARKS));
    385   EXPECT_TRUE(nudge_tracker.IsGetUpdatesRequired());
    386 
    387   // A successful sync cycle means we took care of bookmarks.
    388   nudge_tracker.RecordSuccessfulSyncCycle();
    389   EXPECT_FALSE(nudge_tracker.IsGetUpdatesRequired());
    390 
    391   // But we still haven't dealt with sessions.  We'll need to remember
    392   // that sessions are out of sync and re-enable the flag when their
    393   // throttling interval expires.
    394   nudge_tracker.UpdateTypeThrottlingState(t1);
    395   EXPECT_FALSE(nudge_tracker.IsTypeThrottled(SESSIONS));
    396   EXPECT_TRUE(nudge_tracker.IsGetUpdatesRequired());
    397 }
    398 
    399 // Tests throttling-related getter functions when no types are throttled.
    400 TEST_F(NudgeTrackerTest, NoTypesThrottled) {
    401   NudgeTracker nudge_tracker;
    402 
    403   EXPECT_FALSE(nudge_tracker.IsAnyTypeThrottled());
    404   EXPECT_FALSE(nudge_tracker.IsTypeThrottled(SESSIONS));
    405   EXPECT_TRUE(nudge_tracker.GetThrottledTypes().Empty());
    406 }
    407 
    408 // Tests throttling-related getter functions when some types are throttled.
    409 TEST_F(NudgeTrackerTest, ThrottleAndUnthrottle) {
    410   NudgeTracker nudge_tracker;
    411   const base::TimeTicks t0 = base::TimeTicks::FromInternalValue(1234);
    412   const base::TimeDelta throttle_length = base::TimeDelta::FromMinutes(10);
    413   const base::TimeTicks t1 = t0 + throttle_length;
    414 
    415   nudge_tracker.SetTypesThrottledUntil(ModelTypeSet(SESSIONS, PREFERENCES),
    416                                        throttle_length,
    417                                        t0);
    418 
    419   EXPECT_TRUE(nudge_tracker.IsAnyTypeThrottled());
    420   EXPECT_TRUE(nudge_tracker.IsTypeThrottled(SESSIONS));
    421   EXPECT_TRUE(nudge_tracker.IsTypeThrottled(PREFERENCES));
    422   EXPECT_FALSE(nudge_tracker.GetThrottledTypes().Empty());
    423   EXPECT_EQ(throttle_length, nudge_tracker.GetTimeUntilNextUnthrottle(t0));
    424 
    425   nudge_tracker.UpdateTypeThrottlingState(t1);
    426 
    427   EXPECT_FALSE(nudge_tracker.IsAnyTypeThrottled());
    428   EXPECT_FALSE(nudge_tracker.IsTypeThrottled(SESSIONS));
    429   EXPECT_TRUE(nudge_tracker.GetThrottledTypes().Empty());
    430 }
    431 
    432 TEST_F(NudgeTrackerTest, OverlappingThrottleIntervals) {
    433   NudgeTracker nudge_tracker;
    434   const base::TimeTicks t0 = base::TimeTicks::FromInternalValue(1234);
    435   const base::TimeDelta throttle1_length = base::TimeDelta::FromMinutes(10);
    436   const base::TimeDelta throttle2_length = base::TimeDelta::FromMinutes(20);
    437   const base::TimeTicks t1 = t0 + throttle1_length;
    438   const base::TimeTicks t2 = t0 + throttle2_length;
    439 
    440   // Setup the longer of two intervals.
    441   nudge_tracker.SetTypesThrottledUntil(ModelTypeSet(SESSIONS, PREFERENCES),
    442                                        throttle2_length,
    443                                        t0);
    444   EXPECT_TRUE(ModelTypeSetEquals(
    445           ModelTypeSet(SESSIONS, PREFERENCES),
    446           nudge_tracker.GetThrottledTypes()));
    447   EXPECT_EQ(throttle2_length,
    448             nudge_tracker.GetTimeUntilNextUnthrottle(t0));
    449 
    450   // Setup the shorter interval.
    451   nudge_tracker.SetTypesThrottledUntil(ModelTypeSet(SESSIONS, BOOKMARKS),
    452                                        throttle1_length,
    453                                        t0);
    454   EXPECT_TRUE(ModelTypeSetEquals(
    455           ModelTypeSet(SESSIONS, PREFERENCES, BOOKMARKS),
    456           nudge_tracker.GetThrottledTypes()));
    457   EXPECT_EQ(throttle1_length,
    458             nudge_tracker.GetTimeUntilNextUnthrottle(t0));
    459 
    460   // Expire the first interval.
    461   nudge_tracker.UpdateTypeThrottlingState(t1);
    462 
    463   // SESSIONS appeared in both intervals.  We expect it will be throttled for
    464   // the longer of the two, so it's still throttled at time t1.
    465   EXPECT_TRUE(ModelTypeSetEquals(
    466           ModelTypeSet(SESSIONS, PREFERENCES),
    467           nudge_tracker.GetThrottledTypes()));
    468   EXPECT_EQ(throttle2_length - throttle1_length,
    469             nudge_tracker.GetTimeUntilNextUnthrottle(t1));
    470 
    471   // Expire the second interval.
    472   nudge_tracker.UpdateTypeThrottlingState(t2);
    473   EXPECT_TRUE(nudge_tracker.GetThrottledTypes().Empty());
    474 }
    475 
    476 }  // namespace sessions
    477 }  // namespace syncer
    478