@SmallTest
  public void testComputeScrollOffsetForFling() throws Exception {
    class TestData extends Parameter {
      final int velocity;
      final float decayRate;
      final int expectedScrollPosition;

      TestData(int velocity, float decayRate, int expectedScrollPosition) {
        this.velocity = velocity;
        this.decayRate = decayRate;
        this.expectedScrollPosition = expectedScrollPosition;
      }
    }
    int scrollPosition = 50;
    TestData[] testDataList = {
      new TestData(0, 0f, 50),
      // Decay rate is 0 so scroll animation happens only once.
      new TestData(+1000, 0f, 60),
      // Decay rate is 1 so scroll animation happens repeatedly until
      // reaching the maximum boundary.
      new TestData(+1000, 1f, 90),
      // Check boundary value.
      new TestData(Integer.MAX_VALUE, 1f, 90),
    };
    SnapScroller.TimestampCalculator timestampCalculator =
        createMock(SnapScroller.TimestampCalculator.class);
    SnapScroller scroller = new SnapScroller(timestampCalculator);
    scroller.setContentSize(110);
    scroller.setPageSize(10);
    scroller.setViewSize(20);
    scroller.setMinimumVelocity(0);

    for (TestData testData : testDataList) {
      resetAll();
      expect(timestampCalculator.getTimestamp())
          .andStubAnswer(
              new IAnswer<Long>() {
                long counter = 0;

                @Override
                public Long answer() throws Throwable {
                  return counter += 100;
                }
              });
      replayAll();
      scroller.setDecayRate(testData.decayRate);
      scroller.scrollTo(scrollPosition);
      scroller.fling(testData.velocity);
      while (scroller.isScrolling()) {
        scroller.computeScrollOffset();
      }
      assertEquals(testData.toString(), testData.expectedScrollPosition, scroller.scrollPosition);
    }
  }
  @SmallTest
  public void testScrollTo() {
    class TestData extends Parameter {
      final int toPosition;
      final int expected;

      TestData(int toPosition, int expected) {
        this.toPosition = toPosition;
        this.expected = expected;
      }
    }
    TestData[] testDataList = {
      new TestData(-1, 0),
      new TestData(0, 0),
      new TestData(1, 1),
      new TestData(90, 90),
      new TestData(91, 90),
      new TestData(80, 80),
    };
    SnapScroller scroller = new SnapScroller();
    scroller.setContentSize(100);
    scroller.setViewSize(10);
    for (TestData testData : testDataList) {
      scroller.scrollTo(testData.toPosition);
      assertEquals(testData.toString(), testData.expected, scroller.getScrollPosition());
      assertFalse(testData.toString(), scroller.isScrolling());
    }

    // Widen view size.
    scroller.setViewSize(50);
    testDataList =
        new TestData[] {
          new TestData(81, 80),
          new TestData(70, 70),
          new TestData(71, 70),
          new TestData(1, 1),
          new TestData(50, 50),
          new TestData(51, 50),
        };
    for (TestData testData : testDataList) {
      scroller.scrollTo(testData.toPosition);
      assertEquals(testData.toString(), testData.expected, scroller.getScrollPosition());
      assertFalse(testData.toString(), scroller.isScrolling());
    }
  }
  @SmallTest
  public void testFling() {
    class TestData extends Parameter {
      final int velocity;
      final int mimimumVelocity;
      final int scrollPosition;
      final int expectedEndScrollPosition;
      final boolean expectedIsScrolling;

      TestData(
          int velocity,
          int mimimumVelocity,
          int scrollPosition,
          int expectedEndScrollPosition,
          boolean expectedIsScrolling) {
        this.velocity = velocity;
        this.mimimumVelocity = mimimumVelocity;
        this.scrollPosition = scrollPosition;
        this.expectedEndScrollPosition = expectedEndScrollPosition;
        this.expectedIsScrolling = expectedIsScrolling;
      }
    }
    int pageSize = 10;
    int viewSize = 20;
    int contentSize = 110;
    final long timestamp = 12345;
    TestData[] testDataList = {
      // Velocity is 0 so scroll animation does not start.
      new TestData(0, 0, 0, 0, false),
      // Usual situation to start scroll.
      new TestData(-1, 1, 5, 0, true),
      new TestData(+1, 1, 5, 10, true),
      // Velocity is lower than mininum velocity so animation does not start.
      new TestData(-1, 2, 5, 0, false),
      new TestData(+1, 2, 5, 0, false),
      // Animation starts on the page edge position.
      new TestData(-1, 1, 10, 0, true),
      new TestData(+1, 1, 10, 20, true),
      // Minimum and maximum boundary check.
      new TestData(-1, 1, 0, 0, false),
      new TestData(+1, 1, 100, 90, false),
    };
    SnapScroller scroller =
        new SnapScroller(
            new SnapScroller.TimestampCalculator() {
              @Override
              public long getTimestamp() {
                return timestamp;
              }
            });
    scroller.setContentSize(contentSize);
    scroller.setPageSize(pageSize);
    scroller.setViewSize(viewSize);
    for (TestData testData : testDataList) {
      scroller.scrollTo(testData.scrollPosition);
      scroller.setMinimumVelocity(testData.mimimumVelocity);
      scroller.fling(testData.velocity);
      assertEquals(testData.toString(), testData.expectedIsScrolling, scroller.isScrolling());
      if (scroller.isScrolling()) {
        assertEquals(
            testData.toString(), testData.scrollPosition, scroller.getStartScrollPosition());
        assertEquals(
            testData.toString(),
            testData.expectedEndScrollPosition,
            scroller.getEndScrollPosition());
        assertEquals(testData.toString(), timestamp, scroller.getStartScrollTime());
      }
    }
  }