animation_controller_test.dart 22.7 KB
Newer Older
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:ui' as ui;

7
import 'package:flutter_test/flutter_test.dart';
8
import 'package:flutter/animation.dart';
9
import 'package:flutter/scheduler.dart';
10 11
import 'package:flutter/widgets.dart';

12 13
import '../scheduler/scheduler_tester.dart';

14
void main() {
15
  setUp(() {
16
    WidgetsFlutterBinding.ensureInitialized();
17
    WidgetsBinding.instance.resetEpoch();
18 19
    ui.window.onBeginFrame = null;
    ui.window.onDrawFrame = null;
20 21 22
  });

  test('Can set value during status callback', () {
23
    final AnimationController controller = new AnimationController(
24 25
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
    );
    bool didComplete = false;
    bool didDismiss = false;
    controller.addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.completed) {
        didComplete = true;
        controller.value = 0.0;
        controller.forward();
      } else if (status == AnimationStatus.dismissed) {
        didDismiss = true;
        controller.value = 0.0;
        controller.forward();
      }
    });

    controller.forward();
    expect(didComplete, isFalse);
    expect(didDismiss, isFalse);
44
    tick(const Duration(seconds: 1));
45 46
    expect(didComplete, isFalse);
    expect(didDismiss, isFalse);
47
    tick(const Duration(seconds: 2));
48 49
    expect(didComplete, isTrue);
    expect(didDismiss, isTrue);
50 51 52 53

    controller.stop();
  });

54
  test('Receives status callbacks for forward and reverse', () {
55
    final AnimationController controller = new AnimationController(
56 57
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
58
    );
59 60
    final List<double> valueLog = <double>[];
    final List<AnimationStatus> log = <AnimationStatus>[];
61
    controller
62
      ..addStatusListener(log.add)
63 64 65 66
      ..addListener(() {
        valueLog.add(controller.value);
      });

67 68
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
69 70 71

    controller.forward();

72 73
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward]));
    expect(valueLog, equals(<AnimationStatus>[]));
74 75 76

    controller.reverse();

77 78
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward, AnimationStatus.dismissed]));
    expect(valueLog, equals(<AnimationStatus>[]));
79 80 81

    controller.reverse();

82 83
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward, AnimationStatus.dismissed]));
    expect(valueLog, equals(<AnimationStatus>[]));
84 85 86 87

    log.clear();
    controller.forward();

88 89
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward]));
    expect(valueLog, equals(<AnimationStatus>[]));
90 91 92

    controller.forward();

93 94
    expect(log, equals(<AnimationStatus>[AnimationStatus.forward]));
    expect(valueLog, equals(<AnimationStatus>[]));
95 96 97 98

    controller.reverse();
    log.clear();

99
    tick(const Duration(seconds: 10));
100 101
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
102
    tick(const Duration(seconds: 20));
103 104
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
105
    tick(const Duration(seconds: 30));
106 107
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
108
    tick(const Duration(seconds: 40));
109 110
    expect(log, equals(<AnimationStatus>[]));
    expect(valueLog, equals(<AnimationStatus>[]));
111 112

    controller.stop();
113
  });
114

115
  test('Forward and reverse from values', () {
116
    final AnimationController controller = new AnimationController(
117 118
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
119
    );
120 121
    final List<double> valueLog = <double>[];
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
122
    controller
123
      ..addStatusListener(statusLog.add)
124 125 126 127 128
      ..addListener(() {
        valueLog.add(controller.value);
      });

    controller.reverse(from: 0.2);
129 130
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse ]));
    expect(valueLog, equals(<double>[ 0.2 ]));
131 132 133 134 135
    expect(controller.value, equals(0.2));
    statusLog.clear();
    valueLog.clear();

    controller.forward(from: 0.0);
136 137
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.dismissed, AnimationStatus.forward ]));
    expect(valueLog, equals(<double>[ 0.0 ]));
138 139
    expect(controller.value, equals(0.0));
  });
140

141
  test('Forward only from value', () {
142
    final AnimationController controller = new AnimationController(
143 144
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
145
    );
146 147
    final List<double> valueLog = <double>[];
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
148
    controller
149
      ..addStatusListener(statusLog.add)
150 151 152 153 154 155 156 157 158 159
      ..addListener(() {
        valueLog.add(controller.value);
      });

    controller.forward(from: 0.2);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward ]));
    expect(valueLog, equals(<double>[ 0.2 ]));
    expect(controller.value, equals(0.2));
  });

