--- old/modules/controls/src/test/java/com/sun/javafx/scene/control/skin/VirtualFlowTest.java 2015-09-03 15:30:28.522580100 -0700 +++ /dev/null 2015-09-03 15:30:29.000000000 -0700 @@ -1,1084 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.javafx.scene.control.skin; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.util.Iterator; -import java.util.LinkedList; - -import javafx.beans.InvalidationListener; -import javafx.event.Event; -import javafx.scene.control.IndexedCell; -import javafx.scene.control.SkinStub; -import javafx.scene.input.ScrollEvent; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import com.sun.javafx.scene.CssFlags; -import com.sun.javafx.scene.control.skin.VirtualFlow.ArrayLinkedList; -import java.util.List; -import javafx.util.Callback; - -/** - * Tests for the VirtualFlow class. VirtualFlow is the guts of the ListView, - * TreeView, and TableView implementations. - */ -public class VirtualFlowTest { - // The following 4 vars are used when testing the - private ArrayLinkedList list; - private CellStub a; - private CellStub b; - private CellStub c; - - // The VirtualFlow we are going to test. By default, there are 100 cells - // and each cell is 100 wide and 25 tall, except for the 30th cell, which - // is 200 wide and 100 tall. - private VirtualFlow flow; -// private Scene scene; - - @Before public void setUp() { - list = new ArrayLinkedList(); - a = new CellStub(flow, "A"); - b = new CellStub(flow, "B"); - c = new CellStub(flow, "C"); - - flow = new VirtualFlow(); -// flow.setManaged(false); - flow.setVertical(true); - flow.setCreateCell(p -> new CellStub(flow) { - @Override protected double computeMinWidth(double height) { return computePrefWidth(height); } - @Override protected double computeMaxWidth(double height) { return computePrefWidth(height); } - @Override protected double computePrefWidth(double height) { - return flow.isVertical() ? (c.getIndex() == 29 ? 200 : 100) : (c.getIndex() == 29 ? 100 : 25); - } - - @Override protected double computeMinHeight(double width) { return computePrefHeight(width); } - @Override protected double computeMaxHeight(double width) { return computePrefHeight(width); } - @Override protected double computePrefHeight(double width) { - return flow.isVertical() ? (c.getIndex() == 29 ? 100 : 25) : (c.getIndex() == 29 ? 200 : 100); - } - }); - flow.setCellCount(100); - flow.resize(300, 300); - pulse(); - } - - private void pulse() { -// flow.impl_processCSS(true); - flow.layout(); - } - - /** - * Asserts that the items in the control LinkedList and the ones in the - * list are exactly the same. - */ - private void assertMatch(List control, ArrayLinkedList list) { - assertEquals("The control and list did not have the same sizes. " + - "Expected " + control.size() + " but was " + list.size(), - control.size(), list.size()); - int index = 0; - Iterator itr = control.iterator(); - while (itr.hasNext()) { - IndexedCell cell = itr.next(); - IndexedCell cell2 = list.get(index); - assertSame("The control and list did not have the same item at " + - "index " + index + ". Expected " + cell + " but was " + cell2, - cell, cell2); - index++; - } - } - - /** - * Asserts that only the minimal number of cells are used. - */ - public void assertMinimalNumberOfCellsAreUsed(VirtualFlow flow) { - pulse(); - IndexedCell firstCell = flow.cells.getFirst(); - IndexedCell lastCell = flow.cells.getLast(); - if (flow.isVertical()) { - // First make sure that enough cells were created - assertTrue("There is a gap between the top of the viewport and the first cell", - firstCell.getLayoutY() <= 0); - assertTrue("There is a gap between the bottom of the last cell and the bottom of the viewport", - lastCell.getLayoutY() + lastCell.getHeight() >= flow.getViewportLength()); - - // Now make sure that no extra cells were created. - if (flow.cells.size() > 3) { - IndexedCell secondLastCell = flow.cells.get(flow.cells.size() - 2); - IndexedCell secondCell = flow.cells.get(1); - assertFalse("There are more cells created before the start of " + - "the flow than necessary", - secondCell.getLayoutY() <= 0); - assertFalse("There are more cells created after the end of the " + - "flow than necessary", - secondLastCell.getLayoutY() + secondLastCell.getHeight() >= flow.getViewportLength()); - } - } else { - // First make sure that enough cells were created - assertTrue("There is a gap between the left of the viewport and the first cell", - firstCell.getLayoutX() <= 0); - assertTrue("There is a gap between the right of the last cell and the right of the viewport", - lastCell.getLayoutX() + lastCell.getWidth() >= flow.getViewportLength()); - - // Now make sure that no extra cells were created. - if (flow.cells.size() > 3) { - IndexedCell secondLastCell = flow.cells.get(flow.cells.size() - 2); - IndexedCell secondCell = flow.cells.get(1); - assertFalse("There are more cells created before the start of " + - "the flow than necessary", - secondCell.getLayoutX() <= 0); - assertFalse("There are more cells created after the end of the " + - "flow than necessary", - secondLastCell.getLayoutX() + secondLastCell.getWidth() >= flow.getViewportLength()); - } - } - } - - - /*************************************************************************** - * Tests for VirtualFlow * - * * - * These tests are broken out into several broad categories: * - * - general layout (position of scroll bars, viewport, etc) * - * - cell layout * - * - cell life cycle (creation, configuration, reuse, etc) * - * - position (stable view, adjusts when necessary, etc) * - * - pixel scrolling (cells are reused, position updated, etc) * - * * - * - Test that the preferred width of a vertical flow takes into account * - * the preferred width of the cells that are visible * - * - Test the same for horizontal when working with a horizontal flow * - * - Test that cells are laid out as expected in a vertical flow * - * - Test the same for a horizontal flow * - * - Test that the width of cells in a vertical flow adjusts based on the * - * width of the flow's content area * - * - Test the same for the height of cells in a horizontal flow * - * - Test that changing the number of cells (up and down) also adjusts * - * the position such that it is "stable", unless that is not possible * - * - Test that after changing the cell factory, things are rebuilt * - * - Test that after changing the cell config function, things are * - * reconfigured. * - * - Test that functions which add to the pile and so forth work as * - * expected. * - * - Test the layout of the scroll bars in various combinations, along * - * with the corner region and so forth. * - * * - **************************************************************************/ - - //////////////////////////////////////////////////////////////////////////// - // - // General Layout - // - //////////////////////////////////////////////////////////////////////////// - - /** - * In this test there are no cells. The VirtualFlow should be laid out such - * that the scroll bars and corner are not visible, and the clip view fills - * the entire width/height of the VirtualFlow. - */ - @Test public void testGeneralLayout_NoCells() { - flow.setCellCount(0); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * When we have a few cells, not enough to fill the viewport, then we need - * to make sure there is no virtual scroll bar. In this test case the cells - * are not wider than the viewport so no horizontal bar either. - */ - @Test public void testGeneralLayout_FewCells() { - flow.setCellCount(3); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertEquals(12, flow.cells.size()); // we stil have 12 cells (300px / 25px), even if only three filled cells exist - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); -// assertEquals(3, flow.cells.size()); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Tests the case of a few cells (so not requiring a vertical scroll bar), - * but the cells are wider than the viewport so a horizontal scroll bar is - * required. - */ - @Test public void testGeneralLayout_FewCellsButWide() { - flow.setCellCount(3); - flow.resize(50, flow.getHeight()); - pulse(); - assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); - assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - flow.resize(300, 50); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Tests that having a situation where the hbar in a vertical flow is - * necessary (due to wide cells) will end up hiding the hbar if the flow - * becomes wide enough that the hbar is no longer necessary. - */ - @Test public void testGeneralLayout_FewCellsButWide_ThenNarrow() { - flow.setCellCount(3); - flow.resize(50, flow.getHeight()); - pulse(); - flow.resize(300, flow.getHeight()); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - flow.resize(300, 50); - pulse(); - flow.resize(flow.getWidth(), 300); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Tests that when there are many cells then the vbar in a vertical flow - * is used. - *

- * Note, this test uncovered a bug where the bottom of the last cell was - * exactly on the bottom edge of the flow and the vbar was not made visible. - * Be sure to test for this explicitly some time! - */ - @Test public void testGeneralLayout_ManyCells() { - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - pulse(); - assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); - assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Test that after having only a few cells, if I then have many cells, that - * the vbar is shown appropriately. - */ - @Test public void testGeneralLayout_FewCells_ThenMany() { - flow.setCellCount(3); - pulse(); - flow.setCellCount(100); - pulse(); - assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); - assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); - assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - flow.setCellCount(3); - pulse(); - flow.setCellCount(100); - pulse(); - assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); - assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); - assertFalse("The corner should have been invisible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); - assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Test the case where there are many cells and they are wider than the - * viewport. We should have the hbar, vbar, and corner region in this case. - */ - @Test public void testGeneralLayout_ManyCellsAndWide() { - flow.resize(50, flow.getHeight()); - pulse(); - assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); - assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); - assertTrue("The corner should have been visible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); - assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getVbar().getWidth(), flow.corner.getWidth(), 0.0); - assertEquals(flow.getHbar().getHeight(), flow.corner.getHeight(), 0.0); - assertEquals(flow.getHbar().getWidth(), flow.getWidth() - flow.corner.getWidth(), 0.0); - assertEquals(flow.getVbar().getHeight(), flow.getHeight() - flow.corner.getHeight(), 0.0); - assertEquals(flow.corner.getLayoutX(), flow.getWidth() - flow.corner.getWidth(), 0.0); - assertEquals(flow.corner.getLayoutY(), flow.getHeight() - flow.corner.getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - - flow.setVertical(false); - flow.resize(300, 50); - pulse(); - assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); - assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); - assertTrue("The corner should have been visible", flow.corner.isVisible()); - assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); - assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); - assertEquals(flow.getVbar().getWidth(), flow.corner.getWidth(), 0.0); - assertEquals(flow.getHbar().getHeight(), flow.corner.getHeight(), 0.0); - assertEquals(flow.getHbar().getWidth(), flow.getWidth() - flow.corner.getWidth(), 0.0); - assertEquals(flow.getVbar().getHeight(), flow.getHeight() - flow.corner.getHeight(), 0.0); - assertEquals(flow.corner.getLayoutX(), flow.getWidth() - flow.corner.getWidth(), 0.0); - assertEquals(flow.corner.getLayoutY(), flow.getHeight() - flow.corner.getHeight(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Tests that when the vertical flag changes, it results in layout - */ - @Test public void testGeneralLayout_VerticalChangeResultsInNeedsLayout() { - assertFalse(flow.isNeedsLayout()); - flow.setVertical(false); - assertTrue(flow.isNeedsLayout()); - } - - /** - * Tests that the range of the non-virtual scroll bar is valid - */ - @Test public void testGeneralLayout_NonVirtualScrollBarRange() { - flow.resize(50, flow.getHeight()); - pulse(); - assertEquals(0, flow.getHbar().getMin(), 0.0); - assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getWidth(), flow.getHbar().getMax(), 0.0); - assertEquals((flow.clipView.getWidth()/flow.getMaxPrefBreadth()) * flow.getHbar().getMax(), flow.getHbar().getVisibleAmount(), 0.0); - flow.setPosition(.28f); - pulse(); - assertEquals(0, flow.getHbar().getMin(), 0.0); - assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getWidth(), flow.getHbar().getMax(), 0.0); - assertEquals((flow.clipView.getWidth()/flow.getMaxPrefBreadth()) * flow.getHbar().getMax(), flow.getHbar().getVisibleAmount(), 0.0); - - flow.setVertical(false); - flow.setPosition(0); - flow.resize(300, 50); - pulse(); - assertEquals(0, flow.getVbar().getMin(), 0.0); - assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getHeight(), flow.getVbar().getMax(), 0.0); - assertEquals((flow.clipView.getHeight()/flow.getMaxPrefBreadth()) * flow.getVbar().getMax(), flow.getVbar().getVisibleAmount(), 0.0); - flow.setPosition(.28); - pulse(); - assertEquals(0, flow.getVbar().getMin(), 0.0); - assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getHeight(), flow.getVbar().getMax(), 0.0); - assertEquals((flow.clipView.getHeight()/flow.getMaxPrefBreadth()) * flow.getVbar().getMax(), flow.getVbar().getVisibleAmount(), 0.0); - } - - /** - * Tests that the maxPrefBreadth is computed correctly for the first page of cells. - * In our test case, the first page of cells have a uniform pref. - */ - @Test public void testGeneralLayout_maxPrefBreadth() { - assertEquals(100, flow.getMaxPrefBreadth(), 0.0); - } - - /** - * Tests that even after the first computation of max pref, that it is - * updated when we encounter a new cell (while scrolling for example) that - * has a larger pref. - */ - @Ignore - @Test public void testGeneralLayout_maxPrefBreadthUpdatedWhenEncounterLargerPref() { - flow.setPosition(.28); - pulse(); - assertEquals(200, flow.getMaxPrefBreadth(), 0.0); - } - - /** - * Tests that if we encounter cells or pages of cells with smaller prefs - * than the max pref that we will keep the max pref the same. - */ - @Ignore - @Test public void testGeneralLayout_maxPrefBreadthRemainsSameWhenEncounterSmallerPref() { - flow.setPosition(.28); - pulse(); - flow.setPosition(.8); - pulse(); - assertEquals(200, flow.getMaxPrefBreadth(), 0.0); - } - - /** - * Tests that changes to the vertical property will clear the maxPrefBreadth - */ - @Test public void testGeneralLayout_VerticalChangeClearsmaxPrefBreadth() { - flow.setVertical(false); - assertEquals(-1, flow.getMaxPrefBreadth(), 0.0); - } - - /** - * Tests that changes to the cell count will not affect maxPrefBreadth. - */ - @Ignore - @Test public void testGeneralLayout_maxPrefBreadthUnaffectedByCellCountChanges() { - flow.setCellCount(10); - pulse(); - assertEquals(100, flow.getMaxPrefBreadth(), 0.0); - flow.setCellCount(100); - pulse(); - flow.setPosition(.28); - pulse(); - assertEquals(200, flow.getMaxPrefBreadth(), 0.0); - flow.setCellCount(10); - pulse(); - assertEquals(200, flow.getMaxPrefBreadth(), 0.0); - } - - /** - * Tests that as we scroll, if the non-virtual scroll bar is visible, then - * as we update maxPrefBreadth it will not affect the non-virtual scroll bar's - * value unless the value is such that the scroll bar is scrolled - * all the way to the end, in which case it will remain scrolled to the - * end. - */ - @Test public void testGeneralLayout_maxPrefBreadthScrollBarValueInteraction() { - flow.resize(50, flow.getHeight()); - flow.getHbar().setValue(30); - pulse(); - flow.setPosition(.28); - pulse(); - assertEquals(30, flow.getHbar().getValue(), 0.0); - - // Reset the test and this time check what happens when we are scrolled - // to the very right - flow.setPosition(0); - flow.setVertical(false); - flow.setVertical(true); - pulse(); - assertEquals(100, flow.getMaxPrefBreadth(), 0.0); - flow.getHbar().setValue(flow.getHbar().getMax()); // scroll to the end - flow.setPosition(.28); - pulse(); - assertEquals(flow.getHbar().getMax(), flow.getHbar().getValue(), 0.0); - - flow.setVertical(false); - flow.setPosition(0); - flow.getHbar().setValue(0); - flow.resize(300, 50); - pulse(); - flow.getVbar().setValue(30); - pulse(); - flow.setPosition(.28); - pulse(); - assertEquals(30, flow.getVbar().getValue(), 0.0); - - // Reset the test and this time check what happens when we are scrolled - // to the very right - flow.setPosition(0); - flow.setVertical(true); - flow.setVertical(false); - pulse(); - assertEquals(100, flow.getMaxPrefBreadth(), 0.0); - flow.getVbar().setValue(flow.getVbar().getMax()); // scroll to the end - flow.setPosition(.28); - pulse(); - assertEquals(flow.getVbar().getMax(), flow.getVbar().getValue(), 0.0); - } - - @Test public void testGeneralLayout_ScrollToEndOfVirtual_BarStillVisible() { - assertTrue("The vbar was expected to be visible", flow.getVbar().isVisible()); - flow.setPosition(1); - pulse(); - assertTrue("The vbar was expected to be visible", flow.getVbar().isVisible()); - - flow.setPosition(0); - flow.setVertical(false); - pulse(); - assertTrue("The hbar was expected to be visible", flow.getHbar().isVisible()); - flow.setPosition(1); - pulse(); - assertTrue("The hbar was expected to be visible", flow.getHbar().isVisible()); - } - - // Need to test all the resize operations and make sure the position of - // nodes is as expected, that they don't get shifted etc. - - // Test: Scroll to the bottom, expand size out, then make smaller. The - // thumb/scroll is not consistent right now. - - // TODO figure out and deal with what happens when orientation changes - // to the hbar.value and vbar.value. Do they just switch? Probably not? - // Probably reset the non-virtual direction and swap the virtual one over. - // So if vbar was .5 and hbar was 30, when we set vertical = false, then - // we change the hbar to .5 and the vbar to 0. However this has to be done - // at the same time that the "virtual" property of the scroll bars is - // changed - - //////////////////////////////////////////////////////////////////////////// - // - // Cell Layout - // - //////////////////////////////////////////////////////////////////////////// - - /** - * Test to make sure that we are virtual -- that all cells are not being - * created. - */ - @Test public void testCellLayout_NotAllCellsAreCreated() { - // due to the initial size of the VirtualFlow and the number of cells - // and their heights, we should have more cells than we have space to - // fit them and so only enough cells should be created to meet our - // needs and not any more than that - assertTrue("All of the cells were created", flow.cells.size() < flow.getCellCount()); - assertMinimalNumberOfCellsAreUsed(flow); - } - - /** - * Tests the size and position of all the cells to make sure they were - * laid out properly. - */ - @Test public void testCellLayout_CellSizes_AfterLayout() { - double offset = 0.0; - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(25, cell.getHeight(), 0.0); - assertEquals(offset, cell.getLayoutY(), 0.0); - offset += cell.getHeight(); - } - - offset = 0.0; - flow.setVertical(false); - pulse(); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(25, cell.getWidth(), 0.0); - assertEquals(offset, cell.getLayoutX(), 0.0); - offset += cell.getWidth(); - } - } - - /** - * Test the widths of the cells when the viewport is wider than the - * max pref width/height. They should be uniform, and should be the - * width of the viewport. - */ - @Test public void testCellLayout_ViewportWiderThanmaxPrefBreadth() { - // Note that the pref width of everything is 100, but the actual - // available width is much larger (300 - hbar.width or - // 300 - vbar.height) and so the non-virtual dimension of the cell - // should be larger than the max pref - double expected = flow.clipView.getWidth(); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(expected, cell.getWidth(), 0.0); - } - - flow.setVertical(false); - pulse(); - expected = flow.clipView.getHeight(); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(expected, cell.getHeight(), 0.0); - } - } - - /** - * Test the widths of the cells when the viewport is shorter than the - * max pref width/height. They should be uniform, and should be the max - * pref. - */ - @Test public void testCellLayout_ViewportShorterThanmaxPrefBreadth() { - flow.resize(50, flow.getHeight()); - pulse(); - assertEquals(100, flow.getMaxPrefBreadth(), 0.0); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(flow.getMaxPrefBreadth(), cell.getWidth(), 0.0); - } - - flow.setVertical(false); - flow.resize(flow.getWidth(), 50); - pulse(); - assertEquals(100, flow.getMaxPrefBreadth(), 0.0); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(flow.getMaxPrefBreadth(), cell.getHeight(), 0.0); - } - } - - /** - * Test that when we scroll and encounter a cell which has a larger pref - * than we have previously encountered (happens in this test when we visit - * the cell for item #29), then the max pref is updated and the cells are - * all resized to match. - */ - @Ignore - @Test public void testCellLayout_ScrollingFindsCellWithLargemaxPrefBreadth() { - flow.resize(50, flow.getHeight()); - flow.setPosition(.28); // happens to position such that #29 is visible - pulse(); - assertEquals(200, flow.getMaxPrefBreadth(), 0.0); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(flow.getMaxPrefBreadth(), cell.getWidth(), 0.0); - } - - flow.setVertical(false); - flow.resize(flow.getWidth(), 50); - // NOTE Run this test without the pulse and it fails! - pulse(); - flow.setPosition(.28); - pulse(); - assertEquals(200, flow.getMaxPrefBreadth(), 0.0); - for (int i = 0; i < flow.cells.size(); i++) { - IndexedCell cell = flow.cells.get(i); - assertEquals(flow.getMaxPrefBreadth(), cell.getHeight(), 0.0); - } - } - - /** - * Checks that the initial set of cells (the first page of cells) are - * indexed starting with cell #0 and working up from there. - */ - @Test public void testCellLayout_CellIndexes_FirstPage() { - for (int i = 0; i < flow.cells.size(); i++) { - assertEquals(i, flow.cells.get(i).getIndex()); - } - } - - /** - * The bug here is that if layout() is called on the flow numerous times, - * but nothing substantially has changed, we should reuse the cells in the - * same order they were before. We had a bug where when you clicked on the - * ListView, the click wouldn't register. This was because by clicking we - * were giving focus to the ListView, which caused a layout(), and then the - * cells location was shuffled. It didn't look like it to the user, but that - * is what happened. As a result, when the mouse release took place, the - * event was delivered to a different cell than expected and misbehavior - * took place. - */ - @Test public void testCellLayout_LayoutWithoutChangingThingsUsesCellsInSameOrderAsBefore() { - List cells = new LinkedList(); - for (int i = 0; i < flow.cells.size(); i++) { - cells.add(flow.cells.get(i)); - } - assertMatch(cells, flow.cells); // sanity check - flow.requestLayout(); - pulse(); - assertMatch(cells, flow.cells); - flow.setPosition(1); - pulse(); - cells.clear(); - for (int i = 0; i < flow.cells.size(); i++) { - cells.add(flow.cells.get(i)); - } - flow.requestLayout(); - pulse(); - assertMatch(cells, flow.cells); - } - - - @Test - public void testCellLayout_BiasedCellAndLengthBar() { - flow.setCreateCell(param -> new CellStub(flow) { - @Override protected double computeMinWidth(double height) { return 0; } - @Override protected double computeMaxWidth(double height) { return Double.MAX_VALUE; } - @Override protected double computePrefWidth(double height) { - return 200; - } - - @Override protected double computeMinHeight(double width) { return 0; } - @Override protected double computeMaxHeight(double width) { return Double.MAX_VALUE; } - @Override protected double computePrefHeight(double width) { - return getIndex() == 0 ? 100 - 5 *(Math.floorDiv((int)width - 200, 10)) : 100; - } - }); - flow.setCellCount(3); - flow.recreateCells(); // This help to override layoutChildren() in flow.setCellCount() - flow.getVbar().setPrefWidth(20); // Since Skins are not initialized, we set the pref width explicitly - flow.requestLayout(); - pulse(); - assertEquals(300, flow.cells.get(0).getWidth(), 1e-100); - assertEquals(50, flow.cells.get(0).getHeight(), 1e-100); - - flow.resize(200, 300); - - flow.requestLayout(); - pulse(); - assertEquals(200, flow.cells.get(0).getWidth(), 1e-100); - assertEquals(100, flow.cells.get(0).getHeight(), 1e-100); - - } - - //////////////////////////////////////////////////////////////////////////// - // - // Cell Life Cycle - // - //////////////////////////////////////////////////////////////////////////// - - @Test public void testCellLifeCycle_CellsAreCreatedOnLayout() { - // when the flow was first created in setUp we do a layout() - assertTrue("The cells didn't get created", flow.cells.size() > 0); - } - -// /** -// * During layout the order and contents of cells will change. We need -// * to make sure that CSS for cells is applied at this time. To test this, -// * I just set the position and perform a new pulse. Since layout happens -// * after the CSS updates are applied, if the test fails, then there will -// * be cells left in a state where they need their CSS applied. -// */ -// @Test public void testCellLifeCycle_CSSUpdatesHappenDuringLayout() { -// flow.setPosition(.35); -// pulse(); -// for (int i = 0; i < flow.cells.size(); i++) { -// IndexedCell cell = flow.cells.get(i); -// assertEquals(CssFlags.CLEAN, cell.impl_getCSSFlags()); -// } -// } - - //////////////////////////////////////////////////////////////////////////// - // - // Position - // - //////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////// - // - // Pixel Scrolling - // - //////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////// - // - // Cell Count Changes - // - //////////////////////////////////////////////////////////////////////////// - - // want to test that the view remains stable when the cell count changes - - // LIST VIEW: test that using a bunch of nodes as items to a ListView works - // LIST VIEW: test that inserting an item moves the selected index to keep in sync - // test that dynamically changing all of the contents causes them to refresh - - // test that when the number of cells change, that things are laid out -// @Test public void testCellCountChanges_FirstRowIsRemoved() { -// -// } -// -// @Test public void testCellCountChanges_MiddleRowIsRemoved() { -// -// } -// -// @Test public void testCellCountChanges_LastRowIsRemoved() { -// -// } -// -//// @Test public void testCellCountChanges_SelectedRowRemoved() { -//// -//// } -//// -//// @Test public void testCellCountChanges_NonSelectedRowRemoved() { -//// -//// } -// -// @Test public void testCellCountChanges_FirstRowIsAdded() { -// -// } -// -// @Test public void testCellCountChanges_MiddleRowIsAdded() { -// -// } -// -// @Test public void testCellCountChanges_LastRowIsAdded() { -// -// } -// -//// @Test public void testCellCountChanges_RowIsAddedBeforeSelectedRow() { -//// -//// } -//// -//// @Test public void testCellCountChanges_RowIsAddedAfterSelectedRow() { -//// -//// } - - //////////////////////////////////////////////////////////////////////////// - // - // VirtualFlow State Changes - // - //////////////////////////////////////////////////////////////////////////// - - /** - * Tests that when the createCell method changes, it results in layout - */ - @Test public void testCreateCellFunctionChangesResultInNeedsLayoutAndNoCellsAndNoAccumCell() { - assertFalse(flow.isNeedsLayout()); - flow.getCellLength(49); // forces accum cell to be created - assertNotNull("Accum cell was null", flow.accumCell); - flow.setCreateCell(p -> new CellStub(flow)); - assertTrue(flow.isNeedsLayout()); - assertNull("accumCell didn't get cleared", flow.accumCell); - } - - - //////////////////////////////////////////////////////////////////////////// - // - // Tests on specific functions - // - //////////////////////////////////////////////////////////////////////////// - - @Test public void test_getCellLength() { - assertEquals(100, flow.getCellCount()); - for (int i = 0; i < 50; i++) { - if (i != 29) assertEquals(25, flow.getCellLength(i), 0.0); - } - flow.setVertical(false); - flow.requestLayout(); - pulse(); - assertEquals(100, flow.getCellCount()); - for (int i = 0; i < 50; i++) { - if (i != 29) assertEquals("Bad index: " + i, 25, flow.getCellLength(i), 0.0); - } - } - - /* - ** if we scroll the flow by a number of LINES, - ** without having done anything to select a cell - ** the flow should scroll. - */ - @Test public void testInitialScrollEventActuallyScrolls() { - /* - ** re-initialize this, as it must be the first - ** interaction with the flow - */ - flow = new VirtualFlow(); - flow.setVertical(true); - flow.setCreateCell(p -> new CellStub(flow) { - @Override protected double computeMinWidth(double height) { return computePrefWidth(height); } - @Override protected double computeMaxWidth(double height) { return computePrefWidth(height); } - @Override protected double computePrefWidth(double height) { - return flow.isVertical() ? (c.getIndex() == 29 ? 200 : 100) : (c.getIndex() == 29 ? 100 : 25); - } - - @Override protected double computeMinHeight(double width) { return computePrefHeight(width); } - @Override protected double computeMaxHeight(double width) { return computePrefHeight(width); } - @Override protected double computePrefHeight(double width) { - return flow.isVertical() ? (c.getIndex() == 29 ? 100 : 25) : (c.getIndex() == 29 ? 200 : 100); - } - }); - - flow.setCellCount(100); - flow.resize(300, 300); - pulse(); - - double originalValue = flow.getPosition(); - - Event.fireEvent(flow, - new ScrollEvent(ScrollEvent.SCROLL, - 0.0, -10.0, 0.0, -10.0, - false, false, false, false, true, false, - 0, 0, - 0, 0, - ScrollEvent.HorizontalTextScrollUnits.NONE, 0.0, - ScrollEvent.VerticalTextScrollUnits.LINES, -1.0, - 0, null)); - - assertTrue(originalValue != flow.getPosition()); - } - - @Test - public void test_RT_36507() { - flow = new VirtualFlow(); - flow.setVertical(true); - // Worst case scenario is that the cells have height = 0. - // The code should prevent creating more than 100 of these zero height cells - // (since viewportLength is 100). - // An "INFO: index exceeds maxCellCount" message should print out. - flow.setCreateCell(p -> new CellStub(flow) { - @Override - protected double computeMaxHeight(double width) { return 0; } - @Override - protected double computePrefHeight(double width) { return 0; } - @Override - protected double computeMinHeight(double width) { return 0; } - - }); - flow.setCellCount(10); - flow.setViewportLength(100); - flow.addLeadingCells(1, 0); - flow.sheetChildren.addListener((InvalidationListener) (o) -> { - int count = ((List) o).size(); - assertTrue(Integer.toString(count), count <= 100); - }); - flow.addTrailingCells(true); - } - - private int rt36556_instanceCount; - @Test - public void test_rt36556() { - rt36556_instanceCount = 0; - flow = new VirtualFlow(); - flow.setVertical(true); - flow.setCreateCell(p -> { - rt36556_instanceCount++; - return new CellStub(flow); - }); - flow.setCellCount(100); - flow.resize(300, 300); - pulse(); - final int cellCountAtStart = rt36556_instanceCount; - flow.adjustPixels(10000); - pulse(); - assertEquals(cellCountAtStart, rt36556_instanceCount); - assertNull(flow.getVisibleCell(0)); - assertMinimalNumberOfCellsAreUsed(flow); - } - - @Test - public void test_rt36556_scrollto() { - rt36556_instanceCount = 0; - flow = new VirtualFlow(); - flow.setVertical(true); - flow.setCreateCell(p -> { - rt36556_instanceCount++; - return new CellStub(flow); - }); - flow.setCellCount(100); - flow.resize(300, 300); - pulse(); - final int cellCountAtStart = rt36556_instanceCount; - flow.scrollTo(80); - pulse(); - assertEquals(cellCountAtStart, rt36556_instanceCount); - assertNull(flow.getVisibleCell(0)); - assertMinimalNumberOfCellsAreUsed(flow); - } - - @Test - public void test_RT39035() { - flow.adjustPixels(250); - pulse(); - flow.adjustPixels(500); - pulse(); - assertTrue(flow.getPosition() < 1.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - @Test - public void test_RT37421() { - flow.setPosition(0.98); - pulse(); - flow.adjustPixels(100); - pulse(); - assertEquals(1.0, flow.getPosition(), 0.0); - assertMinimalNumberOfCellsAreUsed(flow); - } - - @Test - public void test_RT39568() { - flow.getHbar().setPrefHeight(16); - flow.resize(50, flow.getHeight()); - flow.setPosition(1); - pulse(); - assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); - assertMinimalNumberOfCellsAreUsed(flow); - assertEquals(flow.getViewportLength()-25.0, flow.cells.getLast().getLayoutY(), 0.0); - } -} - -class CellStub extends IndexedCell { - String s; - VirtualFlow flow; - - public CellStub(VirtualFlow flow) { init(flow); } - public CellStub(VirtualFlow flow, String s) { init(flow); this.s = s; } - - private void init(VirtualFlow flow) { - this.flow = flow; - setSkin(new SkinStub(this)); - updateItem(this, false); - } - - @Override - public void updateIndex(int i) { - super.updateIndex(i); - - s = "Item " + getIndex(); -// updateItem(getIndex(), getIndex() >= flow.getCellCount()); - } -} --- /dev/null 2015-09-03 15:30:29.000000000 -0700 +++ new/modules/controls/src/test/java/javafx/scene/control/skin/VirtualFlowTest.java 2015-09-03 15:30:27.729534700 -0700 @@ -0,0 +1,1144 @@ +/* + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javafx.scene.control.skin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.Iterator; +import java.util.LinkedList; + +import javafx.beans.InvalidationListener; +import javafx.event.Event; +import javafx.scene.control.IndexedCell; +import javafx.scene.control.SkinStub; +import javafx.scene.control.skin.VirtualFlow.ArrayLinkedList; +import javafx.scene.input.ScrollEvent; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.List; + +/** + * Tests for the VirtualFlow class. VirtualFlow is the guts of the ListView, + * TreeView, and TableView implementations. + */ +public class VirtualFlowTest { + // The following 4 vars are used when testing the + private ArrayLinkedList list; + private CellStub a; + private CellStub b; + private CellStub c; + + // The VirtualFlow we are going to test. By default, there are 100 cells + // and each cell is 100 wide and 25 tall, except for the 30th cell, which + // is 200 wide and 100 tall. + private VirtualFlow flow; +// private Scene scene; + + @Before public void setUp() { + list = new ArrayLinkedList(); + a = new CellStub(flow, "A"); + b = new CellStub(flow, "B"); + c = new CellStub(flow, "C"); + + flow = new VirtualFlow(); +// flow.setManaged(false); + flow.setVertical(true); + flow.setCellFactory(p -> new CellStub(flow) { + @Override + protected double computeMinWidth(double height) { + return computePrefWidth(height); + } + + @Override + protected double computeMaxWidth(double height) { + return computePrefWidth(height); + } + + @Override + protected double computePrefWidth(double height) { + return flow.isVertical() ? (c.getIndex() == 29 ? 200 : 100) : (c.getIndex() == 29 ? 100 : 25); + } + + @Override + protected double computeMinHeight(double width) { + return computePrefHeight(width); + } + + @Override + protected double computeMaxHeight(double width) { + return computePrefHeight(width); + } + + @Override + protected double computePrefHeight(double width) { + return flow.isVertical() ? (c.getIndex() == 29 ? 100 : 25) : (c.getIndex() == 29 ? 200 : 100); + } + }); + flow.setCellCount(100); + flow.resize(300, 300); + pulse(); + } + + private void pulse() { +// flow.impl_processCSS(true); + flow.layout(); + } + + /** + * Asserts that the items in the control LinkedList and the ones in the + * list are exactly the same. + */ + private void assertMatch(List control, ArrayLinkedList list) { + assertEquals("The control and list did not have the same sizes. " + + "Expected " + control.size() + " but was " + list.size(), + control.size(), list.size()); + int index = 0; + Iterator itr = control.iterator(); + while (itr.hasNext()) { + IndexedCell cell = itr.next(); + IndexedCell cell2 = list.get(index); + assertSame("The control and list did not have the same item at " + + "index " + index + ". Expected " + cell + " but was " + cell2, + cell, cell2); + index++; + } + } + + /** + * Asserts that only the minimal number of cells are used. + */ + public void assertMinimalNumberOfCellsAreUsed(VirtualFlow flow) { + pulse(); + IndexedCell firstCell = flow.cells.getFirst(); + IndexedCell lastCell = flow.cells.getLast(); + if (flow.isVertical()) { + // First make sure that enough cells were created + assertTrue("There is a gap between the top of the viewport and the first cell", + firstCell.getLayoutY() <= 0); + assertTrue("There is a gap between the bottom of the last cell and the bottom of the viewport", + lastCell.getLayoutY() + lastCell.getHeight() >= flow.getViewportLength()); + + // Now make sure that no extra cells were created. + if (flow.cells.size() > 3) { + IndexedCell secondLastCell = flow.cells.get(flow.cells.size() - 2); + IndexedCell secondCell = flow.cells.get(1); + assertFalse("There are more cells created before the start of " + + "the flow than necessary", + secondCell.getLayoutY() <= 0); + assertFalse("There are more cells created after the end of the " + + "flow than necessary", + secondLastCell.getLayoutY() + secondLastCell.getHeight() >= flow.getViewportLength()); + } + } else { + // First make sure that enough cells were created + assertTrue("There is a gap between the left of the viewport and the first cell", + firstCell.getLayoutX() <= 0); + assertTrue("There is a gap between the right of the last cell and the right of the viewport", + lastCell.getLayoutX() + lastCell.getWidth() >= flow.getViewportLength()); + + // Now make sure that no extra cells were created. + if (flow.cells.size() > 3) { + IndexedCell secondLastCell = flow.cells.get(flow.cells.size() - 2); + IndexedCell secondCell = flow.cells.get(1); + assertFalse("There are more cells created before the start of " + + "the flow than necessary", + secondCell.getLayoutX() <= 0); + assertFalse("There are more cells created after the end of the " + + "flow than necessary", + secondLastCell.getLayoutX() + secondLastCell.getWidth() >= flow.getViewportLength()); + } + } + } + + + /*************************************************************************** + * Tests for VirtualFlow * + * * + * These tests are broken out into several broad categories: * + * - general layout (position of scroll bars, viewport, etc) * + * - cell layout * + * - cell life cycle (creation, configuration, reuse, etc) * + * - position (stable view, adjusts when necessary, etc) * + * - pixel scrolling (cells are reused, position updated, etc) * + * * + * - Test that the preferred width of a vertical flow takes into account * + * the preferred width of the cells that are visible * + * - Test the same for horizontal when working with a horizontal flow * + * - Test that cells are laid out as expected in a vertical flow * + * - Test the same for a horizontal flow * + * - Test that the width of cells in a vertical flow adjusts based on the * + * width of the flow's content area * + * - Test the same for the height of cells in a horizontal flow * + * - Test that changing the number of cells (up and down) also adjusts * + * the position such that it is "stable", unless that is not possible * + * - Test that after changing the cell factory, things are rebuilt * + * - Test that after changing the cell config function, things are * + * reconfigured. * + * - Test that functions which add to the pile and so forth work as * + * expected. * + * - Test the layout of the scroll bars in various combinations, along * + * with the corner region and so forth. * + * * + **************************************************************************/ + + //////////////////////////////////////////////////////////////////////////// + // + // General Layout + // + //////////////////////////////////////////////////////////////////////////// + + /** + * In this test there are no cells. The VirtualFlow should be laid out such + * that the scroll bars and corner are not visible, and the clip view fills + * the entire width/height of the VirtualFlow. + */ + @Test public void testGeneralLayout_NoCells() { + flow.setCellCount(0); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * When we have a few cells, not enough to fill the viewport, then we need + * to make sure there is no virtual scroll bar. In this test case the cells + * are not wider than the viewport so no horizontal bar either. + */ + @Test public void testGeneralLayout_FewCells() { + flow.setCellCount(3); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertEquals(12, flow.cells.size()); // we stil have 12 cells (300px / 25px), even if only three filled cells exist + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); +// assertEquals(3, flow.cells.size()); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Tests the case of a few cells (so not requiring a vertical scroll bar), + * but the cells are wider than the viewport so a horizontal scroll bar is + * required. + */ + @Test public void testGeneralLayout_FewCellsButWide() { + flow.setCellCount(3); + flow.resize(50, flow.getHeight()); + pulse(); + assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); + assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + flow.resize(300, 50); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Tests that having a situation where the hbar in a vertical flow is + * necessary (due to wide cells) will end up hiding the hbar if the flow + * becomes wide enough that the hbar is no longer necessary. + */ + @Test public void testGeneralLayout_FewCellsButWide_ThenNarrow() { + flow.setCellCount(3); + flow.resize(50, flow.getHeight()); + pulse(); + flow.resize(300, flow.getHeight()); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + flow.resize(300, 50); + pulse(); + flow.resize(flow.getWidth(), 300); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Tests that when there are many cells then the vbar in a vertical flow + * is used. + *

+ * Note, this test uncovered a bug where the bottom of the last cell was + * exactly on the bottom edge of the flow and the vbar was not made visible. + * Be sure to test for this explicitly some time! + */ + @Test public void testGeneralLayout_ManyCells() { + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + pulse(); + assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); + assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Test that after having only a few cells, if I then have many cells, that + * the vbar is shown appropriately. + */ + @Test public void testGeneralLayout_FewCells_ThenMany() { + flow.setCellCount(3); + pulse(); + flow.setCellCount(100); + pulse(); + assertFalse("The hbar should have been invisible", flow.getHbar().isVisible()); + assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight(), 0.0); + assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + flow.setCellCount(3); + pulse(); + flow.setCellCount(100); + pulse(); + assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); + assertFalse("The vbar should have been invisible", flow.getVbar().isVisible()); + assertFalse("The corner should have been invisible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); + assertEquals(flow.getHbar().getLayoutY(), flow.getHeight() - flow.getHbar().getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Test the case where there are many cells and they are wider than the + * viewport. We should have the hbar, vbar, and corner region in this case. + */ + @Test public void testGeneralLayout_ManyCellsAndWide() { + flow.resize(50, flow.getHeight()); + pulse(); + assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); + assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); + assertTrue("The corner should have been visible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); + assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getVbar().getWidth(), flow.corner.getWidth(), 0.0); + assertEquals(flow.getHbar().getHeight(), flow.corner.getHeight(), 0.0); + assertEquals(flow.getHbar().getWidth(), flow.getWidth() - flow.corner.getWidth(), 0.0); + assertEquals(flow.getVbar().getHeight(), flow.getHeight() - flow.corner.getHeight(), 0.0); + assertEquals(flow.corner.getLayoutX(), flow.getWidth() - flow.corner.getWidth(), 0.0); + assertEquals(flow.corner.getLayoutY(), flow.getHeight() - flow.corner.getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + + flow.setVertical(false); + flow.resize(300, 50); + pulse(); + assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); + assertTrue("The vbar should have been visible", flow.getVbar().isVisible()); + assertTrue("The corner should have been visible", flow.corner.isVisible()); + assertEquals(flow.getWidth(), flow.clipView.getWidth() + flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getHeight(), flow.clipView.getHeight() + flow.getHbar().getHeight(), 0.0); + assertEquals(flow.getVbar().getLayoutX(), flow.getWidth() - flow.getVbar().getWidth(), 0.0); + assertEquals(flow.getVbar().getWidth(), flow.corner.getWidth(), 0.0); + assertEquals(flow.getHbar().getHeight(), flow.corner.getHeight(), 0.0); + assertEquals(flow.getHbar().getWidth(), flow.getWidth() - flow.corner.getWidth(), 0.0); + assertEquals(flow.getVbar().getHeight(), flow.getHeight() - flow.corner.getHeight(), 0.0); + assertEquals(flow.corner.getLayoutX(), flow.getWidth() - flow.corner.getWidth(), 0.0); + assertEquals(flow.corner.getLayoutY(), flow.getHeight() - flow.corner.getHeight(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Tests that when the vertical flag changes, it results in layout + */ + @Test public void testGeneralLayout_VerticalChangeResultsInNeedsLayout() { + assertFalse(flow.isNeedsLayout()); + flow.setVertical(false); + assertTrue(flow.isNeedsLayout()); + } + + /** + * Tests that the range of the non-virtual scroll bar is valid + */ + @Test public void testGeneralLayout_NonVirtualScrollBarRange() { + flow.resize(50, flow.getHeight()); + pulse(); + assertEquals(0, flow.getHbar().getMin(), 0.0); + assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getWidth(), flow.getHbar().getMax(), 0.0); + assertEquals((flow.clipView.getWidth()/flow.getMaxPrefBreadth()) * flow.getHbar().getMax(), flow.getHbar().getVisibleAmount(), 0.0); + flow.setPosition(.28f); + pulse(); + assertEquals(0, flow.getHbar().getMin(), 0.0); + assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getWidth(), flow.getHbar().getMax(), 0.0); + assertEquals((flow.clipView.getWidth()/flow.getMaxPrefBreadth()) * flow.getHbar().getMax(), flow.getHbar().getVisibleAmount(), 0.0); + + flow.setVertical(false); + flow.setPosition(0); + flow.resize(300, 50); + pulse(); + assertEquals(0, flow.getVbar().getMin(), 0.0); + assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getHeight(), flow.getVbar().getMax(), 0.0); + assertEquals((flow.clipView.getHeight()/flow.getMaxPrefBreadth()) * flow.getVbar().getMax(), flow.getVbar().getVisibleAmount(), 0.0); + flow.setPosition(.28); + pulse(); + assertEquals(0, flow.getVbar().getMin(), 0.0); + assertEquals(flow.getMaxPrefBreadth() - flow.clipView.getHeight(), flow.getVbar().getMax(), 0.0); + assertEquals((flow.clipView.getHeight()/flow.getMaxPrefBreadth()) * flow.getVbar().getMax(), flow.getVbar().getVisibleAmount(), 0.0); + } + + /** + * Tests that the maxPrefBreadth is computed correctly for the first page of cells. + * In our test case, the first page of cells have a uniform pref. + */ + @Test public void testGeneralLayout_maxPrefBreadth() { + assertEquals(100, flow.getMaxPrefBreadth(), 0.0); + } + + /** + * Tests that even after the first computation of max pref, that it is + * updated when we encounter a new cell (while scrolling for example) that + * has a larger pref. + */ + @Ignore + @Test public void testGeneralLayout_maxPrefBreadthUpdatedWhenEncounterLargerPref() { + flow.setPosition(.28); + pulse(); + assertEquals(200, flow.getMaxPrefBreadth(), 0.0); + } + + /** + * Tests that if we encounter cells or pages of cells with smaller prefs + * than the max pref that we will keep the max pref the same. + */ + @Ignore + @Test public void testGeneralLayout_maxPrefBreadthRemainsSameWhenEncounterSmallerPref() { + flow.setPosition(.28); + pulse(); + flow.setPosition(.8); + pulse(); + assertEquals(200, flow.getMaxPrefBreadth(), 0.0); + } + + /** + * Tests that changes to the vertical property will clear the maxPrefBreadth + */ + @Test public void testGeneralLayout_VerticalChangeClearsmaxPrefBreadth() { + flow.setVertical(false); + assertEquals(-1, flow.getMaxPrefBreadth(), 0.0); + } + + /** + * Tests that changes to the cell count will not affect maxPrefBreadth. + */ + @Ignore + @Test public void testGeneralLayout_maxPrefBreadthUnaffectedByCellCountChanges() { + flow.setCellCount(10); + pulse(); + assertEquals(100, flow.getMaxPrefBreadth(), 0.0); + flow.setCellCount(100); + pulse(); + flow.setPosition(.28); + pulse(); + assertEquals(200, flow.getMaxPrefBreadth(), 0.0); + flow.setCellCount(10); + pulse(); + assertEquals(200, flow.getMaxPrefBreadth(), 0.0); + } + + /** + * Tests that as we scroll, if the non-virtual scroll bar is visible, then + * as we update maxPrefBreadth it will not affect the non-virtual scroll bar's + * value unless the value is such that the scroll bar is scrolled + * all the way to the end, in which case it will remain scrolled to the + * end. + */ + @Test public void testGeneralLayout_maxPrefBreadthScrollBarValueInteraction() { + flow.resize(50, flow.getHeight()); + flow.getHbar().setValue(30); + pulse(); + flow.setPosition(.28); + pulse(); + assertEquals(30, flow.getHbar().getValue(), 0.0); + + // Reset the test and this time check what happens when we are scrolled + // to the very right + flow.setPosition(0); + flow.setVertical(false); + flow.setVertical(true); + pulse(); + assertEquals(100, flow.getMaxPrefBreadth(), 0.0); + flow.getHbar().setValue(flow.getHbar().getMax()); // scroll to the end + flow.setPosition(.28); + pulse(); + assertEquals(flow.getHbar().getMax(), flow.getHbar().getValue(), 0.0); + + flow.setVertical(false); + flow.setPosition(0); + flow.getHbar().setValue(0); + flow.resize(300, 50); + pulse(); + flow.getVbar().setValue(30); + pulse(); + flow.setPosition(.28); + pulse(); + assertEquals(30, flow.getVbar().getValue(), 0.0); + + // Reset the test and this time check what happens when we are scrolled + // to the very right + flow.setPosition(0); + flow.setVertical(true); + flow.setVertical(false); + pulse(); + assertEquals(100, flow.getMaxPrefBreadth(), 0.0); + flow.getVbar().setValue(flow.getVbar().getMax()); // scroll to the end + flow.setPosition(.28); + pulse(); + assertEquals(flow.getVbar().getMax(), flow.getVbar().getValue(), 0.0); + } + + @Test public void testGeneralLayout_ScrollToEndOfVirtual_BarStillVisible() { + assertTrue("The vbar was expected to be visible", flow.getVbar().isVisible()); + flow.setPosition(1); + pulse(); + assertTrue("The vbar was expected to be visible", flow.getVbar().isVisible()); + + flow.setPosition(0); + flow.setVertical(false); + pulse(); + assertTrue("The hbar was expected to be visible", flow.getHbar().isVisible()); + flow.setPosition(1); + pulse(); + assertTrue("The hbar was expected to be visible", flow.getHbar().isVisible()); + } + + // Need to test all the resize operations and make sure the position of + // nodes is as expected, that they don't get shifted etc. + + // Test: Scroll to the bottom, expand size out, then make smaller. The + // thumb/scroll is not consistent right now. + + // TODO figure out and deal with what happens when orientation changes + // to the hbar.value and vbar.value. Do they just switch? Probably not? + // Probably reset the non-virtual direction and swap the virtual one over. + // So if vbar was .5 and hbar was 30, when we set vertical = false, then + // we change the hbar to .5 and the vbar to 0. However this has to be done + // at the same time that the "virtual" property of the scroll bars is + // changed + + //////////////////////////////////////////////////////////////////////////// + // + // Cell Layout + // + //////////////////////////////////////////////////////////////////////////// + + /** + * Test to make sure that we are virtual -- that all cells are not being + * created. + */ + @Test public void testCellLayout_NotAllCellsAreCreated() { + // due to the initial size of the VirtualFlow and the number of cells + // and their heights, we should have more cells than we have space to + // fit them and so only enough cells should be created to meet our + // needs and not any more than that + assertTrue("All of the cells were created", flow.cells.size() < flow.getCellCount()); + assertMinimalNumberOfCellsAreUsed(flow); + } + + /** + * Tests the size and position of all the cells to make sure they were + * laid out properly. + */ + @Test public void testCellLayout_CellSizes_AfterLayout() { + double offset = 0.0; + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(25, cell.getHeight(), 0.0); + assertEquals(offset, cell.getLayoutY(), 0.0); + offset += cell.getHeight(); + } + + offset = 0.0; + flow.setVertical(false); + pulse(); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(25, cell.getWidth(), 0.0); + assertEquals(offset, cell.getLayoutX(), 0.0); + offset += cell.getWidth(); + } + } + + /** + * Test the widths of the cells when the viewport is wider than the + * max pref width/height. They should be uniform, and should be the + * width of the viewport. + */ + @Test public void testCellLayout_ViewportWiderThanmaxPrefBreadth() { + // Note that the pref width of everything is 100, but the actual + // available width is much larger (300 - hbar.width or + // 300 - vbar.height) and so the non-virtual dimension of the cell + // should be larger than the max pref + double expected = flow.clipView.getWidth(); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(expected, cell.getWidth(), 0.0); + } + + flow.setVertical(false); + pulse(); + expected = flow.clipView.getHeight(); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(expected, cell.getHeight(), 0.0); + } + } + + /** + * Test the widths of the cells when the viewport is shorter than the + * max pref width/height. They should be uniform, and should be the max + * pref. + */ + @Test public void testCellLayout_ViewportShorterThanmaxPrefBreadth() { + flow.resize(50, flow.getHeight()); + pulse(); + assertEquals(100, flow.getMaxPrefBreadth(), 0.0); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(flow.getMaxPrefBreadth(), cell.getWidth(), 0.0); + } + + flow.setVertical(false); + flow.resize(flow.getWidth(), 50); + pulse(); + assertEquals(100, flow.getMaxPrefBreadth(), 0.0); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(flow.getMaxPrefBreadth(), cell.getHeight(), 0.0); + } + } + + /** + * Test that when we scroll and encounter a cell which has a larger pref + * than we have previously encountered (happens in this test when we visit + * the cell for item #29), then the max pref is updated and the cells are + * all resized to match. + */ + @Ignore + @Test public void testCellLayout_ScrollingFindsCellWithLargemaxPrefBreadth() { + flow.resize(50, flow.getHeight()); + flow.setPosition(.28); // happens to position such that #29 is visible + pulse(); + assertEquals(200, flow.getMaxPrefBreadth(), 0.0); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(flow.getMaxPrefBreadth(), cell.getWidth(), 0.0); + } + + flow.setVertical(false); + flow.resize(flow.getWidth(), 50); + // NOTE Run this test without the pulse and it fails! + pulse(); + flow.setPosition(.28); + pulse(); + assertEquals(200, flow.getMaxPrefBreadth(), 0.0); + for (int i = 0; i < flow.cells.size(); i++) { + IndexedCell cell = flow.cells.get(i); + assertEquals(flow.getMaxPrefBreadth(), cell.getHeight(), 0.0); + } + } + + /** + * Checks that the initial set of cells (the first page of cells) are + * indexed starting with cell #0 and working up from there. + */ + @Test public void testCellLayout_CellIndexes_FirstPage() { + for (int i = 0; i < flow.cells.size(); i++) { + assertEquals(i, flow.cells.get(i).getIndex()); + } + } + + /** + * The bug here is that if layout() is called on the flow numerous times, + * but nothing substantially has changed, we should reuse the cells in the + * same order they were before. We had a bug where when you clicked on the + * ListView, the click wouldn't register. This was because by clicking we + * were giving focus to the ListView, which caused a layout(), and then the + * cells location was shuffled. It didn't look like it to the user, but that + * is what happened. As a result, when the mouse release took place, the + * event was delivered to a different cell than expected and misbehavior + * took place. + */ + @Test public void testCellLayout_LayoutWithoutChangingThingsUsesCellsInSameOrderAsBefore() { + List cells = new LinkedList(); + for (int i = 0; i < flow.cells.size(); i++) { + cells.add(flow.cells.get(i)); + } + assertMatch(cells, flow.cells); // sanity check + flow.requestLayout(); + pulse(); + assertMatch(cells, flow.cells); + flow.setPosition(1); + pulse(); + cells.clear(); + for (int i = 0; i < flow.cells.size(); i++) { + cells.add(flow.cells.get(i)); + } + flow.requestLayout(); + pulse(); + assertMatch(cells, flow.cells); + } + + + @Test + public void testCellLayout_BiasedCellAndLengthBar() { + flow.setCellFactory(param -> new CellStub(flow) { + @Override + protected double computeMinWidth(double height) { + return 0; + } + + @Override + protected double computeMaxWidth(double height) { + return Double.MAX_VALUE; + } + + @Override + protected double computePrefWidth(double height) { + return 200; + } + + @Override + protected double computeMinHeight(double width) { + return 0; + } + + @Override + protected double computeMaxHeight(double width) { + return Double.MAX_VALUE; + } + + @Override + protected double computePrefHeight(double width) { + return getIndex() == 0 ? 100 - 5 * (Math.floorDiv((int) width - 200, 10)) : 100; + } + }); + flow.setCellCount(3); + flow.recreateCells(); // This help to override layoutChildren() in flow.setCellCount() + flow.getVbar().setPrefWidth(20); // Since Skins are not initialized, we set the pref width explicitly + flow.requestLayout(); + pulse(); + assertEquals(300, flow.cells.get(0).getWidth(), 1e-100); + assertEquals(50, flow.cells.get(0).getHeight(), 1e-100); + + flow.resize(200, 300); + + flow.requestLayout(); + pulse(); + assertEquals(200, flow.cells.get(0).getWidth(), 1e-100); + assertEquals(100, flow.cells.get(0).getHeight(), 1e-100); + + } + + //////////////////////////////////////////////////////////////////////////// + // + // Cell Life Cycle + // + //////////////////////////////////////////////////////////////////////////// + + @Test public void testCellLifeCycle_CellsAreCreatedOnLayout() { + // when the flow was first created in setUp we do a layout() + assertTrue("The cells didn't get created", flow.cells.size() > 0); + } + +// /** +// * During layout the order and contents of cells will change. We need +// * to make sure that CSS for cells is applied at this time. To test this, +// * I just set the position and perform a new pulse. Since layout happens +// * after the CSS updates are applied, if the test fails, then there will +// * be cells left in a state where they need their CSS applied. +// */ +// @Test public void testCellLifeCycle_CSSUpdatesHappenDuringLayout() { +// flow.setPosition(.35); +// pulse(); +// for (int i = 0; i < flow.cells.size(); i++) { +// IndexedCell cell = flow.cells.get(i); +// assertEquals(CssFlags.CLEAN, cell.impl_getCSSFlags()); +// } +// } + + //////////////////////////////////////////////////////////////////////////// + // + // Position + // + //////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////// + // + // Pixel Scrolling + // + //////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////// + // + // Cell Count Changes + // + //////////////////////////////////////////////////////////////////////////// + + // want to test that the view remains stable when the cell count changes + + // LIST VIEW: test that using a bunch of nodes as items to a ListView works + // LIST VIEW: test that inserting an item moves the selected index to keep in sync + // test that dynamically changing all of the contents causes them to refresh + + // test that when the number of cells change, that things are laid out +// @Test public void testCellCountChanges_FirstRowIsRemoved() { +// +// } +// +// @Test public void testCellCountChanges_MiddleRowIsRemoved() { +// +// } +// +// @Test public void testCellCountChanges_LastRowIsRemoved() { +// +// } +// +//// @Test public void testCellCountChanges_SelectedRowRemoved() { +//// +//// } +//// +//// @Test public void testCellCountChanges_NonSelectedRowRemoved() { +//// +//// } +// +// @Test public void testCellCountChanges_FirstRowIsAdded() { +// +// } +// +// @Test public void testCellCountChanges_MiddleRowIsAdded() { +// +// } +// +// @Test public void testCellCountChanges_LastRowIsAdded() { +// +// } +// +//// @Test public void testCellCountChanges_RowIsAddedBeforeSelectedRow() { +//// +//// } +//// +//// @Test public void testCellCountChanges_RowIsAddedAfterSelectedRow() { +//// +//// } + + //////////////////////////////////////////////////////////////////////////// + // + // VirtualFlow State Changes + // + //////////////////////////////////////////////////////////////////////////// + + /** + * Tests that when the createCell method changes, it results in layout + */ + @Test public void testCreateCellFunctionChangesResultInNeedsLayoutAndNoCellsAndNoAccumCell() { + assertFalse(flow.isNeedsLayout()); + flow.getCellLength(49); // forces accum cell to be created + assertNotNull("Accum cell was null", flow.accumCell); + flow.setCellFactory(p -> new CellStub(flow)); + assertTrue(flow.isNeedsLayout()); + assertNull("accumCell didn't get cleared", flow.accumCell); + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Tests on specific functions + // + //////////////////////////////////////////////////////////////////////////// + + @Test public void test_getCellLength() { + assertEquals(100, flow.getCellCount()); + for (int i = 0; i < 50; i++) { + if (i != 29) assertEquals(25, flow.getCellLength(i), 0.0); + } + flow.setVertical(false); + flow.requestLayout(); + pulse(); + assertEquals(100, flow.getCellCount()); + for (int i = 0; i < 50; i++) { + if (i != 29) assertEquals("Bad index: " + i, 25, flow.getCellLength(i), 0.0); + } + } + + /* + ** if we scroll the flow by a number of LINES, + ** without having done anything to select a cell + ** the flow should scroll. + */ + @Test public void testInitialScrollEventActuallyScrolls() { + /* + ** re-initialize this, as it must be the first + ** interaction with the flow + */ + flow = new VirtualFlow(); + flow.setVertical(true); + flow.setCellFactory(p -> new CellStub(flow) { + @Override + protected double computeMinWidth(double height) { + return computePrefWidth(height); + } + + @Override + protected double computeMaxWidth(double height) { + return computePrefWidth(height); + } + + @Override + protected double computePrefWidth(double height) { + return flow.isVertical() ? (c.getIndex() == 29 ? 200 : 100) : (c.getIndex() == 29 ? 100 : 25); + } + + @Override + protected double computeMinHeight(double width) { + return computePrefHeight(width); + } + + @Override + protected double computeMaxHeight(double width) { + return computePrefHeight(width); + } + + @Override + protected double computePrefHeight(double width) { + return flow.isVertical() ? (c.getIndex() == 29 ? 100 : 25) : (c.getIndex() == 29 ? 200 : 100); + } + }); + + flow.setCellCount(100); + flow.resize(300, 300); + pulse(); + + double originalValue = flow.getPosition(); + + Event.fireEvent(flow, + new ScrollEvent(ScrollEvent.SCROLL, + 0.0, -10.0, 0.0, -10.0, + false, false, false, false, true, false, + 0, 0, + 0, 0, + ScrollEvent.HorizontalTextScrollUnits.NONE, 0.0, + ScrollEvent.VerticalTextScrollUnits.LINES, -1.0, + 0, null)); + + assertTrue(originalValue != flow.getPosition()); + } + + @Test + public void test_RT_36507() { + flow = new VirtualFlow(); + flow.setVertical(true); + // Worst case scenario is that the cells have height = 0. + // The code should prevent creating more than 100 of these zero height cells + // (since viewportLength is 100). + // An "INFO: index exceeds maxCellCount" message should print out. + flow.setCellFactory(p -> new CellStub(flow) { + @Override + protected double computeMaxHeight(double width) { + return 0; + } + + @Override + protected double computePrefHeight(double width) { + return 0; + } + + @Override + protected double computeMinHeight(double width) { + return 0; + } + + }); + flow.setCellCount(10); + flow.setViewportLength(100); + flow.addLeadingCells(1, 0); + flow.sheetChildren.addListener((InvalidationListener) (o) -> { + int count = ((List) o).size(); + assertTrue(Integer.toString(count), count <= 100); + }); + flow.addTrailingCells(true); + } + + private int rt36556_instanceCount; + @Test + public void test_rt36556() { + rt36556_instanceCount = 0; + flow = new VirtualFlow(); + flow.setVertical(true); + flow.setCellFactory(p -> { + rt36556_instanceCount++; + return new CellStub(flow); + }); + flow.setCellCount(100); + flow.resize(300, 300); + pulse(); + final int cellCountAtStart = rt36556_instanceCount; + flow.scrollPixels(10000); + pulse(); + assertEquals(cellCountAtStart, rt36556_instanceCount); + assertNull(flow.getVisibleCell(0)); + assertMinimalNumberOfCellsAreUsed(flow); + } + + @Test + public void test_rt36556_scrollto() { + rt36556_instanceCount = 0; + flow = new VirtualFlow(); + flow.setVertical(true); + flow.setCellFactory(p -> { + rt36556_instanceCount++; + return new CellStub(flow); + }); + flow.setCellCount(100); + flow.resize(300, 300); + pulse(); + final int cellCountAtStart = rt36556_instanceCount; + flow.scrollTo(80); + pulse(); + assertEquals(cellCountAtStart, rt36556_instanceCount); + assertNull(flow.getVisibleCell(0)); + assertMinimalNumberOfCellsAreUsed(flow); + } + + @Test + public void test_RT39035() { + flow.scrollPixels(250); + pulse(); + flow.scrollPixels(500); + pulse(); + assertTrue(flow.getPosition() < 1.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + @Test + public void test_RT37421() { + flow.setPosition(0.98); + pulse(); + flow.scrollPixels(100); + pulse(); + assertEquals(1.0, flow.getPosition(), 0.0); + assertMinimalNumberOfCellsAreUsed(flow); + } + + @Test + public void test_RT39568() { + flow.getHbar().setPrefHeight(16); + flow.resize(50, flow.getHeight()); + flow.setPosition(1); + pulse(); + assertTrue("The hbar should have been visible", flow.getHbar().isVisible()); + assertMinimalNumberOfCellsAreUsed(flow); + assertEquals(flow.getViewportLength()-25.0, flow.cells.getLast().getLayoutY(), 0.0); + } +} + +class CellStub extends IndexedCell { + String s; + VirtualFlow flow; + + public CellStub(VirtualFlow flow) { init(flow); } + public CellStub(VirtualFlow flow, String s) { init(flow); this.s = s; } + + private void init(VirtualFlow flow) { + this.flow = flow; + setSkin(new SkinStub(this)); + updateItem(this, false); + } + + @Override + public void updateIndex(int i) { + super.updateIndex(i); + + s = "Item " + getIndex(); +// updateItem(getIndex(), getIndex() >= flow.getCellCount()); + } +}