Przeglądaj źródła

Restore and disable memory tests under Java 10

reviewable/pr10973/r2
elmot 6 lat temu
rodzic
commit
fa9643a789

+ 27
- 1
uitest/pom.xml Wyświetl plik

@@ -477,7 +477,33 @@
</plugins>
</build>
</profile>

<profile>
<!--TODO make those tests compatible with Java 10-->
<id>java10</id>
<activation>
<jdk>10</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<excludes>
<exclude>com/vaadin/tests/performance/AbstractBeansMemoryTest.java</exclude>
<exclude>com/vaadin/tests/performance/CompatibilityGridMemory.java</exclude>
<exclude>com/vaadin/tests/performance/GridMemory.java</exclude>
<exclude>com/vaadin/tests/performance/ThreadMemoryLeaksTest.java</exclude>
<exclude>com/vaadin/tests/performance/TreeGridMemory.java</exclude>
<exclude>com/vaadin/tests/performance/TreeTableMemory.java</exclude>
</excludes>
<testExcludes>
<exclude>com/vaadin/tests/performance/MemoryIT.java</exclude>
</testExcludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>

+ 201
- 0
uitest/src/main/java/com/vaadin/tests/performance/AbstractBeansMemoryTest.java Wyświetl plik

@@ -0,0 +1,201 @@
package com.vaadin.tests.performance;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Country;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.tests.data.bean.Sex;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.HasComponents;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;