160
  test('Can fling to upper and lower bounds', () {
161
    final AnimationController controller = new AnimationController(
162 163
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
164 165 166
    );

    controller.fling();
167 168
    tick(const Duration(seconds: 1));
    tick(const Duration(seconds: 2));
169 170 171
    expect(controller.value, 1.0);
    controller.stop();

172
    final AnimationController largeRangeController = new AnimationController(
173 174
      duration: const Duration(milliseconds: 100),
      lowerBound: -30.0,
175 176
      upperBound: 45.0,
      vsync: const TestVSync(),
177 178 179
    );

    largeRangeController.fling();
180 181
    tick(const Duration(seconds: 3));
    tick(const Duration(seconds: 4));
182 183
    expect(largeRangeController.value, 45.0);
    largeRangeController.fling(velocity: -1.0);
184 185
    tick(const Duration(seconds: 5));
    tick(const Duration(seconds: 6));
186 187 188
    expect(largeRangeController.value, -30.0);
    largeRangeController.stop();
  });
189 190

  test('lastElapsedDuration control test', () {
191
    final AnimationController controller = new AnimationController(
192 193
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
194 195
    );
    controller.forward();
196 197 198
    tick(const Duration(milliseconds: 20));
    tick(const Duration(milliseconds: 30));
    tick(const Duration(milliseconds: 40));
199 200 201
    expect(controller.lastElapsedDuration, equals(const Duration(milliseconds: 20)));
    controller.stop();
  });
202 203

  test('toString control test', () {
204
    final AnimationController controller = new AnimationController(
205 206
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
207
    );
208
    expect(controller, hasOneLineDescription);
209
    controller.forward();
210 211
    tick(const Duration(milliseconds: 10));
    tick(const Duration(milliseconds: 20));
212
    expect(controller, hasOneLineDescription);
213
    tick(const Duration(milliseconds: 30));
214
    expect(controller, hasOneLineDescription);
215
    controller.reverse();
216 217
    tick(const Duration(milliseconds: 40));
    tick(const Duration(milliseconds: 50));
218
    expect(controller, hasOneLineDescription);
219 220
    controller.stop();
  });
221 222

  test('velocity test - linear', () {
223
    final AnimationController controller = new AnimationController(
224 225 226 227 228 229
      duration: const Duration(milliseconds: 1000),
      vsync: const TestVSync(),
    );

    // mid-flight
    controller.forward();
230 231
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 500));
232 233 234 235 236
    expect(controller.velocity, inInclusiveRange(0.9, 1.1));

    // edges
    controller.forward();
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
237
    tick(Duration.zero);
238
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
239
    tick(const Duration(milliseconds: 5));
240 241 242 243
    expect(controller.velocity, inInclusiveRange(0.9, 1.1));

    controller.forward(from: 0.5);
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
244
    tick(Duration.zero);
245
    expect(controller.velocity, inInclusiveRange(0.4, 0.6));
246
    tick(const Duration(milliseconds: 5));
247 248 249 250 251
    expect(controller.velocity, inInclusiveRange(0.9, 1.1));

    // stopped
    controller.forward(from: 1.0);
    expect(controller.velocity, 0.0);
252
    tick(Duration.zero);
253
    expect(controller.velocity, 0.0);
254
    tick(const Duration(milliseconds: 500));
255 256 257
    expect(controller.velocity, 0.0);

    controller.forward();
258
    tick(Duration.zero);
259
    tick(const Duration(milliseconds: 1000));
260 261 262 263
    expect(controller.velocity, 0.0);

    controller.stop();
  });
264 265

  test('Disposed AnimationController toString works', () {
266
    final AnimationController controller = new AnimationController(
267 268 269 270 271 272
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
    );
    controller.dispose();
    expect(controller, hasOneLineDescription);
  });
273 274

  test('AnimationController error handling', () {
275
    final AnimationController controller = new AnimationController(
276 277 278 279 280 281 282 283 284 285 286
      vsync: const TestVSync(),
    );

    expect(controller.forward, throwsFlutterError);
    expect(controller.reverse, throwsFlutterError);
    expect(() { controller.animateTo(0.5); }, throwsFlutterError);
    expect(controller.repeat, throwsFlutterError);

    controller.dispose();
    expect(controller.dispose, throwsFlutterError);
  });
