package com.vaadin.tests.server.component;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import com.vaadin.server.VaadinSession;
import com.vaadin.tests.VaadinClasses;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentRootSetter;
import com.vaadin.ui.Composite;
import com.vaadin.ui.ConnectorTracker;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
public class StateGetDoesNotMarkDirtyTest {
private final Set<String> excludedMethods = new HashSet<>();
@Before
public void setUp() {
excludedMethods.add(Label.class.getName() + "getDataProviderValue");
excludedMethods.add("getConnectorId");
excludedMethods.add("getContent");
excludedMethods.add("com.vaadin.ui.Grid:getSelectAllCheckBoxVisible");
excludedMethods.add("com.vaadin.ui.TreeGrid:getDataProvider");
}
@Test
public void testGetDoesntMarkStateDirty() throws Exception {
int count = 0;
for (Class<? extends Component> clazz : VaadinClasses.getComponents()) {
if (clazz.isInterface()
|| Modifier.isAbstract(clazz.getModifiers())) {
continue;
}
Component newInstance = construct(clazz);
if (newInstance == null) {
continue;
}
count++;
prepareMockUI(newInstance);
Set<Method> methods = new HashSet<>();
methods.addAll(Arrays.asList(clazz.getMethods()));
methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
for (Method method : methods) {
try {
if (method.getName().startsWith("is")
|| method.getName().startsWith("get")) {
if (method.getName().startsWith("getState")) {
continue;
}
if (method.getParameterTypes().length > 0) {
// usually getters do not have params, if they have
// we still wouldnt know what to put into
continue;
}
if (excludedMethods.contains(
clazz.getName() + ":" + method.getName())) {
// blacklisted method for specific classes
continue;
}
if (excludedMethods.contains(method.getName())) {
// blacklisted method for all classes
continue;
}
// just to make sure we can invoke it
method.setAccessible(true);
try {
method.invoke(newInstance);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof UnsupportedOperationException) {
// Overridden getter which is not supposed to be
// called
} else {
throw e;
}
}
}
} catch (Exception e) {
System.err.println("problem with method " + clazz.getName()
+ "# " + method.getName());
e.printStackTrace();
throw e;
}
}
}
Assert.assertTrue(count > 0);
}
private void prepareMockUI(Component newInstance) {
UI ui = mockUI();
ConnectorTracker connectorTracker = ui.getConnectorTracker();
Mockito.doThrow(new RuntimeException("getState(true) called in getter"))
.when(connectorTracker).markDirty(newInstance);
newInstance.setParent(null);
newInstance.setParent(ui);
}
private UI mockUI() {
UI ui = Mockito.mock(UI.class);
Mockito.when(ui.getLocale()).thenReturn(Locale.ENGLISH);
ConnectorTracker connectorTracker = Mockito
.mock(ConnectorTracker.class);
Mockito.when(ui.getConnectorTracker()).thenReturn(connectorTracker);
return ui;
}
private Component construct(Class<? extends Component> clazz) {
try {
Constructor<? extends Component> declaredConstructor = clazz
.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Component component = declaredConstructor.newInstance();
if (component instanceof UI) {
return component;
}
if (component.getClass().equals(Composite.class)) {
// Plain Composite needs a root.
ComponentRootSetter.setRoot(component, new Label());
}
emulateAttach(component);
return component;
} catch (NoSuchMethodException e) {
// no default CTOR, skip
return null;
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private void emulateAttach(Component component) {
UI ui = mockUI();
VaadinSession session = Mockito.mock(VaadinSession.class);
Mockito.when(session.hasLock()).thenReturn(true);
Mockito.when(ui.getSession()).thenReturn(session);
component.setParent(ui);
component.attach();
}
}