@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);
    }
  }