287 288 289 290 291

  test('AnimationController repeat() throws if period is not specified', () {
    final AnimationController controller = new AnimationController(
      vsync: const TestVSync(),
    );
292 293
    expect(() { controller.repeat(); }, throwsFlutterError);
    expect(() { controller.repeat(period: null); }, throwsFlutterError);
294 295
  });

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
  test('Do not animate if already at target', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];

    final AnimationController controller = new AnimationController(
      value: 0.5,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(0.5));
    controller.animateTo(0.5, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.completed ]));
    expect(controller.value, equals(0.5));
  });

  test('Do not animate to upperBound if already at upperBound', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];

    final AnimationController controller = new AnimationController(
      value: 1.0,
      upperBound: 1.0,
      lowerBound: 0.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(1.0));
    controller.animateTo(1.0, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.completed ]));
    expect(controller.value, equals(1.0));
  });

  test('Do not animate to lowerBound if already at lowerBound', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];

    final AnimationController controller = new AnimationController(
      value: 0.0,
      upperBound: 1.0,
      lowerBound: 0.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(0.0));
    controller.animateTo(0.0, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.completed ]));
    expect(controller.value, equals(0.0));
  });

  test('Do not animate if already at target mid-flight (forward)', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
    final AnimationController controller = new AnimationController(
      value: 0.0,
      duration: const Duration(milliseconds: 1000),
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(0.0));

    controller.forward();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 500));
    expect(controller.value, inInclusiveRange(0.4, 0.6));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward ]));

    final double currentValue = controller.value;
    controller.animateTo(currentValue, duration: const Duration(milliseconds: 100));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    expect(controller.value, currentValue);
  });

  test('Do not animate if already at target mid-flight (reverse)', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
    final AnimationController controller = new AnimationController(
      value: 1.0,
      duration: const Duration(milliseconds: 1000),
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, equals(1.0));

    controller.reverse();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 500));
    expect(controller.value, inInclusiveRange(0.4, 0.6));
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse ]));

    final double currentValue = controller.value;
    controller.animateTo(currentValue, duration: const Duration(milliseconds: 100));
382
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse, AnimationStatus.completed ]));
383 384
    expect(controller.value, currentValue);
  });
385

386
  test('animateTo can deal with duration == Duration.zero', () {
387 388 389 390 391 392 393
    final AnimationController controller = new AnimationController(
      duration: const Duration(milliseconds: 100),
      vsync: const TestVSync(),
    );

    controller.forward(from: 0.2);
    expect(controller.value, 0.2);
394
    controller.animateTo(1.0, duration: Duration.zero);
395 396 397
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0), reason: 'Expected no animation.');
    expect(controller.value, 1.0);
  });
398

399
  test('resetting animation works at all phases', () {
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
    final AnimationController controller = new AnimationController(
      duration: const Duration(milliseconds: 100),
      value: 0.0,
      lowerBound: 0.0,
      upperBound: 1.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    statusLog.clear();
    controller.forward();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 50));
    expect(controller.status, AnimationStatus.forward);
    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.dismissed ]));

    controller.value = 1.0;
    statusLog.clear();
    controller.reverse();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 50));
    expect(controller.status, AnimationStatus.reverse);
    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse, AnimationStatus.dismissed ]));

    statusLog.clear();
    controller.forward();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.status, AnimationStatus.completed);
    controller.reset();

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed, AnimationStatus.dismissed ]));
  });

