From 9c1908d0a5fdba09167414e837cc08a2f746584b Mon Sep 17 00:00:00 2001 From: jotatu Date: Fri, 7 Aug 2015 14:45:50 +0300 Subject: [PATCH] Optimize layout performance of Table (#17947) Adds functions for skipping child component layout measuring. Removes unnecessary code from VScrollTable. 1. case: no components - render time without the fix: ~105ms - render time with fix: ~105ms 2. case: 2 button and 2 textfield cols - render time without the fix: ~279ms - render time with fix: ~240ms (~17% faster) 3. case: 3 button and 3 textfield cols - render time without the fix: ~350ms - render time with fix: ~281ms (~20% faster) Change-Id: I6025f8ee2fd438d228ff3b65f43535961cf12c0b --- .../HasChildMeasurementHintConnector.java | 72 +++++++ .../src/com/vaadin/client/LayoutManager.java | 3 +- client/src/com/vaadin/client/Util.java | 81 ++++++++ .../com/vaadin/client/ui/VScrollTable.java | 22 ++- .../ui/layout/LayoutDependencyTree.java | 3 +- .../client/ui/table/TableConnector.java | 19 +- .../vaadin/ui/HasChildMeasurementHint.java | 69 +++++++ server/src/com/vaadin/ui/Table.java | 27 ++- .../table/TableChildMeasurementHint.java | 184 ++++++++++++++++++ .../table/TableChildMeasurementHintTest.java | 45 +++++ 10 files changed, 514 insertions(+), 11 deletions(-) create mode 100644 client/src/com/vaadin/client/HasChildMeasurementHintConnector.java create mode 100644 server/src/com/vaadin/ui/HasChildMeasurementHint.java create mode 100644 uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHint.java create mode 100644 uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHintTest.java diff --git a/client/src/com/vaadin/client/HasChildMeasurementHintConnector.java b/client/src/com/vaadin/client/HasChildMeasurementHintConnector.java new file mode 100644 index 0000000000..5ca6317b9b --- /dev/null +++ b/client/src/com/vaadin/client/HasChildMeasurementHintConnector.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client; + +import com.vaadin.client.ui.ManagedLayout; +import com.vaadin.client.ui.layout.ElementResizeListener; + +/** + * Connector with layout measuring hint. Used to improve granularity of control + * over child component measurements. + * + * @since + * @author Vaadin Ltd + */ +public interface HasChildMeasurementHintConnector extends + HasComponentsConnector { + + /** + * Specifies how you would like child components measurements to be handled. + * Since this is a hint, it can be ignored when deemed necessary. + */ + public enum ChildMeasurementHint { + + /** + * Always measure all child components (default). + */ + MEASURE_ALWAYS, + + /** + * Measure child component only if child component is a {@link Layout} + * or implements either {@link ManagedLayout} or + * {@link ElementResizeListener}. + */ + MEASURE_IF_NEEDED, + + /** + * Never measure child components. This can improve rendering speed of + * components with lots of children (e.g. Table), but can cause some + * child components to be rendered incorrectly (e.g. ComboBox). + */ + MEASURE_NEVER + } + + /** + * Sets the child measurement hint for this component. + * + * @param hint + * the value to set + */ + void setChildMeasurementHint(ChildMeasurementHint hint); + + /** + * Returns the current child measurement hint value. + * + * @return a ChildLayoutMeasureMode value + */ + ChildMeasurementHint getChildMeasurementHint(); + +} diff --git a/client/src/com/vaadin/client/LayoutManager.java b/client/src/com/vaadin/client/LayoutManager.java index d39e604aaf..ed83e195d7 100644 --- a/client/src/com/vaadin/client/LayoutManager.java +++ b/client/src/com/vaadin/client/LayoutManager.java @@ -712,7 +712,8 @@ public class LayoutManager { .createArray().cast(); for (int i = 0; i < size; i++) { ComponentConnector candidate = allConnectors.get(i); - if (needsMeasure(candidate.getWidget().getElement())) { + if (!Util.shouldSkipMeasurementOfConnector(candidate) + && needsMeasure(candidate.getWidget().getElement())) { connectors.add(candidate); } } diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java index ab7d344877..8b5caa6051 100644 --- a/client/src/com/vaadin/client/Util.java +++ b/client/src/com/vaadin/client/Util.java @@ -33,8 +33,12 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.HasChildMeasurementHintConnector.ChildMeasurementHint; import com.vaadin.client.RenderInformation.FloatSize; +import com.vaadin.client.ui.AbstractLayoutConnector; +import com.vaadin.client.ui.ManagedLayout; import com.vaadin.client.ui.VOverlay; +import com.vaadin.client.ui.layout.ElementResizeListener; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.communication.MethodInvocation; @@ -1186,6 +1190,83 @@ public class Util { return +(Math.round(num + "e+" + exp) + "e-" + exp); }-*/; + /** + * Test if we can can skip measuring this connector. We can skip the + * measurement if its parent is a ChildMeasureHintConnector and has an + * appropriate mode set. + * + * For internal use only. May be removed or replaced in the future. + */ + public static boolean shouldSkipMeasurementOfConnector( + ComponentConnector candidate, ComponentConnector parent) { + Profiler.enter("skipMeasureDueLayoutHint"); + boolean skip = false; + + if (parent instanceof HasChildMeasurementHintConnector) { + ChildMeasurementHint measureMode = ((HasChildMeasurementHintConnector) parent) + .getChildMeasurementHint(); + + if (measureMode == ChildMeasurementHint.MEASURE_NEVER) { + skip = true; + } else if (measureMode == ChildMeasurementHint.MEASURE_IF_NEEDED) { + skip = canWeSkipChildMeasurement(candidate); + } + } + Profiler.leave("skipMeasureDueLayoutHint"); + return skip; + } + + /** + * Test if we can can skip measuring this connector. We can skip the + * measurement if its parent is a ChildMeasureHintConnector and has an + * appropriate mode set. + * + * This version of the method tries to recursively locate such a parent. + * + * For internal use only. May be removed or replaced in the future. + */ + public static boolean shouldSkipMeasurementOfConnector( + ComponentConnector candidate) { + Profiler.enter("skipMeasureDueLayoutHint"); + boolean skip = false; + + HasChildMeasurementHintConnector parent = getPossibleChildMeasurementHintParentConnector(candidate); + + if (parent != null) { + ChildMeasurementHint measureMode = parent.getChildMeasurementHint(); + + if (measureMode == ChildMeasurementHint.MEASURE_NEVER) { + skip = true; + } else if (measureMode == ChildMeasurementHint.MEASURE_IF_NEEDED) { + skip = canWeSkipChildMeasurement(candidate); + } + } + Profiler.leave("skipMeasureDueLayoutHint"); + return skip; + } + + /** For internal use only. May be removed or replaced in the future. */ + private static boolean canWeSkipChildMeasurement(ComponentConnector child) { + // common cases when child measuring is possibly needed + if (child instanceof ElementResizeListener + || child instanceof ManagedLayout + || child instanceof AbstractLayoutConnector) { + return false; + } + return true; + } + + /** For internal use only. May be removed or replaced in the future. */ + private static HasChildMeasurementHintConnector getPossibleChildMeasurementHintParentConnector( + ComponentConnector candidate) { + ServerConnector parent = candidate.getParent(); + if (parent != null + && parent instanceof HasChildMeasurementHintConnector) { + return (HasChildMeasurementHintConnector) parent; + } + return null; + } + private static Logger getLogger() { return Logger.getLogger(Util.class.getName()); } diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 6bb8f063a6..3b85356f86 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -84,6 +84,7 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorMap; import com.vaadin.client.DeferredWorker; import com.vaadin.client.Focusable; +import com.vaadin.client.HasChildMeasurementHintConnector.ChildMeasurementHint; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.StyleConstants; import com.vaadin.client.TooltipInfo; @@ -699,6 +700,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private int multiselectmode; + /** + * Hint for how to handle measurement of child components + */ + private ChildMeasurementHint childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; + /** For internal use only. May be removed or replaced in the future. */ public int tabIndex; @@ -1238,11 +1244,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, uidl.getIntAttribute("rows")); scrollBodyPanel.add(scrollBody); - // New body starts scrolled to the left, make sure the header and footer - // are also scrolled to the left - tHead.setHorizontalScrollPosition(0); - tFoot.setHorizontalScrollPosition(0); - initialContentReceived = true; sizeNeedsInit = true; scrollBody.restoreRowVisibility(); @@ -2620,8 +2621,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, @Override public void run() { - if (client.getMessageSender().hasActiveRequest() - || navKeyDown) { + if (client.getMessageSender().hasActiveRequest() || navKeyDown) { // if client connection is busy, don't bother loading it more VConsole.log("Postponed rowfetch"); schedule(250); @@ -8332,4 +8332,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private static Logger getLogger() { return Logger.getLogger(VScrollTable.class.getName()); } + + public ChildMeasurementHint getChildMeasurementHint() { + return childMeasurementHint; + } + + public void setChildMeasurementHint(ChildMeasurementHint hint) { + childMeasurementHint = hint; + } } diff --git a/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java b/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java index a4c5668948..27733bfbe3 100644 --- a/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java +++ b/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java @@ -248,7 +248,8 @@ public class LayoutDependencyTree { if (connector instanceof HasComponentsConnector) { HasComponentsConnector container = (HasComponentsConnector) connector; for (ComponentConnector child : container.getChildComponents()) { - if (isRelativeInDirection(child, direction)) { + if (!Util.shouldSkipMeasurementOfConnector(child, connector) + && isRelativeInDirection(child, direction)) { resized.push(child.getConnectorId()); } } diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index 0cce611191..7e7995b5e1 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -28,6 +28,7 @@ import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; +import com.vaadin.client.HasChildMeasurementHintConnector; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler; @@ -55,7 +56,7 @@ import com.vaadin.shared.ui.table.TableState; @Connect(com.vaadin.ui.Table.class) public class TableConnector extends AbstractFieldConnector implements HasComponentsConnector, ConnectorHierarchyChangeHandler, Paintable, - DirectionalManagedLayout, PostLayoutListener { + DirectionalManagedLayout, PostLayoutListener, HasChildMeasurementHintConnector { private List childComponents; @@ -199,6 +200,12 @@ public class TableConnector extends AbstractFieldConnector implements getWidget().updateDragMode(uidl); + // Update child measure hint + int childMeasureHint = uidl.hasAttribute("measurehint") ? uidl + .getIntAttribute("measurehint") : 0; + getWidget().setChildMeasurementHint( + ChildMeasurementHint.values()[childMeasureHint]); + getWidget().updateSelectionProperties(uidl, getState(), isReadOnly()); if (uidl.hasAttribute("alb")) { @@ -532,4 +539,14 @@ public class TableConnector extends AbstractFieldConnector implements ConnectorHierarchyChangeEvent.TYPE, handler); } + @Override + public void setChildMeasurementHint(ChildMeasurementHint hint) { + getWidget().setChildMeasurementHint(hint); + } + + @Override + public ChildMeasurementHint getChildMeasurementHint() { + return getWidget().getChildMeasurementHint(); + } + } diff --git a/server/src/com/vaadin/ui/HasChildMeasurementHint.java b/server/src/com/vaadin/ui/HasChildMeasurementHint.java new file mode 100644 index 0000000000..dadf7d0be6 --- /dev/null +++ b/server/src/com/vaadin/ui/HasChildMeasurementHint.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.ui; + +/** + * Component with layout measuring hint. Used to improve granularity of control + * over child component measurements. + * + * @since + * @author Vaadin Ltd + */ +public interface HasChildMeasurementHint extends HasComponents { + + /** + * Specifies how you would like child components measurements to be handled. + * Since this is a hint, it can be ignored when deemed necessary. + */ + public enum ChildMeasurementHint { + + /** + * Always measure all child components (default). + */ + MEASURE_ALWAYS, + + /** + * Measure child component only if child component is a Layout or + * implements either ManagedLayout or ElementResizeListener. + */ + MEASURE_IF_NEEDED, + + /** + * Never measure child components. This can improve rendering speed of + * components with lots of children (e.g. Table), but can cause some + * child components to be rendered incorrectly (e.g. ComboBox). + */ + MEASURE_NEVER + + } + + /** + * Sets desired child size measurement hint. + * + * @param hint + * desired hint. A value of null will reset value back to the + * default (MEASURE_ALWAYS) + */ + void setChildMeasurementHint(ChildMeasurementHint hint); + + /** + * Returns the current child size measurement hint. + * + * @return a child measurement hint value + */ + ChildMeasurementHint getChildMeasurementHint(); + +} diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index 69874d9947..3edfe7845f 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -100,7 +100,7 @@ import com.vaadin.util.ReflectTools; @SuppressWarnings({ "deprecation" }) public class Table extends AbstractSelect implements Action.Container, Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource, - DropTarget, HasComponents { + DropTarget, HasComponents, HasChildMeasurementHint { private transient Logger logger = null; @@ -357,6 +357,11 @@ public class Table extends AbstractSelect implements Action.Container, private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() { }; + /** + * How layout manager should behave when measuring Table's child components + */ + private ChildMeasurementHint childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; + /* Private table extensions to Select */ /** @@ -3542,6 +3547,7 @@ public class Table extends AbstractSelect implements Action.Container, paintTabIndex(target); paintDragMode(target); paintSelectMode(target); + paintTableChildLayoutMeasureMode(target); if (cacheRate != CACHE_RATE_DEFAULT) { target.addAttribute("cr", cacheRate); @@ -3882,6 +3888,11 @@ public class Table extends AbstractSelect implements Action.Container, } } + private void paintTableChildLayoutMeasureMode(PaintTarget target) + throws PaintException { + target.addAttribute("measurehint", getChildMeasurementHint().ordinal()); + } + /** * Checks whether row headers are visible. * @@ -6467,4 +6478,18 @@ public class Table extends AbstractSelect implements Action.Container, } return logger; } + + @Override + public void setChildMeasurementHint(ChildMeasurementHint hint) { + if (hint == null) { + childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; + } else { + childMeasurementHint = hint; + } + } + + @Override + public ChildMeasurementHint getChildMeasurementHint() { + return childMeasurementHint; + } } diff --git a/uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHint.java b/uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHint.java new file mode 100644 index 0000000000..dd5d162e17 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHint.java @@ -0,0 +1,184 @@ +package com.vaadin.tests.components.table; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.AbstractOrderedLayout; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.DateField; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HasChildMeasurementHint.ChildMeasurementHint; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Table; +import com.vaadin.ui.TextField; + +public class TableChildMeasurementHint extends AbstractTestUI { + + private HorizontalLayout buttonLayout = new HorizontalLayout(); + private HorizontalLayout layout; + private Table table1, table2, table3; + + @Override + public void setup(VaadinRequest request) { + initMain(); + } + + protected void initMain() { + ((AbstractOrderedLayout) getContent()).setMargin(false); + layout = new HorizontalLayout(); + layout.setSpacing(true); + layout.setMargin(false); + layout.setSizeFull(); + addComponent(buttonLayout); + addComponent(layout); + + table1 = createTable(); + table1.setSizeFull(); + table1.setChildMeasurementHint(ChildMeasurementHint.MEASURE_ALWAYS); + + table2 = createTable(); + table2.setSizeFull(); + table2.setChildMeasurementHint(ChildMeasurementHint.MEASURE_IF_NEEDED); + + table3 = createTable(); + table3.setSizeFull(); + table3.setChildMeasurementHint(ChildMeasurementHint.MEASURE_NEVER); + + buttonLayout.addComponent(new Button("Show table1", + new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + layout.addComponent(table1); + table1.focus(); + } + })); + buttonLayout.addComponent(new Button("Show table2", + new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + layout.removeComponent(table1); + layout.addComponent(table2); + table2.focus(); + } + })); + buttonLayout.addComponent(new Button("Show table3", + new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + layout.removeComponent(table2); + layout.addComponent(table3); + table3.focus(); + } + })); + + } + + protected Table createTable() { + Table table = new Table(); + table.setSelectable(true); + table.setPageLength(39); + + for (int i = 0; i < 5; i++) { + table.addContainerProperty("First_Name" + i, String.class, null); + table.addContainerProperty("Last Name" + i, String.class, null); + table.addContainerProperty("Year" + i, Integer.class, null); + } + + /* Add a few items in the table. */ + int j = 0; + for (int i = 0; i < 2; i++) { + table.addItem( + makeRow(new Object[] { "Nicolaus" + i, "Copernicus", 1473 }, + 5), j++); + table.addItem( + makeRow(new Object[] { "Tycho" + i, "Brahe", 1546 }, 5), + j++); + table.addItem( + makeRow(new Object[] { "Giordano" + i, "Bruno", 1548 }, 5), + j++); + table.addItem( + makeRow(new Object[] { "Galileo" + i, "Galilei", 1564 }, 5), + j++); + table.addItem( + makeRow(new Object[] { "Johannes" + i, "Kepler", 1571 }, 5), + j++); + table.addItem( + makeRow(new Object[] { "Isaac" + i, "Newton", 1643 }, 5), + j++); + } + + table.addGeneratedColumn("First_Name" + 0, new Table.ColumnGenerator() { + @Override + public Object generateCell(Table components, Object o, Object o2) { + ComboBox b = new ComboBox("ComboBox"); + b.setWidthUndefined(); + return b; + } + }); + + table.addGeneratedColumn("First_Name" + 1, new Table.ColumnGenerator() { + @Override + public Object generateCell(Table components, Object o, Object o2) { + GridLayout b = new GridLayout(); + b.addComponents(new Label("l1"), new Button("b"), new Label( + "l2")); + b.setWidthUndefined(); + return b; + } + }); + + table.addGeneratedColumn("First_Name" + 2, new Table.ColumnGenerator() { + @Override + public Object generateCell(Table components, Object o, Object o2) { + Button b = new Button("Button"); + b.setWidthUndefined(); + return b; + } + }); + + table.addGeneratedColumn("First_Name" + 3, new Table.ColumnGenerator() { + @Override + public Object generateCell(Table components, Object o, Object o2) { + TextField b = new TextField("Textfield"); + b.setWidthUndefined(); + return b; + } + }); + + table.addGeneratedColumn("First_Name" + 4, new Table.ColumnGenerator() { + @Override + public Object generateCell(Table components, Object o, Object o2) { + DateField b = new DateField("DateField"); + b.setWidthUndefined(); + return b; + } + }); + + table.addGeneratedColumn("First_Name" + 5, new Table.ColumnGenerator() { + @Override + public Object generateCell(Table components, Object o, Object o2) { + Label b = new Label("Label"); + b.setWidthUndefined(); + return b; + } + }); + + return table; + } + + protected Object[] makeRow(Object[] data, int c) { + Object[] row = new Object[c * data.length]; + for (int j = 0; j < c; j++) { + int x = 0; + for (Object value : data) { + row[j * data.length + x] = value; + x++; + } + } + + return row; + } +} \ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHintTest.java b/uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHintTest.java new file mode 100644 index 0000000000..8202b06362 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/table/TableChildMeasurementHintTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.table; + +import java.io.IOException; + +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TableChildMeasurementHintTest extends MultiBrowserTest { + + @Test + public void testCacheSize() throws IOException { + + openTestURL(); + + $(ButtonElement.class).first().click(); + + compareScreen("initial"); + + $(ButtonElement.class).get(1).click(); + + compareScreen("initial"); + + $(ButtonElement.class).get(2).click(); + + compareScreen("initial"); + } + +} -- 2.39.5