/**
* @author Vaadin Ltd
*
*/
public abstract class AbstractBeansMemoryTest<T extends AbstractComponent>
extends UI {

private int dataSize;
private boolean isInMemory;
private boolean isDataOnly;

private Label logLabel;
private Label memoryLabel;

@Override
protected void init(VaadinRequest request) {
String items = request.getParameter("items");
int itemsCount = 1;
if (items != null) {
itemsCount = Integer.parseInt(items);
}

VerticalLayout layout = new VerticalLayout();
setContent(layout);
layout.addComponent(new Label(getClass().getSimpleName()));

memoryLabel = new Label();
memoryLabel.setId("memory");
layout.addComponent(memoryLabel);

logLabel = new Label();
logLabel.setId("log");
layout.addComponent(logLabel);

T component = createComponent();
setData(null, itemsCount, component, true);
layout.addComponent(createMenu(component));

layout.addComponent(component);

Button close = new Button("Close UI", event -> close());
close.setId("close");
layout.addComponent(close);
}

protected abstract T createComponent();

private Random random = new Random();

protected List<Person> createBeans(int size) {
return IntStream.range(0, size).mapToObj(this::createPerson)
.collect(Collectors.toList());
}

protected Person createPerson(int index) {
random.setSeed(index);
Person person = new Person();
person.setFirstName("First Name " + random.nextInt());
person.setLastName("Last Name " + random.nextInt());
person.setAge(random.nextInt());
person.setBirthDate(new Date(random.nextLong()));
person.setDeceased(random.nextBoolean());
person.setEmail(random.nextInt() + "user@example.com");
person.setRent(new BigDecimal(random.nextLong()));
person.setSalary(random.nextInt());
person.setSalaryDouble(random.nextDouble());
person.setSex(Sex.values()[random.nextInt(Sex.values().length)]);

Address address = new Address();
person.setAddress(address);
address.setCity("city " + random.nextInt());
address.setPostalCode(random.nextInt());
address.setStreetAddress("street address " + random.nextInt());
address.setCountry(
Country.values()[random.nextInt(Country.values().length)]);
return person;
}

protected abstract void setInMemoryContainer(T component,
List<Person> data);

protected abstract void setBackendContainer(T component, List<Person> data);

@Override
public VerticalLayout getContent() {
return (VerticalLayout) super.getContent();
}

@SuppressWarnings("restriction")
private void setData(MenuItem item, int size, T component,
boolean memoryContainer) {
if (item != null) {
MenuItem parent = item.getParent();
parent.getChildren().stream().filter(itm -> !itm.equals(item))
.forEach(itm -> itm.setChecked(false));
logLabel.setValue(item.getText());
}
dataSize = size;
isInMemory = memoryContainer;
List<Person> persons = createBeans(size);
if (isDataOnly) {
component.setData(persons);
persons = Collections.emptyList();
} else {
component.setData(null);
}
if (isInMemory) {
setInMemoryContainer(component, persons);
} else {
setBackendContainer(component, persons);
}

HasComponents container = component.getParent();
setParent(component, null);
memoryLabel.setValue(
String.valueOf(ObjectSizeCalculator.getObjectSize(component)));

setParent(component, container);
}

private void setParent(Component component, Component parent) {
try {
Field field = AbstractComponent.class.getDeclaredField("parent");
field.setAccessible(true);
field.set(component, parent);
} catch (NoSuchFieldException | SecurityException
| IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}

private Component createMenu(T component) {
MenuBar menu = new MenuBar();
createContainerSizeMenu(menu.addItem("Size", null), component);
createContainerMenu(menu.addItem("Data provider", null), component);
menu.addItem("Create only data",
item -> toggleDataOnly(item, component)).setCheckable(true);

return menu;
}

private void toggleDataOnly(MenuItem item, T component) {
isDataOnly = item.isChecked();
setData(null, dataSize, component, isInMemory);
}

private void createContainerMenu(MenuItem menu, T component) {
MenuItem menuItem = menu.addItem("Use in-memory container",
item -> setData(item, dataSize, component, true));
menuItem.setCheckable(true);
menuItem.setChecked(true);
menuItem = menu.addItem("Use backend container",
item -> setData(item, dataSize, component, false));
menuItem.setCheckable(true);
}

private void createContainerSizeMenu(MenuItem menu, T component) {
List<MenuItem> items = IntStream.of(1, 10000, 100000, 500000, 1000000)
.mapToObj(size -> addContainerSizeMenu(size, menu, component))
.collect(Collectors.toList());
if (dataSize == 1) {
items.get(0).setChecked(true);
}
}

private MenuItem addContainerSizeMenu(int size, MenuItem menu,
T component) {
MenuItem item = menu.addItem("Set data provider size to " + size,
itm -> setData(itm, size, component, isInMemory));
item.setCheckable(true);
return item;
}
}

+ 88
- 0
uitest/src/main/java/com/vaadin/tests/performance/CompatibilityGridMemory.java Wyświetl plik

@@ -0,0 +1,88 @@
package com.vaadin.tests.performance;

import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinServlet;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.v7.data.Item;
import com.vaadin.v7.data.util.BeanItemContainer;
import com.vaadin.v7.data.util.GeneratedPropertyContainer;
import com.vaadin.v7.data.util.PropertyValueGenerator;
import com.vaadin.v7.ui.Grid;

/**
* @author Vaadin Ltd
*
*/
public class CompatibilityGridMemory extends AbstractBeansMemoryTest<Grid> {

public static final String PATH = "/grid-compatibility-memory/";

/**
* The main servlet for the application.
*/
@WebServlet(urlPatterns = PATH
+ "*", name = "CompatibilityGridServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = CompatibilityGridMemory.class, productionMode = false, widgetset = "com.vaadin.v7.Vaadin7WidgetSet")
public static class Servlet extends VaadinServlet {
}

private static class ColumnGenerator
extends PropertyValueGenerator<String> {

private final Function<Address, Object> getter;

ColumnGenerator(Function<Address, Object> getter) {
this.getter = getter;
}

@Override
public String getValue(Item item, Object itemId, Object propertyId) {
Address address = ((Person) itemId).getAddress();
return Optional.ofNullable(address).map(getter)
.map(Object::toString).orElse(null);
}

@Override
public Class<String> getType() {
return String.class;
}
}

@Override
protected Grid createComponent() {
return new Grid();
}

@Override
protected void setInMemoryContainer(Grid grid, List<Person> persons) {
BeanItemContainer<Person> container = new BeanItemContainer<>(
Person.class);
container.addAll(persons);
GeneratedPropertyContainer generated = new GeneratedPropertyContainer(
container);
generated.addGeneratedProperty("street",
new ColumnGenerator(Address::getStreetAddress));
generated.addGeneratedProperty("zip",
new ColumnGenerator(Address::getPostalCode));
generated.addGeneratedProperty("city",
new ColumnGenerator(Address::getCity));
grid.setContainerDataSource(generated);
configureColumns(grid);
}

@Override
protected void setBackendContainer(Grid component, List<Person> data) {
throw new UnsupportedOperationException();
}

private void configureColumns(Grid grid) {
grid.setColumns("firstName", "lastName", "street", "zip", "city");
}
}

+ 59
- 0
uitest/src/main/java/com/vaadin/tests/performance/GridMemory.java Wyświetl plik

@@ -0,0 +1,59 @@
package com.vaadin.tests.performance;

import java.util.List;
import java.util.Optional;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinServlet;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.ui.Grid;

/**
* @author Vaadin Ltd
*
*/
public class GridMemory extends AbstractBeansMemoryTest<Grid<Person>> {

public static final String PATH = "/grid-memory/";

/**
* The main servlet for the application.
*/
@WebServlet(urlPatterns = PATH
+ "*", name = "GridServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = GridMemory.class, productionMode = false)
public static class Servlet extends VaadinServlet {
}

@Override
protected Grid<Person> createComponent() {
Grid<Person> grid = new Grid<>();
grid.addColumn(Person::getFirstName).setCaption("First Name");
grid.addColumn(Person::getLastName).setCaption("Last Name");
grid.addColumn(person -> Optional.ofNullable(person.getAddress())
.map(Address::getStreetAddress).orElse(null))
.setCaption("Street");
grid.addColumn(person -> Optional.ofNullable(person.getAddress())
.map(Address::getPostalCode).map(Object::toString).orElse(""))
.setCaption("Zip");
grid.addColumn(person -> Optional.ofNullable(person.getAddress())
.map(Address::getCity).orElse(null)).setCaption("City");
return grid;
}

@Override
protected void setInMemoryContainer(Grid<Person> grid,
List<Person> persons) {
grid.setItems(persons);
}

@Override
protected void setBackendContainer(Grid<Person> component,
List<Person> data) {
throw new UnsupportedOperationException();
}

}

+ 54
- 0
uitest/src/main/java/com/vaadin/tests/performance/ThreadMemoryLeaksTest.java Wyświetl plik

@@ -0,0 +1,54 @@
package com.vaadin.tests.performance;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;

public class ThreadMemoryLeaksTest extends AbstractReindeerTestUI {

public static class Worker {
long value = 0;
private TimerTask task = new TimerTask() {
@Override
public void run() {
value++;
}
};
private final Timer timer = new Timer(true);

public Worker() {
timer.scheduleAtFixedRate(task, new Date(), 1000);
}
}

int workers = 0;
Label label;

@Override
protected void setup(VaadinRequest request) {
label = new Label(String.format("%d workers", workers));
addComponent(label);
addComponent(new Button("Add worker", event -> {
new Worker();
workers++;
label.setValue(String.format("%d workers", workers));
}));
}

@Override
protected String getTestDescription() {
return "Inherited ThreadLocals should not leak memory. Clicking the "
+ "button starts a new thread, after which memory consumption "
+ "can be checked with visualvm";
}

@Override
protected Integer getTicketNumber() {
return 12401;
}
}

+ 83
- 0
uitest/src/main/java/com/vaadin/tests/performance/TreeGridMemory.java Wyświetl plik

@@ -0,0 +1,83 @@
package com.vaadin.tests.performance;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.TreeData;
import com.vaadin.data.provider.TreeDataProvider;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.ui.TreeGrid;

public class TreeGridMemory extends AbstractBeansMemoryTest<TreeGrid<Person>> {

public static final String PATH = "/tree-grid-memory/";

@WebServlet(urlPatterns = PATH
+ "*", name = "TreeGridServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = TreeGridMemory.class, productionMode = false)
public static class Servlet extends VaadinServlet {
}

private boolean initiallyExpanded = false;

@Override
protected void init(VaadinRequest request) {
if (request.getParameter("initiallyExpanded") != null) {
initiallyExpanded = true;
}
super.init(request);
}

@Override
protected TreeGrid<Person> createComponent() {
TreeGrid<Person> treeGrid = new TreeGrid<>();
treeGrid.addColumn(Person::getFirstName).setCaption("First Name");
treeGrid.addColumn(Person::getLastName).setCaption("Last Name");
treeGrid.addColumn(person -> Optional.ofNullable(person.getAddress())
.map(Address::getStreetAddress).orElse(null))
.setCaption("Street");
treeGrid.addColumn(person -> Optional.ofNullable(person.getAddress())
.map(Address::getPostalCode).map(Object::toString).orElse(""))
.setCaption("Zip");
treeGrid.addColumn(person -> Optional.ofNullable(person.getAddress())
.map(Address::getCity).orElse(null)).setCaption("City");
return treeGrid;
}

@Override
protected void setInMemoryContainer(TreeGrid<Person> treeGrid,
List<Person> data) {
TreeData<Person> treeData = new TreeData<>();
treeGrid.setDataProvider(new TreeDataProvider<>(treeData));
List<Person> toExpand = new ArrayList<>();
if (data.size() != 0 && data.size() % 2 == 0) {
// treat list as if it were a balanced binary tree
treeData.addItem(null, data.get(0));
int n = 0;
while (2 * n + 2 < data.size()) {
treeData.addItems(data.get(n),
data.subList(2 * n + 1, 2 * n + 3));
toExpand.add(data.get(n));
n++;
}
} else {
treeData.addItems(null, data);
}
if (initiallyExpanded) {
treeGrid.expand(toExpand);
}
}

@Override
protected void setBackendContainer(TreeGrid<Person> component,
List<Person> data) {
throw new UnsupportedOperationException();
}
}

+ 98
- 0
uitest/src/main/java/com/vaadin/tests/performance/TreeTableMemory.java Wyświetl plik

@@ -0,0 +1,98 @@
package com.vaadin.tests.performance;

import java.util.List;
import java.util.Optional;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.tests.data.bean.Address;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.v7.data.Item;
import com.vaadin.v7.data.util.HierarchicalContainer;
import com.vaadin.v7.ui.TreeTable;

public class TreeTableMemory extends AbstractBeansMemoryTest<TreeTable> {

public static final String PATH = "/tree-table-memory/";

@WebServlet(urlPatterns = PATH
+ "*", name = "TreeTableServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = TreeTableMemory.class, productionMode = false, widgetset = "com.vaadin.v7.Vaadin7WidgetSet")
public static class Servlet extends VaadinServlet {
}

private boolean initiallyExpanded = false;

@Override
protected void init(VaadinRequest request) {
if (request.getParameter("initiallyExpanded") != null) {
initiallyExpanded = true;
}
super.init(request);
}

@Override
protected TreeTable createComponent() {
TreeTable treeTable = new TreeTable();
return treeTable;
}

@Override
protected void setInMemoryContainer(TreeTable treeTable,
List<Person> data) {
HierarchicalContainer container = new HierarchicalContainer();
container.addContainerProperty("firstName", String.class, null);
container.addContainerProperty("lastName", String.class, null);
container.addContainerProperty("street", String.class, null);
container.addContainerProperty("zip", String.class, null);
container.addContainerProperty("city", String.class, null);
treeTable.setContainerDataSource(container);

if (data.size() != 0 && data.size() % 2 == 0) {
createItem(0, container, data);
treeTable.setCollapsed(0, false);
int n = 0;
while (2 * n + 2 < data.size()) {
for (int i : new Integer[] { 1, 2 }) {
createItem(2 * n + i, container, data);
container.setParent(2 * n + i, n);
if (initiallyExpanded) {
treeTable.setCollapsed(2 * n + i, false);
}
}
n++;
}
} else {
for (int i = 0; i < data.size(); i++) {
createItem(i, container, data);
}
}
}

private void createItem(int index, HierarchicalContainer container,
List<Person> data) {
Item item = container.addItem(index);
item.getItemProperty("firstName")
.setValue(data.get(index).getFirstName());
item.getItemProperty("lastName")
.setValue(data.get(index).getLastName());
item.getItemProperty("street")
.setValue(Optional.ofNullable(data.get(index).getAddress())
.map(Address::getStreetAddress).orElse(null));
item.getItemProperty("zip")
.setValue(Optional.ofNullable(data.get(index).getAddress())
.map(Address::getPostalCode).map(Object::toString)
.orElse(""));
item.getItemProperty("city")
.setValue(Optional.ofNullable(data.get(index).getAddress())
.map(Address::getCity).orElse(null));
}

@Override
protected void setBackendContainer(TreeTable component, List<Person> data) {
throw new UnsupportedOperationException();
}
}

+ 24
- 0
uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridHugeTreeTest.java Wyświetl plik

@@ -2,6 +2,10 @@ package com.vaadin.tests.components.treegrid;

import static org.junit.Assert.assertEquals;

import org.apache.commons.lang3.StringUtils;
import org.hamcrest.core.StringStartsWith;
import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Test;

import com.vaadin.testbench.elements.ButtonElement;
@@ -63,6 +67,26 @@ public class TreeGridHugeTreeTest extends SingleBrowserTest {
new String[] { "Granddad 0", "Dad 0/0", "Son 0/0/0" });
}

@Test
public void collapsed_subtrees_outside_of_cache_stay_expanded() {
Assume.assumeThat(System.getProperty("java.specification.version"),
new StringStartsWith("1.8"));
getDriver().get(StringUtils.strip(getBaseURL(), "/")
+ "/tree-grid-memory/?items=200&initiallyExpanded");
grid = $(TreeGridElement.class).first();

String[] cellTexts = new String[100];
for (int i = 0; i < 100; i++) {
cellTexts[i] = grid.getRow(i).getCell(0).getText();
}
grid.scrollToRow(0);

grid.collapseWithClick(1);
grid.expandWithClick(1);

assertCellTexts(0, 0, cellTexts);
}

private void assertCellTexts(int startRowIndex, int cellIndex,
String[] cellTexts) {
int index = startRowIndex;

+ 117
- 0
uitest/src/test/java/com/vaadin/tests/performance/MemoryIT.java Wyświetl plik

@@ -0,0 +1,117 @@
package com.vaadin.tests.performance;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import com.vaadin.testbench.By;
import com.vaadin.testcategory.MeasurementTest;
import com.vaadin.tests.tb3.SingleBrowserTest;

@Category(MeasurementTest.class)
public class MemoryIT extends SingleBrowserTest {

private static final int MAX_ITERATIONS = 20;

@Test
public void measureMemory() {
performTest(GridMemory.PATH + "?items=1", "grid-v8-one-item-");
performTest(CompatibilityGridMemory.PATH + "?items=1",
"grid-v7-one-item-");

performTest(GridMemory.PATH + "?items=1", "grid-v8-100thousand-items-");
performTest(CompatibilityGridMemory.PATH + "?items=100000",
"grid-v7-100thousand-items-");

performTest(TreeGridMemory.PATH + "?items=1", "tree-grid-one-item-");
performTest(TreeTableMemory.PATH + "?items=1", "tree-table-one-item-");

performTest(TreeGridMemory.PATH + "?items=100&initiallyExpanded",
"tree-grid-100-items-initially-expanded-");
performTest(TreeTableMemory.PATH + "?items=100&initiallyExpanded",
"tree-table-100-items-initially-expanded-");

performTest(TreeGridMemory.PATH + "?items=100000",
"tree-grid-100thousand-items-");
performTest(TreeTableMemory.PATH + "?items=100000",
"tree-table-100thousand-items-");
}

@Override
protected void closeApplication() {
}

private void performTest(String path, String teamcityStatPrefix) {
double lastResult = 0;
int stableNumber = 0;
List<Long> renderingTimes = new ArrayList<>();
List<Long> requestTimes = new ArrayList<>();
for (int i = 0; i < MAX_ITERATIONS; i++) {
openUI(path);
renderingTimes.add(testBench().totalTimeSpentRendering());
requestTimes.add(testBench().totalTimeSpentServicingRequests());
long currentResult = Long
.parseLong(findElement(By.id("memory")).getText());
close();

if (approx(lastResult, currentResult, 0.001)) {
stableNumber++;
}
lastResult = currentResult;
if (stableNumber == 5) {
System.out.println(
"Memory usage stabilized after " + i + " iterations");
printTeamcityStats(teamcityStatPrefix + "size", currentResult);
printTeamcityStats(teamcityStatPrefix + "rendering-time",
median(renderingTimes));
printTeamcityStats(teamcityStatPrefix + "request-time",
median(requestTimes));
return;
}
if (i == MAX_ITERATIONS) {
fail("Memory size does not stabilize");
}
}
}

private long median(List<Long> values) {
values.sort(Long::compareTo);
int middle = values.size() / 2;
if (values.size() % 2 == 1) {
return values.get(middle);
} else {
return (values.get(middle - 1) + values.get(middle)) / 2;
}
}

private boolean approx(double num1, double num2, double epsilon) {
double delta = Math.abs(num1 - num2);
double deltaLimit = num2 * epsilon;
return delta < deltaLimit;
}

private void openUI(String path) {
getDriver().get(StringUtils.strip(getBaseURL(), "/") + path);
assertTrue(isElementPresent(By.className("v-grid"))
|| isElementPresent(By.className("v-treegrid"))
|| isElementPresent(By.className("v-table")));
}

private void close() {
findElement(By.id("close")).click();
}

private void printTeamcityStats(String key, long value) {
// ##teamcity[buildStatisticValue key=&#39;&lt;valueTypeKey&gt;&#39;
// value=&#39;&lt;value&gt;&#39;]
System.out.println("##teamcity[buildStatisticValue key='" + key
+ "' value='" + value + "']");

}
}

Ładowanie…
Anuluj
Zapisz