452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
  test('setting value directly sets correct status', () {
    final AnimationController controller = new AnimationController(
      value: 0.0,
      lowerBound: 0.0,
      upperBound: 1.0,
      vsync: const TestVSync(),
    );

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    controller.value = 0.5;
    expect(controller.value, 0.5);
    expect(controller.status, AnimationStatus.forward);

    controller.value = 1.0;
    expect(controller.value, 1.0);
    expect(controller.status, AnimationStatus.completed);

    controller.value = 0.5;
    expect(controller.value, 0.5);
    expect(controller.status, AnimationStatus.forward);

    controller.value = 0.0;
    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);
  });

  test('animateTo sets correct status', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
    final AnimationController controller = new AnimationController(
      duration: const Duration(milliseconds: 100),
      value: 0.0,
      lowerBound: 0.0,
      upperBound: 1.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    // Animate from 0.0 to 0.5
    controller.animateTo(0.5);
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    // Animate from 0.5 to 1.0
    controller.animateTo(1.0);
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 1.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    // Animate from 1.0 to 0.5
    controller.animateTo(0.5);
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    // Animate from 0.5 to 1.0
    controller.animateTo(0.0);
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();
  });

  test('after a reverse call animateTo sets correct status', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
    final AnimationController controller = new AnimationController(
      duration: const Duration(milliseconds: 100),
      value: 1.0,
      lowerBound: 0.0,
      upperBound: 1.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 1.0);
    expect(controller.status, AnimationStatus.completed);

    controller.reverse();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.reverse, AnimationStatus.dismissed ]));
    statusLog.clear();

    controller.animateTo(0.5);
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();
  });

  test('after a forward call animateTo sets correct status', () {
    final List<AnimationStatus> statusLog = <AnimationStatus>[];
    final AnimationController controller = new AnimationController(
      duration: const Duration(milliseconds: 100),
      value: 0.0,
      lowerBound: 0.0,
      upperBound: 1.0,
      vsync: const TestVSync(),
    )..addStatusListener(statusLog.add);

    expect(controller.value, 0.0);
    expect(controller.status, AnimationStatus.dismissed);

    controller.forward();
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 1.0);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();

    controller.animateTo(0.5);
    tick(const Duration(milliseconds: 0));
    tick(const Duration(milliseconds: 150));
    expect(controller.value, 0.5);
    expect(statusLog, equals(<AnimationStatus>[ AnimationStatus.forward, AnimationStatus.completed ]));
    statusLog.clear();
  });
581 582 583

  group('AnimationBehavior', () {
    test('Default values for constructor', () {
584
      final AnimationController controller = new AnimationController(vsync: const TestVSync(disableAnimations: true));
585 586
      expect(controller.animationBehavior, AnimationBehavior.normal);

587
      final AnimationController repeating = new AnimationController.unbounded(vsync: const TestVSync(disableAnimations: true));
588 589 590
      expect(repeating.animationBehavior, AnimationBehavior.preserve);
    });

591
    test('AnimationBehavior.preserve runs at normal speed when animatingTo', () async {
592
      final AnimationController controller = new AnimationController(
593
        vsync: const TestVSync(disableAnimations: true),
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
        animationBehavior: AnimationBehavior.preserve,
      );

      expect(controller.value, 0.0);
      expect(controller.status, AnimationStatus.dismissed);

      controller.animateTo(1.0, duration: const Duration(milliseconds: 100));
      tick(const Duration(milliseconds: 0));
      tick(const Duration(milliseconds: 50));

      expect(controller.value, 0.5);
      expect(controller.status, AnimationStatus.forward);

      tick(const Duration(milliseconds: 0));
      tick(const Duration(milliseconds: 150));

      expect(controller.value, 1.0);
      expect(controller.status, AnimationStatus.completed);
    });

614
    test('AnimationBehavior.normal runs at 20x speed when animatingTo', () async {
615
      final AnimationController controller = new AnimationController(
616
        vsync: const TestVSync(disableAnimations: true),
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
        animationBehavior: AnimationBehavior.normal,
      );

      expect(controller.value, 0.0);
      expect(controller.status, AnimationStatus.dismissed);

      controller.animateTo(1.0, duration: const Duration(milliseconds: 100));
      tick(const Duration(milliseconds: 0));
      tick(const Duration(microseconds: 2500));

      expect(controller.value, 0.5);
      expect(controller.status, AnimationStatus.forward);

      tick(const Duration(milliseconds: 0));
      tick(const Duration(milliseconds: 5, microseconds: 1000));

      expect(controller.value, 1.0);
      expect(controller.status, AnimationStatus.completed);
    });

637
    test('AnimationBehavior.normal runs "faster" whan AnimationBehavior.preserve', () {
638
      final AnimationController controller = new AnimationController(
639
        vsync: const TestVSync(disableAnimations: true),
640 641
      );
      final AnimationController fastController = new AnimationController(
642
        vsync: const TestVSync(disableAnimations: true),
643 644 645 646 647 648 649 650 651 652 653
      );

      controller.fling(velocity: 1.0, animationBehavior: AnimationBehavior.preserve);
      fastController.fling(velocity: 1.0, animationBehavior: AnimationBehavior.normal);
      tick(const Duration(milliseconds: 0));
      tick(const Duration(milliseconds: 50));

      // We don't assert a specific faction that normal animation.
      expect(controller.value < fastController.value, true);
    });
  });
654
}