1 // Copyright 2013 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 "cc/animation/scroll_offset_animation_curve.h"
6 
7 #include "cc/animation/scroll_offset_animation_curve_factory.h"
8 #include "cc/animation/timing_function.h"
9 #include "cc/test/geometry_test_utils.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 
12 using DurationBehavior = cc::ScrollOffsetAnimationCurve::DurationBehavior;
13 
14 const double kConstantDuration = 9.0;
15 const double kDurationDivisor = 60.0;
16 const double kInverseDeltaMaxDuration = 12.0;
17 
18 namespace cc {
19 namespace {
20 
21 // This is the value of the default Impulse bezier curve when t = 0.5
22 constexpr double halfway_through_default_impulse_curve = 0.874246;
23 
TEST(ScrollOffsetAnimationCurveTest,DeltaBasedDuration)24 TEST(ScrollOffsetAnimationCurveTest, DeltaBasedDuration) {
25   gfx::ScrollOffset target_value(100.f, 200.f);
26   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
27       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
28           target_value));
29 
30   curve->SetInitialValue(target_value);
31   EXPECT_DOUBLE_EQ(0.0, curve->Duration().InSecondsF());
32 
33   // x decreases, y stays the same.
34   curve->SetInitialValue(gfx::ScrollOffset(136.f, 200.f));
35   EXPECT_DOUBLE_EQ(0.1, curve->Duration().InSecondsF());
36 
37   // x increases, y stays the same.
38   curve->SetInitialValue(gfx::ScrollOffset(19.f, 200.f));
39   EXPECT_DOUBLE_EQ(0.15, curve->Duration().InSecondsF());
40 
41   // x stays the same, y decreases.
42   curve->SetInitialValue(gfx::ScrollOffset(100.f, 344.f));
43   EXPECT_DOUBLE_EQ(0.2, curve->Duration().InSecondsF());
44 
45   // x stays the same, y increases.
46   curve->SetInitialValue(gfx::ScrollOffset(100.f, 191.f));
47   EXPECT_DOUBLE_EQ(0.05, curve->Duration().InSecondsF());
48 
49   // x decreases, y decreases.
50   curve->SetInitialValue(gfx::ScrollOffset(32500.f, 500.f));
51   EXPECT_DOUBLE_EQ(0.7, curve->Duration().InSecondsF());
52 
53   // x decreases, y increases.
54   curve->SetInitialValue(gfx::ScrollOffset(150.f, 119.f));
55   EXPECT_DOUBLE_EQ(0.15, curve->Duration().InSecondsF());
56 
57   // x increases, y decreases.
58   curve->SetInitialValue(gfx::ScrollOffset(0.f, 14600.f));
59   EXPECT_DOUBLE_EQ(0.7, curve->Duration().InSecondsF());
60 
61   // x increases, y increases.
62   curve->SetInitialValue(gfx::ScrollOffset(95.f, 191.f));
63   EXPECT_DOUBLE_EQ(0.05, curve->Duration().InSecondsF());
64 }
65 
TEST(ScrollOffsetAnimationCurveTest,GetValue)66 TEST(ScrollOffsetAnimationCurveTest, GetValue) {
67   gfx::ScrollOffset initial_value(2.f, 40.f);
68   gfx::ScrollOffset target_value(10.f, 20.f);
69   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
70       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
71           target_value));
72   curve->SetInitialValue(initial_value);
73 
74   base::TimeDelta duration = curve->Duration();
75   EXPECT_GT(curve->Duration().InSecondsF(), 0);
76   EXPECT_LT(curve->Duration().InSecondsF(), 0.1);
77 
78   EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, curve->Type());
79   EXPECT_EQ(duration, curve->Duration());
80 
81   EXPECT_VECTOR2DF_EQ(initial_value,
82                       curve->GetValue(base::TimeDelta::FromSecondsD(-1.0)));
83   EXPECT_VECTOR2DF_EQ(initial_value, curve->GetValue(base::TimeDelta()));
84   EXPECT_VECTOR2DF_NEAR(gfx::ScrollOffset(6.f, 30.f),
85                         curve->GetValue(duration * 0.5f), 0.00025);
86   EXPECT_VECTOR2DF_EQ(target_value, curve->GetValue(duration));
87   EXPECT_VECTOR2DF_EQ(
88       target_value,
89       curve->GetValue(duration + base::TimeDelta::FromSecondsD(1.0)));
90 
91   // Verify that GetValue takes the timing function into account.
92   gfx::ScrollOffset value = curve->GetValue(duration * 0.25f);
93   EXPECT_NEAR(3.0333f, value.x(), 0.0002f);
94   EXPECT_NEAR(37.4168f, value.y(), 0.0002f);
95 }
96 
97 // Verify that a clone behaves exactly like the original.
TEST(ScrollOffsetAnimationCurveTest,Clone)98 TEST(ScrollOffsetAnimationCurveTest, Clone) {
99   gfx::ScrollOffset initial_value(2.f, 40.f);
100   gfx::ScrollOffset target_value(10.f, 20.f);
101   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
102       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
103           target_value));
104   curve->SetInitialValue(initial_value);
105   base::TimeDelta duration = curve->Duration();
106 
107   std::unique_ptr<AnimationCurve> clone(curve->Clone());
108 
109   EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, clone->Type());
110   EXPECT_EQ(duration, clone->Duration());
111 
112   EXPECT_VECTOR2DF_EQ(initial_value,
113                       clone->ToScrollOffsetAnimationCurve()->GetValue(
114                           base::TimeDelta::FromSecondsD(-1.0)));
115   EXPECT_VECTOR2DF_EQ(
116       initial_value,
117       clone->ToScrollOffsetAnimationCurve()->GetValue(base::TimeDelta()));
118   EXPECT_VECTOR2DF_NEAR(
119       gfx::ScrollOffset(6.f, 30.f),
120       clone->ToScrollOffsetAnimationCurve()->GetValue(duration * 0.5f),
121       0.00025);
122   EXPECT_VECTOR2DF_EQ(
123       target_value, clone->ToScrollOffsetAnimationCurve()->GetValue(duration));
124   EXPECT_VECTOR2DF_EQ(target_value,
125                       clone->ToScrollOffsetAnimationCurve()->GetValue(
126                           duration + base::TimeDelta::FromSecondsD(1.f)));
127 
128   // Verify that the timing function was cloned correctly.
129   gfx::ScrollOffset value =
130       clone->ToScrollOffsetAnimationCurve()->GetValue(duration * 0.25f);
131   EXPECT_NEAR(3.0333f, value.x(), 0.0002f);
132   EXPECT_NEAR(37.4168f, value.y(), 0.0002f);
133 }
134 
TEST(ScrollOffsetAnimationCurveTest,EaseInOutUpdateTarget)135 TEST(ScrollOffsetAnimationCurveTest, EaseInOutUpdateTarget) {
136   gfx::ScrollOffset initial_value(0.f, 0.f);
137   gfx::ScrollOffset target_value(0.f, 3600.f);
138   double duration = kConstantDuration / kDurationDivisor;
139   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
140       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
141           target_value, DurationBehavior::CONSTANT));
142   curve->SetInitialValue(initial_value);
143   EXPECT_NEAR(duration, curve->Duration().InSecondsF(), 0.0002f);
144   EXPECT_NEAR(
145       1800.0,
146       curve->GetValue(base::TimeDelta::FromSecondsD(duration / 2.0)).y(),
147       0.0002f);
148   EXPECT_NEAR(3600.0,
149               curve->GetValue(base::TimeDelta::FromSecondsD(duration)).y(),
150               0.0002f);
151 
152   curve->UpdateTarget(base::TimeDelta::FromSecondsD(duration / 2),
153                       gfx::ScrollOffset(0.0, 9900.0));
154 
155   EXPECT_NEAR(duration * 1.5, curve->Duration().InSecondsF(), 0.0002f);
156   EXPECT_NEAR(
157       1800.0,
158       curve->GetValue(base::TimeDelta::FromSecondsD(duration / 2.0)).y(),
159       0.0002f);
160   EXPECT_NEAR(6827.6,
161               curve->GetValue(base::TimeDelta::FromSecondsD(duration)).y(),
162               0.1f);
163   EXPECT_NEAR(
164       9900.0,
165       curve->GetValue(base::TimeDelta::FromSecondsD(duration * 1.5)).y(),
166       0.0002f);
167 
168   curve->UpdateTarget(base::TimeDelta::FromSecondsD(duration),
169                       gfx::ScrollOffset(0.0, 7200.0));
170 
171   // A closer target at high velocity reduces the duration.
172   EXPECT_NEAR(duration * 1.0794, curve->Duration().InSecondsF(), 0.0002f);
173   EXPECT_NEAR(6827.6,
174               curve->GetValue(base::TimeDelta::FromSecondsD(duration)).y(),
175               0.1f);
176   EXPECT_NEAR(
177       7200.0,
178       curve->GetValue(base::TimeDelta::FromSecondsD(duration * 1.08)).y(),
179       0.0002f);
180 }
181 
TEST(ScrollOffsetAnimationCurveTest,ImpulseUpdateTarget)182 TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTarget) {
183   gfx::ScrollOffset initial_value(0.f, 0.f);
184   gfx::ScrollOffset initial_target_value(0.f, 3600.f);
185   gfx::Vector2dF initial_delta = initial_target_value.DeltaFrom(initial_value);
186   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
187       ScrollOffsetAnimationCurveFactory::CreateImpulseAnimationForTesting(
188           initial_target_value));
189   curve->SetInitialValue(initial_value);
190 
191   base::TimeDelta initial_duration =
192       ScrollOffsetAnimationCurve::ImpulseSegmentDuration(initial_delta,
193                                                          base::TimeDelta());
194   EXPECT_NEAR(initial_duration.InSecondsF(), curve->Duration().InSecondsF(),
195               0.0002f);
196   EXPECT_NEAR(initial_delta.y() * halfway_through_default_impulse_curve,
197               curve->GetValue(initial_duration / 2).y(), 0.01f);
198   EXPECT_NEAR(initial_delta.y(), curve->GetValue(initial_duration).y(),
199               0.0002f);
200 
201   base::TimeDelta time_of_update = initial_duration / 2;
202   gfx::ScrollOffset distance_halfway_through_initial_animation =
203       curve->GetValue(time_of_update);
204 
205   gfx::ScrollOffset new_target_value(0.f, 9900.f);
206   curve->UpdateTarget(time_of_update, new_target_value);
207 
208   gfx::Vector2dF new_delta =
209       new_target_value.DeltaFrom(distance_halfway_through_initial_animation);
210   base::TimeDelta updated_segment_duration =
211       ScrollOffsetAnimationCurve::ImpulseSegmentDuration(new_delta,
212                                                          base::TimeDelta());
213 
214   base::TimeDelta overall_duration = time_of_update + updated_segment_duration;
215   EXPECT_NEAR(overall_duration.InSecondsF(), curve->Duration().InSecondsF(),
216               0.0002f);
217   EXPECT_NEAR(distance_halfway_through_initial_animation.y(),
218               curve->GetValue(time_of_update).y(), 0.01f);
219   EXPECT_NEAR(new_target_value.y(), curve->GetValue(overall_duration).y(),
220               0.0002f);
221 
222   // Ensure that UpdateTarget increases the initial slope of the generated curve
223   // (for velocity matching). To test this, we check if the value is greater
224   // than the default value would be half way through.
225   // Also - to ensure it isn't passing just due to floating point imprecision,
226   // some epsilon is added to the default amount.
227   EXPECT_LT(
228       new_delta.y() * halfway_through_default_impulse_curve + 0.01f,
229       curve->GetValue(time_of_update + (updated_segment_duration / 2)).y());
230 }
231 
TEST(ScrollOffsetAnimationCurveTest,ImpulseUpdateTargetSwitchDirections)232 TEST(ScrollOffsetAnimationCurveTest, ImpulseUpdateTargetSwitchDirections) {
233   gfx::ScrollOffset initial_value(0.f, 0.f);
234   gfx::ScrollOffset initial_target_value(0.f, 200.f);
235   double initial_duration =
236       ScrollOffsetAnimationCurve::ImpulseSegmentDuration(
237           gfx::Vector2dF(initial_target_value.x(), initial_target_value.y()),
238           base::TimeDelta())
239           .InSecondsF();
240 
241   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
242       ScrollOffsetAnimationCurveFactory::CreateImpulseAnimationForTesting(
243           initial_target_value));
244   curve->SetInitialValue(initial_value);
245   EXPECT_NEAR(initial_duration, curve->Duration().InSecondsF(), 0.0002f);
246   EXPECT_NEAR(
247       initial_target_value.y() * halfway_through_default_impulse_curve,
248       curve->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0))
249           .y(),
250       0.01f);
251 
252   // Animate back to 0. This should force the new curve's initial velocity to be
253   // 0, so the default curve will be generated.
254   gfx::ScrollOffset updated_initial_value = gfx::ScrollOffset(
255       0, initial_target_value.y() * halfway_through_default_impulse_curve);
256   gfx::ScrollOffset updated_target = gfx::ScrollOffset(0.f, 0.f);
257   curve->UpdateTarget(base::TimeDelta::FromSecondsD(initial_duration / 2),
258                       updated_target);
259 
260   double updated_duration =
261       ScrollOffsetAnimationCurve::ImpulseSegmentDuration(
262           gfx::Vector2dF(updated_initial_value.x(), updated_initial_value.y()),
263           base::TimeDelta())
264           .InSecondsF();
265 
266   EXPECT_NEAR(
267       initial_target_value.y() * halfway_through_default_impulse_curve,
268       curve->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0))
269           .y(),
270       0.01f);
271 
272   EXPECT_NEAR(
273       updated_initial_value.y() * (1.0 - halfway_through_default_impulse_curve),
274       curve
275           ->GetValue(base::TimeDelta::FromSecondsD(initial_duration / 2.0 +
276                                                    updated_duration / 2.0))
277           .y(),
278       0.01f);
279   EXPECT_NEAR(0.0,
280               curve
281                   ->GetValue(base::TimeDelta::FromSecondsD(
282                       initial_duration / 2.0 + updated_duration))
283                   .y(),
284               0.0002f);
285 }
286 
TEST(ScrollOffsetAnimationCurveTest,InverseDeltaDuration)287 TEST(ScrollOffsetAnimationCurveTest, InverseDeltaDuration) {
288   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
289       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
290           gfx::ScrollOffset(0.f, 100.f), DurationBehavior::INVERSE_DELTA));
291 
292   curve->SetInitialValue(gfx::ScrollOffset());
293   double smallDeltaDuration = curve->Duration().InSecondsF();
294 
295   curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f),
296                       gfx::ScrollOffset(0.f, 300.f));
297   double mediumDeltaDuration = curve->Duration().InSecondsF();
298 
299   curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f),
300                       gfx::ScrollOffset(0.f, 500.f));
301   double largeDeltaDuration = curve->Duration().InSecondsF();
302 
303   EXPECT_GT(smallDeltaDuration, mediumDeltaDuration);
304   EXPECT_GT(mediumDeltaDuration, largeDeltaDuration);
305 
306   curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f),
307                       gfx::ScrollOffset(0.f, 5000.f));
308   EXPECT_EQ(largeDeltaDuration, curve->Duration().InSecondsF());
309 }
310 
TEST(ScrollOffsetAnimationCurveTest,LinearAnimation)311 TEST(ScrollOffsetAnimationCurveTest, LinearAnimation) {
312   // Testing autoscroll downwards for a scroller of length 1000px.
313   gfx::ScrollOffset current_offset(0.f, 0.f);
314   gfx::ScrollOffset target_offset(0.f, 1000.f);
315   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
316       ScrollOffsetAnimationCurveFactory::CreateLinearAnimationForTesting(
317           target_offset));
318 
319   const float autoscroll_velocity = 800.f;  // pixels per second.
320   curve->SetInitialValue(current_offset, base::TimeDelta(),
321                          autoscroll_velocity);
322   EXPECT_FLOAT_EQ(1.25f, curve->Duration().InSecondsF());
323 
324   // Test scrolling down from half way.
325   current_offset = gfx::ScrollOffset(0.f, 500.f);
326   curve->SetInitialValue(current_offset, base::TimeDelta(),
327                          autoscroll_velocity);
328   EXPECT_FLOAT_EQ(0.625f, curve->Duration().InSecondsF());
329 
330   // Test scrolling down when max_offset is reached.
331   current_offset = gfx::ScrollOffset(0.f, 1000.f);
332   curve->SetInitialValue(current_offset, base::TimeDelta(),
333                          autoscroll_velocity);
334   EXPECT_FLOAT_EQ(0.f, curve->Duration().InSecondsF());
335 }
336 
TEST(ScrollOffsetAnimationCurveTest,ImpulseDuration)337 TEST(ScrollOffsetAnimationCurveTest, ImpulseDuration) {
338   // The duration of an impulse-style curve in milliseconds is simply 1.5x the
339   // scroll distance in physical pixels, with a minimum of 200ms and a maximum
340   // of 500ms.
341   gfx::Vector2dF small_delta = gfx::Vector2dF(0.f, 100.f);
342   gfx::Vector2dF moderate_delta = gfx::Vector2dF(0.f, 250.f);
343   gfx::Vector2dF large_delta = gfx::Vector2dF(0.f, 400.f);
344 
345   base::TimeDelta duration = ScrollOffsetAnimationCurve::ImpulseSegmentDuration(
346       small_delta, base::TimeDelta());
347   EXPECT_FLOAT_EQ(duration.InMillisecondsF(), 200.f);
348 
349   duration = ScrollOffsetAnimationCurve::ImpulseSegmentDuration(
350       moderate_delta, base::TimeDelta());
351   EXPECT_NEAR(duration.InMillisecondsF(), moderate_delta.y() * 1.5f, 0.0002f);
352 
353   duration = ScrollOffsetAnimationCurve::ImpulseSegmentDuration(
354       large_delta, base::TimeDelta());
355   EXPECT_FLOAT_EQ(duration.InMillisecondsF(), 500.f);
356 }
357 
TEST(ScrollOffsetAnimationCurveTest,CurveWithDelay)358 TEST(ScrollOffsetAnimationCurveTest, CurveWithDelay) {
359   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
360       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
361           gfx::ScrollOffset(0.f, 100.f), DurationBehavior::INVERSE_DELTA));
362   double duration_in_seconds = kInverseDeltaMaxDuration / kDurationDivisor;
363   double delay_in_seconds = 0.02;
364   double curve_duration = duration_in_seconds - delay_in_seconds;
365 
366   curve->SetInitialValue(gfx::ScrollOffset(),
367                          base::TimeDelta::FromSecondsD(delay_in_seconds));
368   EXPECT_NEAR(curve_duration, curve->Duration().InSecondsF(), 0.0002f);
369 
370   curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.01f),
371                       gfx::ScrollOffset(0.f, 500.f));
372   EXPECT_GT(curve_duration, curve->Duration().InSecondsF());
373   EXPECT_EQ(gfx::ScrollOffset(0.f, 500.f), curve->target_value());
374 }
375 
TEST(ScrollOffsetAnimationCurveTest,CurveWithLargeDelay)376 TEST(ScrollOffsetAnimationCurveTest, CurveWithLargeDelay) {
377   DurationBehavior duration_hint = DurationBehavior::INVERSE_DELTA;
378   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
379       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
380           gfx::ScrollOffset(0.f, 100.f), duration_hint));
381   curve->SetInitialValue(gfx::ScrollOffset(),
382                          base::TimeDelta::FromSecondsD(0.2));
383   EXPECT_EQ(0.f, curve->Duration().InSecondsF());
384 
385   // Re-targeting when animation duration is 0.
386   curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.01),
387                       gfx::ScrollOffset(0.f, 300.f));
388   double duration = ScrollOffsetAnimationCurve::EaseInOutSegmentDuration(
389                         gfx::Vector2dF(0.f, 200.f), duration_hint,
390                         base::TimeDelta::FromSecondsD(0.01))
391                         .InSecondsF();
392   EXPECT_EQ(duration, curve->Duration().InSecondsF());
393 
394   // Re-targeting before last_retarget_, the  difference should be accounted for
395   // in duration.
396   curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.01),
397                       gfx::ScrollOffset(0.f, 500.f));
398   duration = ScrollOffsetAnimationCurve::EaseInOutSegmentDuration(
399                  gfx::Vector2dF(0.f, 500.f), duration_hint,
400                  base::TimeDelta::FromSecondsD(0.01))
401                  .InSecondsF();
402   EXPECT_EQ(duration, curve->Duration().InSecondsF());
403 
404   EXPECT_VECTOR2DF_EQ(gfx::ScrollOffset(0.f, 500.f),
405                       curve->GetValue(base::TimeDelta::FromSecondsD(1.0)));
406 }
407 
408 // This test verifies that if the last segment duration is zero, ::UpdateTarget
409 // simply updates the total animation duration see crbug.com/645317.
TEST(ScrollOffsetAnimationCurveTest,UpdateTargetZeroLastSegmentDuration)410 TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) {
411   DurationBehavior duration_hint = DurationBehavior::INVERSE_DELTA;
412   std::unique_ptr<ScrollOffsetAnimationCurve> curve(
413       ScrollOffsetAnimationCurveFactory::CreateEaseInOutAnimationForTesting(
414           gfx::ScrollOffset(0.f, 100.f), duration_hint));
415   double duration_in_seconds = kInverseDeltaMaxDuration / kDurationDivisor;
416   double delay_in_seconds = 0.02;
417   double curve_duration = duration_in_seconds - delay_in_seconds;
418 
419   curve->SetInitialValue(gfx::ScrollOffset(),
420                          base::TimeDelta::FromSecondsD(delay_in_seconds));
421   EXPECT_NEAR(curve_duration, curve->Duration().InSecondsF(), 0.0002f);
422 
423   // Re-target 1, this should set last_retarget_ to 0.05.
424   gfx::ScrollOffset new_delta =
425       gfx::ScrollOffset(0.f, 200.f) -
426       curve->GetValue(base::TimeDelta::FromSecondsD(0.05));
427   double expected_duration =
428       ScrollOffsetAnimationCurve::EaseInOutSegmentDuration(
429           gfx::Vector2dF(new_delta.x(), new_delta.y()), duration_hint,
430           base::TimeDelta())
431           .InSecondsF() +
432       0.05;
433   curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.05),
434                       gfx::ScrollOffset(0.f, 200.f));
435   EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f);
436 
437   // Re-target 2, this should set total_animation_duration to t, which is
438   // last_retarget_. This is what would cause the DCHECK failure in
439   // crbug.com/645317.
440   curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.145),
441                       gfx::ScrollOffset(0.f, 300.f));
442   EXPECT_NEAR(0.05, curve->Duration().InSecondsF(), 0.0002f);
443 
444   // Re-target 3, this should set total_animation_duration based on new_delta.
445   new_delta = gfx::ScrollOffset(0.f, 500.f) -
446               curve->GetValue(base::TimeDelta::FromSecondsD(0.05));
447   expected_duration = ScrollOffsetAnimationCurve::EaseInOutSegmentDuration(
448                           gfx::Vector2dF(new_delta.x(), new_delta.y()),
449                           duration_hint, base::TimeDelta::FromSecondsD(0.15))
450                           .InSecondsF();
451   curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.1),
452                       gfx::ScrollOffset(0.f, 500.f));
453   EXPECT_NEAR(expected_duration, curve->Duration().InSecondsF(), 0.0002f);
454 }
455 
456 }  // namespace
457 }  // namespace cc
458