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