@Test
 public void testGetCount() {
   delegate = new TimelineDelegate<>(mockTimeline);
   assertEquals(0, delegate.getCount());
   delegate = new TimelineDelegate<>(mockTimeline, null, testItems);
   assertEquals(testItems.size(), delegate.getCount());
 }
 @Test
 public void testWithinMaxCapacity() {
   delegate = new TimelineDelegate<>(mockTimeline);
   assertTrue(delegate.withinMaxCapacity());
   TestItem.populateList(testItems, TimelineDelegate.CAPACITY);
   delegate = new TimelineDelegate<>(mockTimeline, mockObservable, testItems);
   assertFalse(delegate.withinMaxCapacity());
 }
 @Test
 public void testIsLastPosition() {
   testItems = new LinkedList<>();
   TestItem.populateList(testItems, NUM_ITEMS);
   delegate = new TimelineDelegate<>(mockTimeline, mockObservable, testItems);
   assertFalse(delegate.isLastPosition(0));
   assertFalse(delegate.isLastPosition(NUM_ITEMS - 2));
   assertTrue(delegate.isLastPosition(NUM_ITEMS - 1));
 }
 @Test
 public void testPrevious_addsItems() {
   final Timeline<TestItem> fakeTimeline =
       new FakeItemTimeline(NUM_ITEMS, ANY_POSITION, ANY_POSITION);
   delegate = new TimelineDelegate<>(fakeTimeline, mockObservable, null);
   delegate.refresh(null);
   // refresh loads latest items (notifyChange)
   delegate.previous();
   // assert items are added and notifyChanges is called again
   assertEquals(2 * NUM_ITEMS, delegate.getCount());
   verify(mockObservable, times(2)).notifyChanged();
 }
 @Test
 public void testGetNonLastItem_doesNotLoadPrevious() {
   final Timeline<TestItem> fakeTimeline =
       new FakeItemTimeline(NUM_ITEMS, ANY_POSITION, ANY_POSITION);
   delegate = new TimelineDelegate<>(fakeTimeline, mockObservable, null);
   delegate.refresh(null);
   // refresh loads latest items (notifyChange)
   verify(mockObservable).notifyChanged();
   assertEquals(NUM_ITEMS, delegate.getCount());
   delegate.getItem(1);
   // assert no items added and notifyChanged is not called again
   assertEquals(NUM_ITEMS, delegate.getCount());
   verify(mockObservable, times(1)).notifyChanged();
 }
 @Test
 public void testPrevious_doesNotAddItemsAtEndOfTimeline() {
   // when a Timeline successfully returns an empty set of items, its end has been reached
   LinkedList<TestItem> initialItems = new LinkedList<>();
   final int INITIAL_COUNT = 5;
   initialItems = TestItem.populateList(initialItems, INITIAL_COUNT);
   final Timeline<TestItem> fakeEndTimeline =
       new FakeItemTimeline(ZERO_ITEMS, ANY_POSITION, ANY_POSITION);
   delegate = new TimelineDelegate<>(fakeEndTimeline, mockObservable, initialItems);
   delegate.previous();
   // assert no items are added and notifyChanged is not called
   assertEquals(INITIAL_COUNT, delegate.getCount());
   verifyZeroInteractions(mockObservable);
 }
 @Test
 public void testLoadNext() {
   delegate = new TimelineDelegate<>(mockTimeline);
   final Callback<TimelineResult<TestItem>> testCb =
       delegate.new NextCallback(null, delegate.timelineStateHolder);
   delegate.loadNext(TEST_MIN_POSITION, testCb);
   verify(mockTimeline).next(TEST_MIN_POSITION, testCb);
 }
 @Test
 public void testLoadPrevious() {
   delegate = new TimelineDelegate<>(mockTimeline);
   final Callback<TimelineResult<TestItem>> testCb =
       delegate.new PreviousCallback(delegate.timelineStateHolder);
   delegate.loadPrevious(TEST_MAX_POSITION, testCb);
   verify(mockTimeline).previous(TEST_MAX_POSITION, testCb);
 }
 @Test
 public void testRefresh_resetsTimelineCursors() {
   delegate = new TimelineDelegate<>(mockTimeline);
   delegate.timelineStateHolder.setNextCursor(new TimelineCursor(ANY_POSITION, ANY_POSITION));
   delegate.timelineStateHolder.setPreviousCursor(new TimelineCursor(ANY_POSITION, ANY_POSITION));
   delegate.refresh(null);
   assertNull(delegate.timelineStateHolder.positionForNext());
   assertNull(delegate.timelineStateHolder.positionForPrevious());
 }
 @Test
 public void testPrevious_updatesPositionForPrevious() {
   final Timeline<TestItem> fakeTimeline =
       new FakeItemTimeline(NUM_ITEMS, TEST_MIN_POSITION, ANY_POSITION);
   delegate = new TimelineDelegate<>(fakeTimeline);
   assertNull(delegate.timelineStateHolder.positionForPrevious());
   delegate.previous();
   assertEquals(TEST_MIN_POSITION, delegate.timelineStateHolder.positionForPrevious());
 }
 @Test
 public void testPrevious_doesNotUpdatePositionAtEndOfTimeline() {
   final Timeline<TestItem> fakeEndTimeline =
       new FakeItemTimeline(ZERO_ITEMS, ANY_POSITION, ANY_POSITION);
   delegate = new TimelineDelegate<>(fakeEndTimeline);
   delegate.timelineStateHolder.setPreviousCursor(new TimelineCursor(null, null));
   delegate.previous();
   assertNull(delegate.timelineStateHolder.positionForPrevious());
 }
 // should append result items, set previous cursor, and call notifyChanged
 @Test
 public void testPreviousCallback_successReceivedItems() {
   delegate = new TimelineDelegate<>(mockTimeline, mockObservable, testItems);
   final TimelineStateHolder timelineStateHolder =
       new TimelineStateHolder(
           new TimelineCursor(ANY_POSITION, ANY_POSITION),
           new TimelineCursor(ANY_POSITION, ANY_POSITION));
   final TimelineDelegate.PreviousCallback cb = delegate.new PreviousCallback(timelineStateHolder);
   cb.success(new Result<>(new TimelineResult<>(TEST_TIMELINE_CURSOR, testExtraItems), null));
   // assert the previous TimelineCursor is set on the ScrollStateHolder
   assertEquals(TEST_MIN_POSITION, timelineStateHolder.positionForPrevious());
   assertEquals(ANY_POSITION, timelineStateHolder.positionForNext());
   // assert that extra items were appended in order received
   assertEquals(TOTAL_ITEMS, delegate.itemList.size());
   assertEquals(TEST_ITEM_2, delegate.getItem(0));
   assertEquals(TEST_ITEM_1, delegate.getItem(1));
   assertEquals(TEST_ITEM_4, delegate.getItem(2));
   assertEquals(TEST_ITEM_3, delegate.getItem(3));
   // assert observer's notifyChanged is called
   verify(mockObservable).notifyChanged();
 }
 @Test
 public void testLoadPrevious_respectsRequestInFlight() {
   delegate = new TimelineDelegate<>(mockTimeline);
   delegate.timelineStateHolder.startTimelineRequest();
   final Callback<TimelineResult<TestItem>> mockCallback = mock(Callback.class);
   delegate.loadPrevious(ANY_POSITION, mockCallback);
   final ArgumentCaptor<TwitterException> exceptionCaptor =
       ArgumentCaptor.forClass(TwitterException.class);
   verifyZeroInteractions(mockTimeline);
   verify(mockCallback).failure(exceptionCaptor.capture());
   assertEquals(exceptionCaptor.getValue().getMessage(), REQUIRED_REQUEST_IN_FLIGHT_ERROR);
 }
 @Test
 public void testLoadPrevious_respectsMaxCapacity() {
   delegate = new TimelineDelegate<>(mockTimeline);
   TestItem.populateList(delegate.itemList, TimelineDelegate.CAPACITY);
   final Callback<TimelineResult<TestItem>> mockCallback = mock(Callback.class);
   delegate.loadPrevious(ANY_POSITION, mockCallback);
   final ArgumentCaptor<TwitterException> exceptionCaptor =
       ArgumentCaptor.forClass(TwitterException.class);
   verifyZeroInteractions(mockTimeline);
   verify(mockCallback).failure(exceptionCaptor.capture());
   assertEquals(exceptionCaptor.getValue().getMessage(), REQUIRED_MAX_CAPACITY_ERROR);
 }
 @Test
 public void testRefresh_replacesItems() {
   // refresh replaces initial items
   final Timeline<TestItem> fakeTimeline =
       new FakeItemTimeline(NUM_ITEMS, ANY_POSITION, ANY_POSITION);
   delegate = new TimelineDelegate<>(fakeTimeline, mockObservable, testItems);
   assertEquals(testItems, delegate.itemList);
   delegate.refresh(null);
   // assert that items were replaced and notifyChanged called
   assertEquals(NUM_ITEMS, delegate.itemList.size());
   verify(mockObservable).notifyChanged();
 }
 // should clear items with result items, set next cursor, and call notifyChanged
 @Test
 public void testRefreshCallback_successReceivedItems() {
   delegate = new TimelineDelegate<>(mockTimeline, mockObservable, testItems);
   final TimelineStateHolder timelineStateHolder =
       new TimelineStateHolder(
           new TimelineCursor(ANY_POSITION, ANY_POSITION),
           new TimelineCursor(ANY_POSITION, ANY_POSITION));
   final TimelineDelegate.RefreshCallback cb =
       delegate.new RefreshCallback(null, timelineStateHolder);
   cb.success(new Result<>(new TimelineResult<>(TEST_TIMELINE_CURSOR, testExtraItems), null));
   // assert the next TimelineCursor is set on the ScrollStateHolder, previous unchanged
   assertEquals(TEST_MAX_POSITION, timelineStateHolder.positionForNext());
   assertEquals(ANY_POSITION, timelineStateHolder.positionForPrevious());
   // assert that extra items replaced the old items
   assertEquals(testExtraItems.size(), delegate.itemList.size());
   assertEquals(TEST_ITEM_4, delegate.getItem(0));
   assertEquals(TEST_ITEM_3, delegate.getItem(1));
   // assert observer's notifyChanged is called
   verify(mockObservable).notifyChanged();
 }
 @Test
 public void testNotifyDataSetInvalidated() {
   delegate = new TimelineDelegate<>(mockTimeline, mockObservable, null);
   delegate.notifyDataSetInvalidated();
   verify(mockObservable, times(1)).notifyInvalidated();
 }
 @Test
 public void testUnregisterDataSetObserver() {
   delegate = new TimelineDelegate<>(mockTimeline, mockObservable, null);
   delegate.unregisterDataSetObserver(mock(DataSetObserver.class));
   verify(mockObservable, times(1)).unregisterObserver(any(DataSetObserver.class));
 }
 @Test
 public void testGetItemId() {
   delegate = new TimelineDelegate<>(mockTimeline, null, testItems);
   assertEquals(TEST_ITEM_2.getId(), delegate.getItemId(0));
   assertEquals(TEST_ITEM_1.getId(), delegate.getItemId(1));
 }
 @Test
 public void testRefresh_callsNextForLatest() {
   delegate = new TimelineDelegate<>(mockTimeline);
   delegate.refresh(null);
   verify(mockTimeline).next(isNull(Long.class), any(TimelineDelegate.NextCallback.class));
 }