/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.user.cellview.client; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArrayInteger; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.GwtEvent.Type; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.cellview.client.HasDataPresenter.View; import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy.KeyboardPagingPolicy; import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy; import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState; import com.google.gwt.view.client.HasData; import com.google.gwt.view.client.MockHasData; import com.google.gwt.view.client.MockHasData.MockRangeChangeHandler; import com.google.gwt.view.client.MockHasData.MockRowCountChangeHandler; import com.google.gwt.view.client.MockSelectionModel; import com.google.gwt.view.client.ProvidesKey; import com.google.gwt.view.client.Range; import com.google.gwt.view.client.RangeChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent.Handler; import com.google.gwt.view.client.SelectionModel; import com.google.gwt.view.client.SingleSelectionModel; import java.util.ArrayList; import java.util.List; /** * Tests for {@link HasDataPresenter}. */ public class HasDataPresenterTest extends GWTTestCase { /** * A mock {@link SelectionChangeEvent.Handler} used for testing. */ private static class MockSelectionChangeHandler implements SelectionChangeEvent.Handler { private boolean eventFired; /** * Assert that a {@link SelectionChangeEvent} was fired and clear the * boolean. * * @param expected the expected value */ public void assertEventFired(boolean expected) { assertEquals(expected, eventFired); eventFired = false; } @Override public void onSelectionChange(SelectionChangeEvent event) { assertFalse(eventFired); eventFired = true; } } /** * A mock {@link SelectionModel} used for testing without used any GWT client * code. * * @param <T> the selection type */ private class MockSingleSelectionModel<T> extends SingleSelectionModel<T> { public MockSingleSelectionModel(ProvidesKey<T> keyProvider) { super(keyProvider); } @Override public void fireSelectionChangeEvent() { super.fireSelectionChangeEvent(); } } /** * A mock view used for testing. * * @param <T> the data type */ private static class MockView<T> implements View<T> { /** * A call to replacement. */ private static class Replacement { private final boolean isReplaceAll; private final int size; private final int start; public Replacement(boolean isReplaceAll, int start, int size) { this.isReplaceAll = isReplaceAll; this.start = start; this.size = size; } } private int childCount; private List<Integer> keyboardSelectedRow = new ArrayList<Integer>(); private List<Boolean> keyboardSelectedRowState = new ArrayList<Boolean>(); private List<Replacement> lastReplacement = new ArrayList<Replacement>(); private LoadingState loadingState; @Override public <H extends EventHandler> HandlerRegistration addHandler(H handler, Type<H> type) { throw new UnsupportedOperationException(); } /** * Assert the value of the oldest keyboard selected row and pop it. * * @param row the row index * @param selected true if selected, false if not */ public void assertKeyboardSelectedRow(int row, boolean selected) { int actualRow = keyboardSelectedRow.remove(0); boolean actualSelected = keyboardSelectedRowState.remove(0); assertEquals(row, actualRow); assertEquals(selected, actualSelected); } /** * Assert that the keyboard selected row queue is empty. */ public void assertKeyboardSelectedRowEmpty() { assertEquals(0, keyboardSelectedRow.size()); } public void assertLoadingState(LoadingState expected) { assertEquals(expected, loadingState); } public void assertReplaceAllChildrenCalled(int size) { assertFalse("replaceAllChildren was not called", lastReplacement.isEmpty()); Replacement call = lastReplacement.remove(0); assertTrue("replaceChildren called instead of replaceAllChidren", call.isReplaceAll); assertEquals(size, call.size); } public void assertReplaceChildrenCalled(int start, int size) { assertFalse("replaceChildren was not called", lastReplacement.isEmpty()); Replacement call = lastReplacement.remove(0); assertFalse("replaceAllChildren called instead of replaceChidren", call.isReplaceAll); assertEquals(start, call.start); assertEquals(size, call.size); } public void assertReplaceChildrenNotCalled() { assertTrue(lastReplacement.isEmpty()); } public int getChildCount() { return childCount; } @Override public void replaceAllChildren(List<T> values, SelectionModel<? super T> selectionModel, boolean stealFocus) { childCount = values.size(); lastReplacement.add(new Replacement(true, -1, values.size())); } @Override public void replaceChildren(List<T> values, int start, SelectionModel<? super T> selectionModel, boolean stealFocus) { childCount = Math.max(childCount, start + values.size()); lastReplacement.add(new Replacement(false, start, values.size())); } @Override public void resetFocus() { } @Override public void setKeyboardSelected(int index, boolean selected, boolean stealFocus) { keyboardSelectedRow.add(index); keyboardSelectedRowState.add(selected); } @Override public void setLoadingState(LoadingState state) { this.loadingState = state; } } @Override public String getModuleName() { return "com.google.gwt.user.cellview.CellView"; } public void testAddRowCountChangeHandler() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); MockRowCountChangeHandler handler = new MockRowCountChangeHandler(); // Adding a handler should not invoke the handler. // Add the handler to the view because it is the source of events. HandlerRegistration reg = listView.addRowCountChangeHandler(handler); assertEquals(-1, handler.getLastRowCount()); handler.reset(); // Change the row count. presenter.setRowCount(100, true); assertEquals(100, handler.getLastRowCount()); assertTrue(handler.isLastRowCountExact()); handler.reset(); // Set the same row count and verify no event is fired. presenter.setRowCount(100, true); assertEquals(-1, handler.getLastRowCount()); handler.reset(); // Change the row count again. presenter.setRowCount(110, false); assertEquals(110, handler.getLastRowCount()); assertFalse(handler.isLastRowCountExact()); handler.reset(); // Change only the isExact param and verify an event is fired. presenter.setRowCount(110, true); assertEquals(110, handler.getLastRowCount()); assertTrue(handler.isLastRowCountExact()); handler.reset(); // Remove the handler and verify it no longer receives events. reg.removeHandler(); presenter.setRowCount(200, true); assertEquals(-1, handler.getLastRowCount()); } public void testAddRangeChangeHandler() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); MockRangeChangeHandler handler = new MockRangeChangeHandler(); // Adding a handler should not invoke the handler. // Add the handler to the view because it is the source of events. HandlerRegistration reg = listView.addRangeChangeHandler(handler); assertNull(handler.getLastRange()); handler.reset(); // Change the pageStart. presenter.setVisibleRange(new Range(10, 10)); assertEquals(new Range(10, 10), handler.getLastRange()); handler.reset(); // Change the pageSize. presenter.setVisibleRange(new Range(10, 20)); assertEquals(new Range(10, 20), handler.getLastRange()); handler.reset(); // Reuse the same range and verify an event is not fired. presenter.setVisibleRange(new Range(10, 20)); assertNull(handler.getLastRange()); handler.reset(); // Remove the handler and verify it no longer receives events. reg.removeHandler(); presenter.setVisibleRange(new Range(20, 100)); assertNull(handler.getLastRange()); handler.reset(); } /** * Test that the presenter can gracefully handle a view that throws exceptions * when rendering the content. */ public void testBadViewSelectionModel() { SelectionModel<String> badModel = new SelectionModel<String>() { @Override public void fireEvent(GwtEvent<?> event) { } @Override public Object getKey(String item) { return null; } @Override public HandlerRegistration addSelectionChangeHandler(Handler handler) { return null; } @Override public boolean isSelected(String object) { throw new NullPointerException(); } @Override public void setSelected(String object, boolean selected) { throw new NullPointerException(); } }; // Use the bad view in a presenter. MockView<String> view = new MockView<String>(); HasData<String> listView = new MockHasData<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setSelectionModel(badModel); presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION); testPresenterWithBadUserCode(presenter); } /** * Test that the presenter can gracefully handle a view that throws exceptions * when rendering the children. */ public void testBadViewReplaceChildren() { MockView<String> badView = new MockView<String>() { @Override public void replaceAllChildren(List<String> values, SelectionModel<? super String> selectionModel, boolean stealFocus) { throw new NullPointerException(); } @Override public void replaceChildren(List<String> values, int start, SelectionModel<? super String> selectionModel, boolean stealFocus) { throw new NullPointerException(); } }; // Use the bad view in a presenter. HasData<String> listView = new MockHasData<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, badView, 10, null); testPresenterWithBadUserCode(presenter); } public void testCalculateModifiedRanges() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); JsArrayInteger rows = JavaScriptObject.createArray().cast(); // Empty set of rows. assertListContains(presenter.calculateModifiedRanges(rows, 0, 10)); // One row in range. rows.push(5); assertListContains(presenter.calculateModifiedRanges(rows, 0, 10), new Range(5, 1)); // One row not in range. assertListContains(presenter.calculateModifiedRanges(rows, 6, 10)); // Consecutive rows (should return only one range). rows.push(6); rows.push(7); rows.push(8); assertListContains(presenter.calculateModifiedRanges(rows, 0, 10), new Range(5, 4)); // Disjoint rows. Should return two ranges. rows.push(10); rows.push(11); assertListContains(presenter.calculateModifiedRanges(rows, 0, 20), new Range(5, 4), new Range( 10, 2)); // Multiple gaps. The largest gap should be between the two ranges. rows.push(15); rows.push(17); assertListContains(presenter.calculateModifiedRanges(rows, 0, 20), new Range(5, 7), new Range( 15, 3)); } public void testClearSelectionModel() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); assertNull(presenter.getSelectionModel()); // Initialize some data. populatePresenter(presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(10); // Set the selection model. SelectionModel<String> model = new MockSelectionModel<String>(null); model.setSelected("test 0", true); presenter.setSelectionModel(model); presenter.flush(); view.assertReplaceChildrenCalled(0, 1); // Clear the selection model without updating the view. presenter.clearSelectionModel(); presenter.flush(); view.assertReplaceChildrenNotCalled(); } public void testDefaults() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); assertEquals(0, presenter.getRowCount()); assertFalse(presenter.isRowCountExact()); assertEquals(0, presenter.getCurrentPageSize()); assertEquals(new Range(0, 10), presenter.getVisibleRange()); } /** * Test that keyboard selection moves if its value moves. */ public void testFindIndexOfBestMatch() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); populatePresenter(presenter); // Select the second element. presenter.setKeyboardSelectedRow(2, false, false); presenter.flush(); assertEquals(2, presenter.getKeyboardSelectedRow()); assertEquals("test 2", presenter.getKeyboardSelectedRowValue()); // Shift the values by one. presenter.setRowData(1, createData(0, 9)); presenter.flush(); assertEquals(3, presenter.getKeyboardSelectedRow()); assertEquals("test 2", presenter.getKeyboardSelectedRowValue()); // Replace the keyboard selected value. presenter.setRowData(0, createData(100, 10)); presenter.flush(); assertEquals(0, presenter.getKeyboardSelectedRow()); assertEquals(null, presenter.getKeyboardSelectedRowValue()); } public void testFlush() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Data should not be pushed to the view until flushed. populatePresenter(presenter); view.assertReplaceChildrenNotCalled(); // Now the data is pushed. presenter.flush(); view.assertReplaceAllChildrenCalled(10); } public void testGetCurrentPageSize() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setRowCount(35, true); // First page. assertEquals(10, presenter.getCurrentPageSize()); // Last page. presenter.setVisibleRange(new Range(30, 10)); assertEquals(5, presenter.getCurrentPageSize()); } public void testIsEmpty() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Non-zero row count. presenter.setRowCount(1, true); populatePresenter(presenter); presenter.flush(); assertFalse(presenter.isEmpty()); // Zero row count with unknown size. presenter.setRowCount(0, false); populatePresenter(presenter); presenter.flush(); assertFalse(presenter.isEmpty()); // Zero row count with known size. presenter.setRowCount(0, true); populatePresenter(presenter); presenter.flush(); assertFalse(presenter.isEmpty()); } /** * Test that we can detect an infinite loop caused by user code updating the * presenter every time we try to resolve state. */ public void testLoopDetection() { HasData<String> listView = new MockHasData<String>(); final MockView<String> view = new MockView<String>(); final HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setSelectionModel(new SingleSelectionModel<String>() { @Override public boolean isSelected(String object) { // This selection model triggers a selection change event every time it // is accessed, which puts the presenter in a pending state. SelectionChangeEvent.fire(this); return super.isSelected(object); } }); populatePresenter(presenter); try { presenter.flush(); fail("Expected IllegalStateException because of infinite loop."); } catch (IllegalStateException e) { // Expected. } } /** * Test that pending command execute in a finally loop. */ public void testPendingCommand() { HasData<String> listView = new MockHasData<String>(); final MockView<String> view = new MockView<String>(); final HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Data should not be pushed to the view until the pending command executes. populatePresenter(presenter); assertTrue(presenter.hasPendingState()); view.assertReplaceChildrenNotCalled(); // The pending command is scheduled. Wait for it to execute. delayTestFinish(5000); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { assertFalse(presenter.hasPendingState()); view.assertReplaceAllChildrenCalled(10); finishTest(); } }); } public void testRedraw() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Initialize some data. presenter.setRowCount(10, true); populatePresenter(presenter); presenter.flush(); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); // Redraw. presenter.redraw(); view.assertReplaceChildrenNotCalled(); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); } public void testSetKeyboardSelectedRowBound() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(0, 10)); populatePresenter(presenter); presenter.flush(); // The default is ENABLED. assertEquals(KeyboardSelectionPolicy.ENABLED, presenter.getKeyboardSelectionPolicy()); // Change to bound with paging. presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION); presenter.setKeyboardPagingPolicy(KeyboardPagingPolicy.CHANGE_PAGE); // Add a selection model. MockSelectionModel<String> model = new MockSelectionModel<String>(null); presenter.setSelectionModel(model); presenter.flush(); assertEquals(0, model.getSelectedSet().size()); // Clear the data and push new data. This should not select an item because // there has not been any user interaction. presenter.setRowCount(0, true); presenter.flush(); presenter.setRowCount(100, false); presenter.flush(); populatePresenter(presenter); presenter.flush(); assertEquals(0, model.getSelectedSet().size()); // Select an element. presenter.setKeyboardSelectedRow(5, false, false); presenter.flush(); assertEquals(1, model.getSelectedSet().size()); assertTrue(model.isSelected("test 5")); // Select another element. presenter.setKeyboardSelectedRow(9, false, false); presenter.flush(); assertEquals(1, model.getSelectedSet().size()); assertTrue(model.isSelected("test 9")); // Select an element on another page. presenter.setKeyboardSelectedRow(11, false, false); presenter.flush(); // The previous value is still selected because we don't have new data. assertEquals(1, model.getSelectedSet().size()); assertTrue(model.isSelected("test 9")); populatePresenter(presenter); presenter.flush(); // Once data is pushed, the selection model should be populated. assertEquals(1, model.getSelectedSet().size()); assertTrue(model.isSelected("test 10")); } /** * Test that programmatically deselecting a row works. */ public void testSetKeyboardSelectedRowBoundWithDeselect() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION); presenter.setVisibleRange(new Range(0, 10)); populatePresenter(presenter); presenter.flush(); // Add a selection model. MockSingleSelectionModel<String> model = new MockSingleSelectionModel<String>(null); presenter.setSelectionModel(model); presenter.flush(); assertEquals(null, model.getSelectedObject()); // Select an item. presenter.setKeyboardSelectedRow(1, false, false); presenter.flush(); assertTrue(model.isSelected("test 1")); // Deselect the item. model.setSelected("test 1", false); assertFalse(model.isSelected("test 1")); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); // Reselect the item. presenter.setKeyboardSelectedRow(1, false, false); presenter.flush(); assertTrue(model.isSelected("test 1")); } /** * Test that we only get one selection event when keyboard selection changes. */ public void testSetKeyboardSelectedRowFiresOneSelectionEvent() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(0, 10)); populatePresenter(presenter); presenter.flush(); // Bind keyboard selection to the selection model. presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.BOUND_TO_SELECTION); presenter.setKeyboardPagingPolicy(KeyboardPagingPolicy.CHANGE_PAGE); // Add a selection model. MockSingleSelectionModel<String> model = new MockSingleSelectionModel<String>(null); presenter.setSelectionModel(model); presenter.flush(); assertNull(model.getSelectedObject()); // Add an selection event handler. MockSelectionChangeHandler handler = new MockSelectionChangeHandler(); model.addSelectionChangeHandler(handler); // Select an element. presenter.setKeyboardSelectedRow(5, false, false); presenter.flush(); model.fireSelectionChangeEvent(); handler.assertEventFired(true); assertEquals("test 5", model.getSelectedObject()); // Select another element. presenter.setKeyboardSelectedRow(9, false, false); presenter.flush(); model.fireSelectionChangeEvent(); handler.assertEventFired(true); assertEquals("test 9", model.getSelectedObject()); // Select an element on another page. presenter.setKeyboardSelectedRow(11, false, false); presenter.flush(); model.fireSelectionChangeEvent(); // The previous value is still selected because we don't have new data. handler.assertEventFired(false); assertEquals("test 9", model.getSelectedObject()); populatePresenter(presenter); presenter.flush(); model.fireSelectionChangeEvent(); // Once data is pushed, the selection model should be populated. assertEquals("test 10", model.getSelectedObject()); handler.assertEventFired(true); } public void testSetKeyboardSelectedRowChangePage() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(10, 10)); populatePresenter(presenter); presenter.flush(); // Default policy is CHANGE_PAGE. assertEquals(KeyboardPagingPolicy.CHANGE_PAGE, presenter.getKeyboardPagingPolicy()); // Default to row 0. assertEquals(0, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRowEmpty(); // Move to middle. presenter.setKeyboardSelectedRow(1, false, false); assertEquals("test 11", presenter.getKeyboardSelectedRowValue()); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); assertEquals("test 11", presenter.getKeyboardSelectedRowValue()); view.assertKeyboardSelectedRow(0, false); view.assertKeyboardSelectedRow(1, true); // Move to same row (should not early out). presenter.setKeyboardSelectedRow(1, false, true); assertEquals("test 11", presenter.getKeyboardSelectedRowValue()); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); assertEquals("test 11", presenter.getKeyboardSelectedRowValue()); view.assertKeyboardSelectedRow(1, false); view.assertKeyboardSelectedRow(1, true); // Move to last row. presenter.setKeyboardSelectedRow(9, false, false); assertEquals("test 19", presenter.getKeyboardSelectedRowValue()); presenter.flush(); assertEquals(9, presenter.getKeyboardSelectedRow()); assertEquals("test 19", presenter.getKeyboardSelectedRowValue()); view.assertKeyboardSelectedRow(1, false); view.assertKeyboardSelectedRow(9, true); assertEquals(10, presenter.getVisibleRange().getStart()); assertEquals(10, presenter.getVisibleRange().getLength()); // Move to next page. presenter.setKeyboardSelectedRow(10, false, false); populatePresenter(presenter); assertNull(presenter.getKeyboardSelectedRowValue()); presenter.flush(); assertEquals(0, presenter.getKeyboardSelectedRow()); assertEquals("test 20", presenter.getKeyboardSelectedRowValue()); view.assertReplaceAllChildrenCalled(10); view.assertKeyboardSelectedRowEmpty(); assertEquals(20, presenter.getVisibleRange().getStart()); assertEquals(10, presenter.getVisibleRange().getLength()); // Negative index. presenter.setKeyboardSelectedRow(-1, false, false); populatePresenter(presenter); assertNull(presenter.getKeyboardSelectedRowValue()); presenter.flush(); assertEquals(9, presenter.getKeyboardSelectedRow()); assertEquals("test 19", presenter.getKeyboardSelectedRowValue()); view.assertReplaceAllChildrenCalled(10); view.assertKeyboardSelectedRowEmpty(); assertEquals(10, presenter.getVisibleRange().getStart()); assertEquals(10, presenter.getVisibleRange().getLength()); // Negative index out of range. presenter.setVisibleRange(new Range(3, 10)); presenter.setKeyboardSelectedRow(3, false, false); populatePresenter(presenter); presenter.flush(); presenter.setKeyboardSelectedRow(-4, false, false); populatePresenter(presenter); presenter.flush(); assertEquals(0, presenter.getKeyboardSelectedRow()); assertEquals(0, presenter.getVisibleRange().getStart()); assertEquals(10, presenter.getVisibleRange().getLength()); } public void testSetKeyboardSelectedRowCurrentPage() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(10, 10)); populatePresenter(presenter); presenter.flush(); presenter.setKeyboardPagingPolicy(KeyboardPagingPolicy.CURRENT_PAGE); // Default to row 0. assertEquals(0, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRowEmpty(); // Negative index (should remain at index 0). presenter.setKeyboardSelectedRow(-1, false, false); presenter.flush(); assertEquals(0, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(0, false); view.assertKeyboardSelectedRow(0, true); // Move to middle. presenter.setKeyboardSelectedRow(1, false, false); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(0, false); view.assertKeyboardSelectedRow(1, true); // Move to same row (should not early out). presenter.setKeyboardSelectedRow(1, false, true); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(1, false); view.assertKeyboardSelectedRow(1, true); // Move to last row. presenter.setKeyboardSelectedRow(9, false, false); presenter.flush(); assertEquals(9, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(1, false); view.assertKeyboardSelectedRow(9, true); // Move to next page (confined to page). presenter.setKeyboardSelectedRow(10, false, false); presenter.flush(); assertEquals(9, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRowEmpty(); assertEquals(10, presenter.getVisibleRange().getStart()); assertEquals(10, presenter.getVisibleRange().getLength()); } public void testSetKeyboardSelectedRowDisabled() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(10, 10)); populatePresenter(presenter); presenter.flush(); presenter.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED); assertEquals(-1, presenter.getKeyboardSelectedRow()); assertNull(presenter.getKeyboardSelectedRowValue()); view.assertKeyboardSelectedRowEmpty(); presenter.setKeyboardSelectedRow(1, false, false); presenter.flush(); assertEquals(-1, presenter.getKeyboardSelectedRow()); assertNull(presenter.getKeyboardSelectedRowValue()); view.assertKeyboardSelectedRowEmpty(); } public void testSetKeyboardSelectedRowIncreaseRange() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(10, 10)); populatePresenter(presenter); presenter.flush(); presenter.setKeyboardPagingPolicy(KeyboardPagingPolicy.INCREASE_RANGE); int pageSize = presenter.getVisibleRange().getLength(); // Default to row 0. assertEquals(0, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRowEmpty(); // Move to middle. presenter.setKeyboardSelectedRow(1, false, false); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(0, false); view.assertKeyboardSelectedRow(1, true); // Move to same row (should not early out). presenter.setKeyboardSelectedRow(1, false, true); presenter.flush(); assertEquals(1, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(1, false); view.assertKeyboardSelectedRow(1, true); // Move to last row. presenter.setKeyboardSelectedRow(9, false, false); presenter.flush(); assertEquals(9, presenter.getKeyboardSelectedRow()); view.assertKeyboardSelectedRow(1, false); view.assertKeyboardSelectedRow(9, true); assertEquals(10, presenter.getVisibleRange().getStart()); assertEquals(pageSize, presenter.getVisibleRange().getLength()); // Move to next page. presenter.setKeyboardSelectedRow(10, false, false); populatePresenter(presenter); presenter.flush(); assertEquals(10, presenter.getKeyboardSelectedRow()); view.assertReplaceAllChildrenCalled(pageSize); view.assertKeyboardSelectedRowEmpty(); assertEquals(10, presenter.getVisibleRange().getStart()); pageSize += HasDataPresenter.PAGE_INCREMENT; assertEquals(pageSize, presenter.getVisibleRange().getLength()); // Negative index near index 0. presenter.setKeyboardSelectedRow(-1, false, false); populatePresenter(presenter); presenter.flush(); assertEquals(9, presenter.getKeyboardSelectedRow()); view.assertReplaceAllChildrenCalled(pageSize); view.assertKeyboardSelectedRowEmpty(); assertEquals(0, presenter.getVisibleRange().getStart()); pageSize += 10; assertEquals(pageSize, presenter.getVisibleRange().getLength()); // Negative index out of range. presenter.setVisibleRange(new Range(3, 10)); presenter.setKeyboardSelectedRow(3, false, false); populatePresenter(presenter); presenter.flush(); presenter.setKeyboardSelectedRow(-4, false, false); populatePresenter(presenter); presenter.flush(); assertEquals(0, presenter.getKeyboardSelectedRow()); assertEquals(0, presenter.getVisibleRange().getStart()); assertEquals(13, presenter.getVisibleRange().getLength()); } public void testSetRowCount() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); view.assertLoadingState(null); // Set size to 100. presenter.setRowCount(100, true); assertEquals(100, presenter.getRowCount()); assertTrue(presenter.isRowCountExact()); presenter.flush(); view.assertLoadingState(LoadingState.LOADING); // Set size to 0, but not exact. The state is loading until we know there is // no data. presenter.setRowCount(0, false); assertEquals(0, presenter.getRowCount()); assertFalse(presenter.isRowCountExact()); presenter.flush(); view.assertLoadingState(LoadingState.LOADING); // Set size to 0 and exact. Now we know the list is empty. presenter.setRowCount(0, true); assertEquals(0, presenter.getRowCount()); assertTrue(presenter.isRowCountExact()); presenter.flush(); view.assertLoadingState(LoadingState.LOADED); } public void testSetRowCountNoBoolean() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); try { presenter.setRowCount(100); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException e) { // Expected. } } public void testSetRowCountTrimsCurrentPage() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); view.assertLoadingState(null); // Initialize some data. presenter.setRowCount(10, true); presenter.setVisibleRange(new Range(0, 10)); assertEquals(new Range(0, 10), presenter.getVisibleRange()); populatePresenter(presenter); presenter.flush(); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); // Trim the size. presenter.setRowCount(8, true); assertEquals(8, presenter.getRowCount()); assertTrue(presenter.isRowCountExact()); assertEquals(new Range(0, 10), presenter.getVisibleRange()); assertEquals(8, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(8); view.assertLoadingState(LoadingState.LOADED); } public void testSetRowData() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(5, 10)); presenter.flush(); view.assertReplaceAllChildrenCalled(0); view.assertLoadingState(LoadingState.LOADING); // Page range same as data range. List<String> expectedData = createData(5, 10); presenter.setRowData(5, createData(5, 10)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(10); assertEquals(10, view.getChildCount()); view.assertLoadingState(LoadingState.LOADED); // Page range contains data range. expectedData.set(2, "test 100"); expectedData.set(3, "test 101"); presenter.setRowData(7, createData(100, 2)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceChildrenCalled(2, 2); assertEquals(10, view.getChildCount()); view.assertLoadingState(LoadingState.LOADED); // Data range overlaps page start. expectedData.set(0, "test 202"); expectedData.set(1, "test 203"); presenter.setRowData(3, createData(200, 4)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceChildrenCalled(0, 2); assertEquals(10, view.getChildCount()); view.assertLoadingState(LoadingState.LOADED); // Data range overlaps page end. expectedData.set(8, "test 300"); expectedData.set(9, "test 301"); presenter.setRowData(13, createData(300, 4)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceChildrenCalled(8, 2); assertEquals(10, view.getChildCount()); view.assertLoadingState(LoadingState.LOADED); // Data range contains page range. expectedData = createData(400, 20).subList(2, 12); presenter.setRowData(3, createData(400, 20)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(10); assertEquals(10, view.getChildCount()); view.assertLoadingState(LoadingState.LOADED); } /** * Setting data outside of the data size should update the data size. */ public void testSetRowValuesChangesDataSize() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Set the initial data size. presenter.setRowCount(10, true); presenter.flush(); view.assertLoadingState(LoadingState.LOADING); // Set the data within the range. presenter.setRowData(0, createData(0, 10)); presenter.flush(); view.assertLoadingState(LoadingState.LOADED); // Set the data past the range. presenter.setRowData(5, createData(5, 10)); assertEquals(15, presenter.getRowCount()); presenter.flush(); view.assertLoadingState(LoadingState.LOADED); } /** * Setting an empty list that starts on the page start should pass through to * the view. */ public void testSetRowValuesEmptySet() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Set the initial data size. presenter.setRowCount(10, true); presenter.flush(); view.assertLoadingState(LoadingState.LOADING); // Set an empty list of row values. presenter.setRowData(0, createData(0, 0)); presenter.flush(); view.assertLoadingState(LoadingState.LOADING); view.assertReplaceAllChildrenCalled(0); } public void testSetRowValuesOutsideRange() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); presenter.setVisibleRange(new Range(5, 10)); presenter.flush(); view.assertReplaceAllChildrenCalled(0); view.assertLoadingState(LoadingState.LOADING); // Page range same as data range. List<String> expectedData = createData(5, 10); presenter.setRowData(5, createData(5, 10)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); // Data range past page end. presenter.setRowData(15, createData(15, 5)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceChildrenNotCalled(); view.assertLoadingState(LoadingState.LOADED); // Data range before page start. presenter.setRowData(0, createData(0, 5)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceChildrenNotCalled(); view.assertLoadingState(LoadingState.LOADED); } /** * Test that modifying more than 30% of the rows forces a full redraw. */ public void testSetRowValuesRequiresRedraw() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 100, null); // Initialize 100% of the rows. populatePresenter(presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(100); // Modify 30% of the rows. presenter.setRowData(0, createData(0, 30)); presenter.flush(); view.assertReplaceChildrenCalled(0, 30); // Modify 31% of the rows. presenter.setRowData(0, createData(0, 31)); presenter.flush(); view.assertReplaceAllChildrenCalled(100); /* * Modify 4 rows in a 5 row table. This should NOT require a redraw because * it is less than the minimum threshold. */ presenter.setRowCount(5, true); presenter.flush(); view.assertReplaceAllChildrenCalled(5); presenter.setRowData(0, createData(0, 4)); presenter.flush(); view.assertReplaceChildrenCalled(0, 4); } /** * As an optimization, the presenter does not replace the rendered string if * the rendered string is identical to the previously rendered string. This is * useful for tables that refresh on an interval. */ public void testSetRowValuesSameContents() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); view.assertLoadingState(null); // Initialize some data. presenter.setVisibleRange(new Range(0, 10)); presenter.setRowData(0, createData(0, 10)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); // Set the same data over the entire range. presenter.setRowData(0, createData(0, 10)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); } /** * Set data at the end of the page only. */ public void testSetRowValuesSparse() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); view.assertLoadingState(null); List<String> expectedData = createData(5, 3); expectedData.add(0, null); expectedData.add(0, null); expectedData.add(0, null); expectedData.add(0, null); expectedData.add(0, null); presenter.setVisibleRange(new Range(0, 10)); presenter.setRowData(5, createData(5, 3)); assertPresenterRowData(expectedData, presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(8); view.assertLoadingState(LoadingState.PARTIALLY_LOADED); } public void testSetSelectionModel() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); assertNull(presenter.getSelectionModel()); // Initialize some data. presenter.setVisibleRange(new Range(0, 10)); populatePresenter(presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(10); // Set the selection model. SelectionModel<String> model = new MockSelectionModel<String>(null); model.setSelected("test 0", true); presenter.setSelectionModel(model); presenter.flush(); view.assertReplaceChildrenCalled(0, 1); // Select something. model.setSelected("test 2", true); presenter.flush(); view.assertReplaceChildrenCalled(2, 1); // Set selection model to null. presenter.setSelectionModel(null); presenter.flush(); view.assertReplaceChildrenCalled(0, 1); view.assertReplaceChildrenCalled(2, 1); view.assertReplaceChildrenNotCalled(); } public void testSetVisibleRange() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Set the range the first time. presenter.setVisibleRange(new Range(0, 100)); assertEquals(new Range(0, 100), presenter.getVisibleRange()); assertEquals(0, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceChildrenNotCalled(); view.assertLoadingState(LoadingState.LOADING); // Set the range to the same value. presenter.setVisibleRange(new Range(0, 100)); assertEquals(new Range(0, 100), presenter.getVisibleRange()); assertEquals(0, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceChildrenNotCalled(); view.assertLoadingState(LoadingState.LOADING); // Set the start to a negative value. try { presenter.setVisibleRange(new Range(-1, 100)); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { // Expected. } // Set the length to a negative value. try { presenter.setVisibleRange(new Range(0, -100)); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { // Expected. } } public void testSetVisibleRangeAndClearDataDifferentRange() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Add a range change handler. final List<Range> events = new ArrayList<Range>(); listView.addRangeChangeHandler(new RangeChangeEvent.Handler() { @Override public void onRangeChange(RangeChangeEvent event) { events.add(event.getNewRange()); } }); // Set some initial data. presenter.setVisibleRange(new Range(5, 10)); presenter.setRowData(5, createData(5, 10)); assertEquals(new Range(5, 10), presenter.getVisibleRange()); assertEquals(10, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); assertEquals(1, events.size()); // Set a different range. presenter.setVisibleRangeAndClearData(new Range(0, 10), false); assertEquals(new Range(0, 10), presenter.getVisibleRange()); assertEquals(0, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(0); view.assertLoadingState(LoadingState.LOADING); assertEquals(2, events.size()); } public void testSetVisibleRangeAndClearDataSameRange() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Add a range change handler. final List<Range> events = new ArrayList<Range>(); listView.addRangeChangeHandler(new RangeChangeEvent.Handler() { @Override public void onRangeChange(RangeChangeEvent event) { events.add(event.getNewRange()); } }); // Set some initial data. presenter.setRowData(0, createData(0, 10)); assertEquals(new Range(0, 10), presenter.getVisibleRange()); assertEquals(10, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); assertEquals(0, events.size()); // Set the same range. presenter.setVisibleRangeAndClearData(new Range(0, 10), false); assertEquals(new Range(0, 10), presenter.getVisibleRange()); assertEquals(0, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(0); view.assertLoadingState(LoadingState.LOADING); assertEquals(0, events.size()); } public void testSetVisibleRangeAndClearDataSameRangeForceEvent() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Add a range change handler. final List<Range> events = new ArrayList<Range>(); listView.addRangeChangeHandler(new RangeChangeEvent.Handler() { @Override public void onRangeChange(RangeChangeEvent event) { events.add(event.getNewRange()); } }); // Set some initial data. presenter.setRowData(0, createData(0, 10)); assertEquals(new Range(0, 10), presenter.getVisibleRange()); assertEquals(10, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); assertEquals(0, events.size()); // Set the same range. presenter.setVisibleRangeAndClearData(new Range(0, 10), true); assertEquals(new Range(0, 10), presenter.getVisibleRange()); assertEquals(0, presenter.getVisibleItemCount()); presenter.flush(); view.assertReplaceAllChildrenCalled(0); view.assertLoadingState(LoadingState.LOADING); assertEquals(1, events.size()); } public void testSetVisibleRangeDecreasePageSize() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Initialize some data. presenter.setVisibleRange(new Range(0, 10)); assertEquals(new Range(0, 10), presenter.getVisibleRange()); presenter.setRowData(0, createData(0, 10)); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); // Decrease the page size. presenter.setVisibleRange(new Range(0, 8)); assertEquals(new Range(0, 8), presenter.getVisibleRange()); assertEquals(8, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(8); view.assertLoadingState(LoadingState.LOADED); } public void testSetVisibleRangeDecreasePageStart() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Initialize some data. presenter.setVisibleRange(new Range(10, 30)); assertEquals(new Range(10, 30), presenter.getVisibleRange()); presenter.setRowData(10, createData(0, 10)); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.PARTIALLY_LOADED); // Decrease the start index. presenter.setVisibleRange(new Range(8, 30)); assertEquals(new Range(8, 30), presenter.getVisibleRange()); assertEquals(12, presenter.getVisibleItemCount()); assertEquals(null, presenter.getVisibleItem(0)); assertEquals(null, presenter.getVisibleItem(1)); assertEquals("test 0", presenter.getVisibleItem(2)); presenter.flush(); view.assertReplaceAllChildrenCalled(12); view.assertLoadingState(LoadingState.PARTIALLY_LOADED); } public void testSetVisibleRangeIncreasePageSize() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Initialize some data. presenter.setVisibleRange(new Range(0, 10)); assertEquals(new Range(0, 10), presenter.getVisibleRange()); presenter.setRowData(0, createData(0, 10)); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.LOADED); // Increase the page size. presenter.setVisibleRange(new Range(0, 20)); assertEquals(new Range(0, 20), presenter.getVisibleRange()); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceChildrenNotCalled(); view.assertLoadingState(LoadingState.PARTIALLY_LOADED); } public void testSetVisibleRangeIncreasePageStart() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Initialize some data. presenter.setVisibleRange(new Range(0, 20)); assertEquals(new Range(0, 20), presenter.getVisibleRange()); presenter.setRowData(0, createData(0, 10)); assertEquals(10, presenter.getVisibleItemCount()); assertEquals("test 0", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(10); view.assertLoadingState(LoadingState.PARTIALLY_LOADED); // Increase the start index. presenter.setVisibleRange(new Range(2, 20)); assertEquals(new Range(2, 20), presenter.getVisibleRange()); assertEquals(8, presenter.getVisibleItemCount()); assertEquals("test 2", presenter.getVisibleItem(0)); presenter.flush(); view.assertReplaceAllChildrenCalled(8); view.assertLoadingState(LoadingState.PARTIALLY_LOADED); } public void testSetVisibleRangeInts() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); try { presenter.setVisibleRange(0, 100); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException e) { // Expected. } } /** * Test that the view is correctly updated if we move the page start back and * forth in the same render loop. */ public void testSetVisibleRangeResetPageStart() { HasData<String> listView = new MockHasData<String>(); MockView<String> view = new MockView<String>(); HasDataPresenter<String> presenter = new HasDataPresenter<String>(listView, view, 10, null); // Initialize the view. populatePresenter(presenter); presenter.flush(); view.assertReplaceAllChildrenCalled(10); // Move pageStart to 2, then back to 0. presenter.setVisibleRange(new Range(2, 8)); presenter.setVisibleRange(new Range(0, 10)); presenter.flush(); view.assertReplaceChildrenCalled(0, 2); } /** * Assert that the expected List of values matches the row data in the * specified {@link HasDataPresenter}. * * @param <T> the data type * @param expected the expected values * @param presenter the presenter */ private <T> void assertPresenterRowData(List<T> expected, HasDataPresenter<T> presenter) { assertEquals(expected.size(), presenter.getVisibleItemCount()); for (int i = 0; i < expected.size(); i++) { assertEquals(expected.get(i), presenter.getVisibleItem(i)); } } /** * Assert that the specified set contains specified values in order. * * @param <T> the data type * @param list the list to check * @param values the expected values */ private <T> void assertListContains(List<T> list, T... values) { assertEquals(values.length, list.size()); for (int i = 0; i < values.length; i++) { assertEquals(values[i], list.get(i)); } } /** * Create a list of data for testing. * * @param start the start index * @param length the length * @return a list of data */ private List<String> createData(int start, int length) { List<String> toRet = new ArrayList<String>(); for (int i = 0; i < length; i++) { toRet.add("test " + (i + start)); } return toRet; } /** * Populate the entire range of a presenter. * * @param presenter the presenter */ private void populatePresenter(HasDataPresenter<String> presenter) { Range range = presenter.getVisibleRange(); int start = range.getStart(); int length = range.getLength(); presenter.setRowData(start, createData(start, length)); } /** * Test that the presenter can gracefully handle a view or * {@link SelectionModel} that throws an exception. * * @param presenter the presenter to test */ private void testPresenterWithBadUserCode(HasDataPresenter<String> presenter) { // Render some data with an exception. try { populatePresenter(presenter); presenter.setKeyboardSelectedRow(0, false, false); presenter.flush(); fail("Expected NullPointerException"); } catch (NullPointerException e) { // Expected. } // Render additional data with an exception. try { presenter.setVisibleRange(new Range(10, 10)); populatePresenter(presenter); presenter.setKeyboardSelectedRow(1, false, false); presenter.flush(); fail("Expected NullPointerException"); } catch (NullPointerException e) { /* * Expected. If we do not get a NullPointerException, then we are stuck in * the rendering loop. We should not get an IllegalStateException from the * rendering loop if the presenter fails gracefully. */ } } }