/**
* Copyright (c) 2014 - 2017 Frank Appel
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Frank Appel - initial API and implementation
*/
package com.codeaffine.eclipse.swt.util;
import static com.codeaffine.eclipse.swt.testhelper.TestResources.PROTECTED_CLASS_NAME;
import static com.codeaffine.eclipse.swt.util.ControlReflectionUtil.$;
import static com.codeaffine.eclipse.swt.widget.scrollable.TreeHelper.createTree;
import static com.codeaffine.test.util.lang.ThrowableCaptor.thrownBy;
import static java.lang.Integer.valueOf;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.lang.reflect.Field;
import org.eclipse.swt.accessibility.Accessible;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Tree;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import com.codeaffine.eclipse.swt.test.util.DisplayHelper;
import com.codeaffine.eclipse.swt.test.util.SWTIgnoreConditions.GtkPlatform;
import com.codeaffine.eclipse.swt.widget.scrollable.StyledTextAdapter;
import com.codeaffine.eclipse.swt.widget.scrollable.TreeAdapter;
import com.codeaffine.test.util.junit.ConditionalIgnoreRule;
import com.codeaffine.test.util.junit.ConditionalIgnoreRule.ConditionalIgnore;
public class ControlReflectionUtilTest {
private static final String UNDECLARED = "undeclared";
private static final String FIELD_NAME_DISPLAY = "display";
private static final String FIELD_NAME_PARENT = "parent";
private static final String FIELD_NAME_COLUMN_COUNT = "columnCount";
@Rule public final ConditionalIgnoreRule conditionIgnoreRule = new ConditionalIgnoreRule();
@Rule public final DisplayHelper displayHelper = new DisplayHelper();
private ControlReflectionUtil reflectionUtil;
@Before
public void setUp() {
reflectionUtil = new ControlReflectionUtil();
}
@Test
public void defineWidgetClass() {
Class<?> actual = reflectionUtil.defineWidgetClass( PROTECTED_CLASS_NAME );
assertThat( actual.getName() ).isSameAs( PROTECTED_CLASS_NAME );
}
@Test
public void defineWidgetClassThatDoesNotExist() {
Throwable actual = thrownBy( () -> reflectionUtil.defineWidgetClass( "path.to.Unknown" ) );
assertThat( actual )
.hasMessageContaining( "path/to/Unknown.class" )
.isInstanceOf( IllegalArgumentException.class );
}
@Test
public void newInstance() {
Tree actual = reflectionUtil.newInstance( Tree.class );
assertThat( actual ).isNotNull();
}
@Test
public void newInstanceWithUninstantiableType() {
Throwable actual = thrownBy( () -> reflectionUtil.newInstance( Control.class ) );
assertThat( actual )
.hasMessageContaining( Control.class.getName() )
.hasRootCauseInstanceOf( InstantiationException.class );
}
@Test
public void invokeOfControlMethod() {
TreeAdapter receiver = reflectionUtil.newInstance( TreeAdapter.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, displayHelper.getDisplay() );
reflectionUtil.setField( receiver, FIELD_NAME_PARENT, displayHelper.createShell() );
reflectionUtil.invoke( receiver, "createWidget", $( valueOf( 0 ), int.class ) );
assertThat( receiver.isDisposed() ).isFalse();
}
@Test
public void invokeOfReceiverMethod() {
Tree receiver = mock( Tree.class );
reflectionUtil.invoke( receiver, "showSelection" );
verify( receiver ).showSelection();
}
@Test
@ConditionalIgnore( condition = GtkPlatform.class )
public void invokeOfReceiverExtensionMethod() {
StyledText receiver = reflectionUtil.newInstance( StyledTextAdapter.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, displayHelper.getDisplay() );
reflectionUtil.invoke( receiver, "initializeAccessible" );
Accessible actual = reflectionUtil.getField( receiver, "acc", Accessible.class );
assertThat( actual ).isNotNull();
}
@Test
public void invokeOfUndeclaredMethod() {
Throwable actual = thrownBy( () -> reflectionUtil.invoke( mock( Tree.class ), UNDECLARED ) );
assertThat( actual )
.hasMessageContaining( UNDECLARED )
.hasRootCauseInstanceOf( NoSuchMethodException.class );
}
@Test
public void invokeOfMethodThatThrowsException() {
RuntimeException expected = new RuntimeException( "thrownOnPurpose" );
final Tree receiver = stubTreeWithProblemOnRedraw( expected );
Throwable actual = thrownBy( () -> reflectionUtil.invoke( receiver, "redraw" ) );
assertThat( actual )
.isSameAs( expected );
}
@Test
public void setFieldOfWidget() {
Display expected = displayHelper.getDisplay();
Tree receiver = reflectionUtil.newInstance( Tree.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, expected );
Display actual = receiver.getDisplay();
assertThat( actual ).isSameAs( expected );
}
@Test
public void setFieldOfControl() {
Composite expected = displayHelper.createShell();
Tree receiver = reflectionUtil.newInstance( Tree.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, displayHelper.getDisplay() );
reflectionUtil.setField( receiver, FIELD_NAME_PARENT, expected );
Composite actual = receiver.getParent();
assertThat( actual ).isSameAs( expected );
}
@Test
public void setFieldOfReceiver() throws Exception {
Tree receiver = createTree( displayHelper.createShell(), 1, 1 );
int expected = 10;
reflectionUtil.setField( receiver, FIELD_NAME_COLUMN_COUNT, expected );
Object actual = readAndreset( receiver, 0 );
assertThat( actual ).isEqualTo( expected );
}
@Test
public void setFieldWithUndeclaredName() {
Tree tree = mock( Tree.class );
Throwable actual = thrownBy( () -> reflectionUtil.setField( tree, UNDECLARED, displayHelper.getDisplay() ) );
assertThat( actual )
.hasMessageContaining( UNDECLARED )
.hasCauseInstanceOf( NoSuchFieldException.class );
}
@Test
public void setFieldWithWrongType() {
Throwable actual = thrownBy( () -> reflectionUtil.setField( mock( Tree.class ), FIELD_NAME_DISPLAY, new Object() ) );
assertThat( actual )
.hasMessageContaining( FIELD_NAME_DISPLAY )
.hasMessageContaining( Object.class.getName() )
.isInstanceOf( IllegalArgumentException.class );
}
@Test
public void getFieldOfWidget() {
Display expected = displayHelper.getDisplay();
Tree receiver = reflectionUtil.newInstance( Tree.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, expected );
Display actual = reflectionUtil.getField( receiver, FIELD_NAME_DISPLAY, Display.class );
assertThat( actual ).isSameAs( expected );
}
@Test
public void getFieldOfControl() {
Composite expected = displayHelper.createShell();
Tree receiver = reflectionUtil.newInstance( Tree.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, displayHelper.getDisplay() );
reflectionUtil.setField( receiver, FIELD_NAME_PARENT, expected );
Composite actual = reflectionUtil.getField( receiver, FIELD_NAME_PARENT, Composite.class );
assertThat( actual ).isSameAs( expected );
}
@Test
public void getFieldOfReceiver() {
int expected = 10;
Tree receiver = reflectionUtil.newInstance( Tree.class );
reflectionUtil.setField( receiver, FIELD_NAME_COLUMN_COUNT, expected );
int actual = reflectionUtil.getField( receiver, FIELD_NAME_COLUMN_COUNT, Integer.class );
assertThat( actual ).isEqualTo( expected );
}
@Test
@ConditionalIgnore( condition = GtkPlatform.class )
public void getFieldOfReceiverExtension() {
int expected = 10;
Tree receiver = reflectionUtil.newInstance( TreeAdapter.class );
reflectionUtil.setField( receiver, FIELD_NAME_COLUMN_COUNT, expected );
int actual = reflectionUtil.getField( receiver, FIELD_NAME_COLUMN_COUNT, Integer.class );
assertThat( actual ).isEqualTo( expected );
}
@Test
public void getFieldWithUndeclaredName() {
Tree tree = mock( Tree.class );
Throwable actual = thrownBy( () -> reflectionUtil.getField( tree, UNDECLARED, Display.class ) );
assertThat( actual )
.hasMessageContaining( UNDECLARED )
.hasCauseInstanceOf( NoSuchFieldException.class );
}
@Test
public void getFieldWithWrongType() {
Tree receiver = reflectionUtil.newInstance( Tree.class );
reflectionUtil.setField( receiver, FIELD_NAME_DISPLAY, displayHelper.getDisplay() );
Throwable actual = thrownBy( () -> reflectionUtil.getField( receiver, FIELD_NAME_DISPLAY, Runnable.class ) );
assertThat( actual )
.hasMessageContaining( Display.class.getName() )
.hasMessageContaining( Runnable.class.getName() )
.isInstanceOf( ClassCastException.class );
}
private Object readAndreset( Tree receiver, int resetValue ) throws Exception {
Field field = Tree.class.getDeclaredField( FIELD_NAME_COLUMN_COUNT );
field.setAccessible( true );
Object result = field.get( receiver );
reflectionUtil.setField( receiver, FIELD_NAME_COLUMN_COUNT, resetValue );
return result;
}
private static Tree stubTreeWithProblemOnRedraw( Throwable problem ) {
Tree result = mock( Tree.class );
doThrow( problem ).when( result ).redraw();
return result;
}
}