/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.internal.ui.ridgets.swt; import java.util.ArrayList; import java.util.List; import java.util.Queue; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.riena.core.util.ReflectionUtils; import org.eclipse.riena.internal.ui.swt.test.UITestHelper; import org.eclipse.riena.ui.ridgets.IRidget; import org.eclipse.riena.ui.ridgets.ITreeRidget; import org.eclipse.riena.ui.ridgets.listener.SelectionEvent; import org.eclipse.riena.ui.ridgets.swt.uibinding.SwtControlRidgetMapper; import org.eclipse.riena.ui.ridgets.tree2.ITreeNode; import org.eclipse.riena.ui.ridgets.tree2.TreeNode; /** * Tests for the {@link TreeRidget}. * <p> * These tests use a TreeNode that wraps a String and focus on methods from the * {@link ITreeRidget}. * * @see TreeRidgetTest2 */ public class TreeRidgetTest extends AbstractSWTRidgetTest { private static final String ROOT_NODE_USER_OBJECT = "TestRootNode"; private static final String ROOT_CHILD1_NODE_USER_OBJECT = "TestRootChild1Node"; private static final String ROOT_CHILD2_NODE_USER_OBJECT = "TestRootChild2Node"; private static final String ROOT_CHILD1_CHILD1_NODE_USER_OBJECT = "TestRootChild1Child1Node"; private static final String ROOT_CHILD1_CHILD2_NODE_USER_OBJECT = "TestRootChild1Child2Node"; private static final String ROOT_CHILD1_CHILD2_CHILD_NODE_USER_OBJECT = "TestRootChild1Child2ChildNode"; private ITreeNode rootNode; private ITreeNode rootChild1Node; private ITreeNode rootChild2Node; private ITreeNode rootChild1Child2Node; private ITreeNode rootChild1Child1Node; private ITreeNode rootChild1Child2ChildNode; @Override protected void setUp() throws Exception { super.setUp(); getRidget().bindToModel(initializeTreeModel(), ITreeNode.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE); } @Override protected Control createWidget(final Composite parent) { return new Tree(parent, SWT.MULTI); } @Override protected IRidget createRidget() { return new TreeRidget(); } @Override protected Tree getWidget() { return (Tree) super.getWidget(); } @Override protected ITreeRidget getRidget() { return (ITreeRidget) super.getRidget(); } // testing methods // //////////////// public void testRidgetMapping() { final SwtControlRidgetMapper mapper = SwtControlRidgetMapper.getInstance(); assertSame(TreeRidget.class, mapper.getRidgetClass(getWidget())); } public void testGetUIControl() throws Exception { final Tree control = getWidget(); assertEquals(control, getRidget().getUIControl()); } public void testBindToModel() { assertEquals(ROOT_NODE_USER_OBJECT, getUIControlItem(0).getText(0)); assertEquals(ROOT_CHILD1_NODE_USER_OBJECT, getUIControlItem(1).getText(0)); assertEquals(ROOT_CHILD1_CHILD1_NODE_USER_OBJECT, getUIControlItem(2).getText(0)); assertEquals(ROOT_CHILD2_NODE_USER_OBJECT, getUIControlItem(3).getText(0)); } public void testBindToModelNull() { final ITreeRidget ridget = getRidget(); final ITreeNode[] roots = { rootNode }; try { ridget.bindToModel(null, ITreeNode.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE); fail(); } catch (final RuntimeException rex) { ok(); } try { ridget.bindToModel(roots, null, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE); fail(); } catch (final RuntimeException rex) { ok(); } try { ridget.bindToModel(roots, ITreeNode.class, null, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE); fail(); } catch (final RuntimeException rex) { ok(); } try { ridget.bindToModel(roots, ITreeNode.class, ITreeNode.PROPERTY_CHILDREN, null, ITreeNode.PROPERTY_VALUE); fail(); } catch (final RuntimeException rex) { ok(); } try { ridget.bindToModel(roots, ITreeNode.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, null); fail(); } catch (final RuntimeException rex) { ok(); } } public void testUpdateTreeFromModel() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); ridget.expandAll(); assertEquals(1, control.getItemCount()); final TreeItem treeRoot = control.getItems()[0]; assertEquals(ROOT_NODE_USER_OBJECT, treeRoot.getText()); assertEquals(2, treeRoot.getItemCount()); final TreeItem treeRootChild1 = treeRoot.getItem(0); assertEquals(ROOT_CHILD1_NODE_USER_OBJECT, treeRootChild1.getText()); final TreeItem treeRootChild2 = treeRoot.getItem(1); assertEquals(ROOT_CHILD2_NODE_USER_OBJECT, treeRootChild2.getText()); assertEquals(2, treeRootChild1.getItemCount()); assertEquals(0, treeRootChild2.getItemCount()); final TreeItem treeRootChild1Child1 = treeRootChild1.getItem(0); assertEquals(ROOT_CHILD1_CHILD1_NODE_USER_OBJECT, treeRootChild1Child1.getText()); final TreeItem treeRootChild1Child2 = treeRootChild1.getItem(1); assertEquals(ROOT_CHILD1_CHILD2_NODE_USER_OBJECT, treeRootChild1Child2.getText()); assertEquals(0, treeRootChild1Child1.getItemCount()); assertEquals(1, treeRootChild1Child2.getItemCount()); final TreeItem treeRootChild1Child2Child = treeRootChild1Child2.getItem(0); assertEquals(ROOT_CHILD1_CHILD2_CHILD_NODE_USER_OBJECT, treeRootChild1Child2Child.getText()); assertEquals(0, treeRootChild1Child2Child.getItemCount()); } public void testUpdateOnModelChanges() { final Tree control = getWidget(); assertEquals(3, TreeUtils.getItemCount(control)); removeFromParent(rootChild2Node); assertEquals(2, TreeUtils.getItemCount(control)); new TreeNode(rootNode, "TestNewNode1"); new TreeNode(rootNode, "TestNewNode2"); assertEquals(4, TreeUtils.getItemCount(control)); } public void testExpandAndCollapseTree() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); assertEquals(3, TreeUtils.getItemCount(control)); ridget.expandAll(); assertEquals(6, TreeUtils.getItemCount(control)); ridget.collapseAll(); assertEquals(1, TreeUtils.getItemCount(control)); // check that the tree was collapsed recursively... ridget.expand(rootNode); assertEquals(3, TreeUtils.getItemCount(control)); ridget.expandAll(); assertEquals(6, TreeUtils.getItemCount(control)); // ...to check that it collapses to its default state when a new model // is updated... getRidget().bindToModel(initializeTreeModel(), ITreeNode.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE); assertEquals(3, TreeUtils.getItemCount(control)); ridget.expandAll(); assertEquals(6, TreeUtils.getItemCount(control)); // ...update from model should preserver expansion state... ridget.updateFromModel(); assertEquals(6, TreeUtils.getItemCount(control)); } /** * The expandTree and collapseTree methods do not affect the control when * unbound, but are applied to the control after rebinding. */ public void testExpandAndCollapseTreeWhenUnbound() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); assertEquals(3, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.expandAll(); ridget.setUIControl(control); assertEquals(6, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.collapseAll(); ridget.setUIControl(control); assertEquals(1, TreeUtils.getItemCount(control)); } public void testExpandAndCollapseSingleNodes() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); ridget.expandAll(); assertEquals(6, TreeUtils.getItemCount(control)); ridget.collapse(rootChild1Child2Node); assertEquals(5, TreeUtils.getItemCount(control)); ridget.collapse(rootChild1Node); assertEquals(3, TreeUtils.getItemCount(control)); ridget.expand(rootChild1Node); assertEquals(5, TreeUtils.getItemCount(control)); ridget.expand(rootChild1Child2Node); assertEquals(6, TreeUtils.getItemCount(control)); } /** * The expand and collapse methods do not affect the control when unbound, * but are applied to the control after rebinding. */ public void testExpandAndCollapseSingleNodesWhenUnbound() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); assertEquals(3, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.expand(rootChild1Node); ridget.setUIControl(control); assertEquals(5, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.collapse(rootNode); ridget.setUIControl(control); assertEquals(1, TreeUtils.getItemCount(control)); } public void testFullExpansionStatusIsPreserved() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); ridget.expandAll(); assertEquals(6, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.setUIControl(control); assertEquals(6, TreeUtils.getItemCount(control)); ridget.collapseAll(); assertEquals(1, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.setUIControl(control); assertEquals(1, TreeUtils.getItemCount(control)); } public void testPartialExpansionStatusIsPreserved() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); assertEquals(3, TreeUtils.getItemCount(control)); ridget.expand(rootChild1Node); assertEquals(5, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.setUIControl(control); assertEquals(5, TreeUtils.getItemCount(control)); ridget.collapse(rootNode); assertEquals(1, TreeUtils.getItemCount(control)); ridget.setUIControl(null); ridget.setUIControl(control); assertEquals(1, TreeUtils.getItemCount(control)); } public void testAddDoubleClickListener() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); try { ridget.addDoubleClickListener(null); fail(); } catch (final RuntimeException npe) { ok(); } final FTActionListener listener1 = new FTActionListener(); ridget.addDoubleClickListener(listener1); final FTActionListener listener2 = new FTActionListener(); ridget.addDoubleClickListener(listener2); ridget.addDoubleClickListener(listener2); final Event doubleClick = new Event(); doubleClick.widget = control; doubleClick.type = SWT.MouseDoubleClick; control.notifyListeners(SWT.MouseDoubleClick, doubleClick); assertEquals(1, listener1.getCount()); assertEquals(1, listener2.getCount()); ridget.removeDoubleClickListener(listener1); control.notifyListeners(SWT.MouseDoubleClick, doubleClick); assertEquals(1, listener1.getCount()); } public void testAddSelectionListener() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); try { ridget.addSelectionListener(null); fail(); } catch (final RuntimeException npe) { ok(); } final TestSelectionListener selectionListener = new TestSelectionListener(); ridget.addSelectionListener(selectionListener); assertEquals(0, ridget.getSelection().size()); assertEquals(0, control.getSelectionCount()); control.setFocus(); UITestHelper.sendKeyAction(control.getDisplay(), UITestHelper.KC_ARROW_DOWN); assertEquals(1, ridget.getSelection().size()); assertEquals(1, control.getSelectionCount()); assertEquals(1, selectionListener.getCount()); final SelectionEvent selectionEvent = selectionListener.getSelectionEvent(); assertEquals(ridget, selectionEvent.getSource()); assertTrue(selectionEvent.getOldSelection().isEmpty()); assertEquals(ridget.getSelection(), selectionEvent.getNewSelection()); // System.out.println("SelectionEvent: " + selectionListener.getSelectionEvent()); UITestHelper.sendKeyAction(control.getDisplay(), UITestHelper.KC_ARROW_DOWN); assertEquals(1, ridget.getSelection().size()); assertEquals(1, control.getSelectionCount()); assertEquals(2, selectionListener.getCount()); final SelectionEvent selectionEvent2 = selectionListener.getSelectionEvent(); assertEquals(ridget, selectionEvent.getSource()); assertEquals(selectionEvent.getNewSelection(), selectionEvent2.getOldSelection()); assertEquals(ridget.getSelection(), selectionEvent2.getNewSelection()); // System.out.println("SelectionEvent: " + selectionListener.getSelectionEvent()); ridget.removeSelectionListener(selectionListener); } public void testBug266042() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); final TreeNode root1 = new TreeNode("r1"); new TreeNode(root1, "r1.a"); new TreeNode(root1, "r1.b"); final TreeNode root2 = new TreeNode("r2"); new TreeNode(root2, "r2.a"); new TreeNode(root2, "r2.b"); ridget.bindToModel(new Object[] { root1, root2 }, TreeNode.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE); assertEquals(2, TreeUtils.getItemCount(control)); assertEquals("r1", control.getItem(0).getText()); assertEquals("r2", control.getItem(1).getText()); } public void testBug290365() { final ITreeRidget ridget = getRidget(); final TreeNodeWithIcon root1 = new TreeNodeWithIcon(null, "r1"); new TreeNodeWithIcon(root1, "r1.a"); new TreeNodeWithIcon(root1, "r1.b"); ridget.bindToModel(new Object[] { root1 }, TreeNodeWithIcon.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE, null, null, "icon"); ridget.updateFromModel(); // bug: throws exception ok(); } /** * As per bug 277283. */ public void testSetRootsVisibleFiresEvents() { final ITreeRidget ridget = getRidget(); assertTrue(ridget.getRootsVisible()); expectPropertyChangeEvent(ITreeRidget.PROPERTY_ROOTS_VISIBLE, true, false); ridget.setRootsVisible(false); verifyPropertyChangeEvents(); assertFalse(ridget.getRootsVisible()); expectPropertyChangeEvent(ITreeRidget.PROPERTY_ROOTS_VISIBLE, false, true); ridget.setRootsVisible(true); verifyPropertyChangeEvents(); assertTrue(ridget.getRootsVisible()); expectNoPropertyChangeEvent(); ridget.setRootsVisible(true); verifyPropertyChangeEvents(); } /** * As per bug 300014 */ public void testRefreshNull() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); final TreeNode root = new TreeNode(null) { private Object myvalue; // hack to create an implementation that does not fire an event @Override public void setValue(final Object value) { myvalue = value; } @Override public Object getValue() { return myvalue; } }; root.setValue("hello world"); ridget.bindToModel(new Object[] { root }, TreeNode.class, TreeNode.PROPERTY_CHILDREN, TreeNode.PROPERTY_PARENT, TreeNode.PROPERTY_VALUE); ridget.updateFromModel(); final TreeItem treeRoot = control.getItems()[0]; assertEquals("hello world", treeRoot.getText()); root.setValue("changed"); assertEquals("hello world", treeRoot.getText()); ridget.refresh(null); assertEquals("changed", treeRoot.getText()); } /** * As per bug 300014 */ public void testRefresh() { final ITreeRidget ridget = getRidget(); final Tree control = getWidget(); final TreeNode root = new TreeNode(null) { private Object myvalue; // hack to create an implementation that does not fire an event @Override public void setValue(final Object value) { myvalue = value; } @Override public Object getValue() { return myvalue; } }; root.setValue("hello world"); ridget.bindToModel(new Object[] { root }, TreeNode.class, TreeNode.PROPERTY_CHILDREN, TreeNode.PROPERTY_PARENT, TreeNode.PROPERTY_VALUE); ridget.updateFromModel(); final TreeItem treeRoot = control.getItems()[0]; assertEquals("hello world", treeRoot.getText()); root.setValue("changed"); assertEquals("hello world", treeRoot.getText()); ridget.refresh(root); assertEquals("changed", treeRoot.getText()); } /** * Tests the private method {@code collectChildren}. */ public void testCollectChildren() { initializeTreeModel(); final List<ITreeNode> result = new ArrayList<ITreeNode>(); ReflectionUtils.invokeHidden(getRidget(), "collectChildren", rootNode, result); assertEquals(5, result.size()); assertSame(rootChild1Child1Node, result.get(0)); assertSame(rootChild1Child2ChildNode, result.get(1)); assertSame(rootChild1Child2Node, result.get(2)); assertSame(rootChild1Node, result.get(3)); assertSame(rootChild2Node, result.get(4)); } /** * Tests the private method {@code addExpansionCommandsForRootDescendants}. */ public void testAddExpansionCommandsForRootDescendants() { final ITreeNode[] treeModel = initializeTreeModel(); getRidget().bindToModel(treeModel, ITreeNode.class, ITreeNode.PROPERTY_CHILDREN, ITreeNode.PROPERTY_PARENT, ITreeNode.PROPERTY_VALUE, null, null, null, null, ExpandedTreeNode.PROPERTY_EXPANDED); final Queue<?> expansionStack = ReflectionUtils.getHidden(getRidget(), "expansionStack"); expansionStack.clear(); ReflectionUtils.invokeHidden(getRidget(), "addExpansionCommandsForRootDescendants", treeModel[0]); assertEquals(2, expansionStack.size()); } /** * Tests the private method {@code isLeaf(Object)}. */ public void testIsLeaf() { initializeTreeModel(); boolean ret = ReflectionUtils.invokeHidden(getRidget(), "isLeaf", rootChild1Node); assertFalse(ret); ret = ReflectionUtils.invokeHidden(getRidget(), "isLeaf", rootChild1Child2ChildNode); assertTrue(ret); } // helping methods // //////////////// private ITreeNode[] initializeTreeModel() { rootNode = new ExpandedTreeNode(ROOT_NODE_USER_OBJECT); rootChild1Node = new ExpandedTreeNode(rootNode, ROOT_CHILD1_NODE_USER_OBJECT, false); rootChild2Node = new ExpandedTreeNode(rootNode, ROOT_CHILD2_NODE_USER_OBJECT); rootChild1Child1Node = new ExpandedTreeNode(rootChild1Node, ROOT_CHILD1_CHILD1_NODE_USER_OBJECT); rootChild1Child2Node = new ExpandedTreeNode(rootChild1Node, ROOT_CHILD1_CHILD2_NODE_USER_OBJECT, true); rootChild1Child2ChildNode = new ExpandedTreeNode(rootChild1Child2Node, ROOT_CHILD1_CHILD2_CHILD_NODE_USER_OBJECT); return new ITreeNode[] { rootNode }; } /** Removes the give node from its parent (if it has one). */ private void removeFromParent(final ITreeNode node) { final ITreeNode parent = node.getParent(); if (parent != null) { final List<ITreeNode> children = parent.getChildren(); children.remove(node); parent.setChildren(children); } } /** * Return the TreeItem corresponding to the following mock "index" scheme: * 0: item for node1, 1: item for node2, 2: item for node3, 3: item for * node4. * <p> * This method will fully expand the tree to ensure all tree items are * created. */ private TreeItem getUIControlItem(final int index) { getRidget().expandAll(); final Tree control = getWidget(); switch (index) { case 0: return control.getItem(0); case 1: return control.getItem(0).getItem(0); case 2: return control.getItem(0).getItem(0).getItem(0); case 3: return control.getItem(0).getItem(1); default: throw new IndexOutOfBoundsException("index= " + index); } } // helping classes ////////////////// /** * Tree node with an icon */ private static final class TreeNodeWithIcon extends TreeNode { public TreeNodeWithIcon(final ITreeNode pParent, final Object pValue) { super(pParent, pValue); } @SuppressWarnings("unused") public String getIcon() { return "eclipse.gif"; } } /** * Tree node with an expanded flag. */ public static final class ExpandedTreeNode extends TreeNode { public final static String PROPERTY_EXPANDED = "expanded"; //$NON-NLS-1$ private boolean expanded; public ExpandedTreeNode(final Object pValue) { super(pValue); setExpanded(true); } public ExpandedTreeNode(final ITreeNode pParent, final Object pValue) { this(pParent, pValue, true); } public ExpandedTreeNode(final ITreeNode pParent, final Object pValue, final boolean expanded) { super(pParent, pValue); setExpanded(expanded); } public boolean isExpanded() { return expanded; } public void setExpanded(final boolean expanded) { this.expanded = expanded; } } }