From 1bfbc942eb2d6670937f021c9703eab6c1d46b33 Mon Sep 17 00:00:00 2001
From: buczekp <buczekp>
Date: Tue, 24 May 2011 07:57:04 +0000
Subject: [PATCH] [LMS-2104] refactored VirtualHierarchicalContent to write
 more reliable tests

SVN: 21454
---
 .../common/io/VirtualHierarchicalContent.java | 125 ++++++--
 .../io/VirtualHierarchicalContentTest.java    | 171 ++++++++---
 .../cisd/common/io/VirtualNodeTest.java       | 269 ++++++++++++++++++
 3 files changed, 513 insertions(+), 52 deletions(-)
 create mode 100644 common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java

diff --git a/common/source/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContent.java b/common/source/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContent.java
index d36d8ba98a2..6cc44962d4e 100644
--- a/common/source/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContent.java
+++ b/common/source/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContent.java
@@ -26,6 +26,7 @@ import java.util.Map;
 
 import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
 import ch.systemsx.cisd.base.io.IRandomAccessFile;
+import ch.systemsx.cisd.common.collections.CollectionUtils;
 
 /**
  * Simple {@link IHierarchicalContent} implementation for virtual data sets with dynamic behavior
@@ -36,17 +37,40 @@ import ch.systemsx.cisd.base.io.IRandomAccessFile;
 class VirtualHierarchicalContent implements IHierarchicalContent
 {
 
+    final static IVirtualNodeMergerFactory DEFAULT_MERGER_FACTORY = new IVirtualNodeMergerFactory()
+        {
+            public IVirtualNodeMerger createNodeMerger()
+            {
+                return new VirtualNodeMerger(this);
+            }
+
+            public IVirtualNodeListMerger createNodeListMerger()
+            {
+                return new VirtualNodeListMerger(this);
+            }
+        };
+
+    private final IVirtualNodeMergerFactory mergerFactory;
+
     private final List<IHierarchicalContent> components;
 
     private IHierarchicalContentNode rootNode; // cached
 
-    public VirtualHierarchicalContent(List<IHierarchicalContent> components)
+    // for tests
+    VirtualHierarchicalContent(IVirtualNodeMergerFactory mergerFactory,
+            List<IHierarchicalContent> components)
     {
         if (components == null || components.isEmpty())
         {
             throw new IllegalArgumentException("Undefined contents");
         }
         this.components = components;
+        this.mergerFactory = mergerFactory;
+    }
+
+    public VirtualHierarchicalContent(List<IHierarchicalContent> components)
+    {
+        this(DEFAULT_MERGER_FACTORY, components);
     }
 
     public IHierarchicalContentNode getRootNode()
@@ -113,9 +137,51 @@ class VirtualHierarchicalContent implements IHierarchicalContent
         }
     }
 
+    //
+    // Object
+    //
+
+    @Override
+    public String toString()
+    {
+        return "VirtualHierarchicalContent [components="
+                + CollectionUtils.abbreviate(components, 10) + "]";
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        for (IHierarchicalContent component : components)
+        {
+            result = prime * result + component.hashCode();
+        }
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (obj == null)
+        {
+            return false;
+        }
+        if (!(obj instanceof VirtualHierarchicalContent))
+        {
+            return false;
+        }
+        VirtualHierarchicalContent other = (VirtualHierarchicalContent) obj;
+        return components.equals(other.components);
+    }
+
     private IHierarchicalContentNode mergeNodes(INodeProvider provider)
     {
-        IVirtualNodeMerger merger = createNodeMerger();
+        IVirtualNodeMerger merger = mergerFactory.createNodeMerger();
         for (IHierarchicalContent component : components)
         {
             IHierarchicalContentNode componentNode = provider.tryGetNode(component);
@@ -129,7 +195,7 @@ class VirtualHierarchicalContent implements IHierarchicalContent
 
     private List<IHierarchicalContentNode> mergeNodeLists(INodeListProvider listProvider)
     {
-        IVirtualNodeListMerger listMerger = createNodeListMerger();
+        IVirtualNodeListMerger listMerger = mergerFactory.createNodeListMerger();
         for (IHierarchicalContent component : components)
         {
             List<IHierarchicalContentNode> componentNodes = listProvider.getNodeList(component);
@@ -138,24 +204,25 @@ class VirtualHierarchicalContent implements IHierarchicalContent
         return listMerger.createMergedNodeList();
     }
 
-    private static IVirtualNodeMerger createNodeMerger()
+    private interface INodeProvider
     {
-        return new VirtualNodeMerger();
+        IHierarchicalContentNode tryGetNode(IHierarchicalContent content);
     }
 
-    private static IVirtualNodeListMerger createNodeListMerger()
+    private interface INodeListProvider
     {
-        return new VirtualNodeListMerger();
+        List<IHierarchicalContentNode> getNodeList(IHierarchicalContent content);
     }
 
-    interface INodeProvider
-    {
-        IHierarchicalContentNode tryGetNode(IHierarchicalContent content);
-    }
+    //
+    // NOTE: following interfaces and classes are exposed (package protected) only for testing
+    //
 
-    interface INodeListProvider
+    interface IVirtualNodeMergerFactory
     {
-        List<IHierarchicalContentNode> getNodeList(IHierarchicalContent content);
+        IVirtualNodeMerger createNodeMerger();
+
+        IVirtualNodeListMerger createNodeListMerger();
     }
 
     interface IVirtualNodeMerger
@@ -181,11 +248,18 @@ class VirtualHierarchicalContent implements IHierarchicalContent
      */
     static class VirtualNodeMerger implements IVirtualNodeMerger
     {
+        private final IVirtualNodeMergerFactory factory;
+
         // For convenience in iteration the order of these nodes is reversed.
         // It is the first node, not the last one, which is overriding all files of other nodes.
-        private LinkedList<IHierarchicalContentNode> nodes =
+        private final LinkedList<IHierarchicalContentNode> nodes =
                 new LinkedList<IHierarchicalContentNode>();
 
+        public VirtualNodeMerger(IVirtualNodeMergerFactory factory)
+        {
+            this.factory = factory;
+        }
+
         public void addNode(IHierarchicalContentNode node)
         {
             nodes.addFirst(node);
@@ -193,7 +267,7 @@ class VirtualHierarchicalContent implements IHierarchicalContent
 
         public IHierarchicalContentNode createMergedNode()
         {
-            return new VirtualNode(nodes);
+            return new VirtualNode(factory, nodes);
         }
     }
 
@@ -202,8 +276,16 @@ class VirtualHierarchicalContent implements IHierarchicalContent
      */
     static class VirtualNodeListMerger implements IVirtualNodeListMerger
     {
+        private final IVirtualNodeMergerFactory nodeMergerFactory;
+
         // relative path -> merger (with preserved order)
-        Map<String, IVirtualNodeMerger> mergers = new LinkedHashMap<String, IVirtualNodeMerger>();
+        private final Map<String, IVirtualNodeMerger> mergers =
+                new LinkedHashMap<String, IVirtualNodeMerger>();
+
+        public VirtualNodeListMerger(IVirtualNodeMergerFactory nodeMergerFactory)
+        {
+            this.nodeMergerFactory = nodeMergerFactory;
+        }
 
         public void addNodes(List<IHierarchicalContentNode> nodes)
         {
@@ -213,7 +295,7 @@ class VirtualHierarchicalContent implements IHierarchicalContent
                 IVirtualNodeMerger merger = mergers.get(relativePath);
                 if (merger == null)
                 {
-                    merger = createNodeMerger();
+                    merger = nodeMergerFactory.createNodeMerger();
                     mergers.put(relativePath, merger);
                 }
                 merger.addNode(node);
@@ -231,6 +313,7 @@ class VirtualHierarchicalContent implements IHierarchicalContent
         }
     }
 
+    // NOTE: exposed for tests
     /**
      * {@link IHierarchicalContentNode} implementation merging nodes with the same relative paths:
      * <ul>
@@ -241,14 +324,18 @@ class VirtualHierarchicalContent implements IHierarchicalContent
     static class VirtualNode implements IHierarchicalContentNode
     {
 
+        private final IVirtualNodeMergerFactory nodeMergerFactory;
+
         private final List<IHierarchicalContentNode> nodes;
 
-        public VirtualNode(List<IHierarchicalContentNode> nodes)
+        public VirtualNode(IVirtualNodeMergerFactory factory, List<IHierarchicalContentNode> nodes)
         {
+            assert nodes != null : "Undefined nodes.";
             if (nodes.isEmpty())
             {
                 throw new IllegalArgumentException("Resource doesn't exist.");
             }
+            this.nodeMergerFactory = factory;
             this.nodes = nodes;
         }
 
@@ -305,7 +392,7 @@ class VirtualHierarchicalContent implements IHierarchicalContent
 
         public List<IHierarchicalContentNode> getChildNodes() throws UnsupportedOperationException
         {
-            IVirtualNodeListMerger listMerger = createNodeListMerger();
+            IVirtualNodeListMerger listMerger = nodeMergerFactory.createNodeListMerger();
             for (IHierarchicalContentNode node : nodes)
             {
                 listMerger.addNodes(node.getChildNodes());
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContentTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContentTest.java
index 023c7c44062..8ad4711581f 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContentTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContentTest.java
@@ -16,45 +16,72 @@
 
 package ch.systemsx.cisd.common.io;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 import org.jmock.Expectations;
 import org.jmock.Mockery;
+import org.testng.AssertJUnit;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeListMerger;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeMerger;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeMergerFactory;
 
 /**
  * Unit tests for {@link VirtualHierarchicalContent}
  * 
  * @author Piotr Buczek
  */
-public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
+public class VirtualHierarchicalContentTest extends AssertJUnit
 {
 
-    private IHierarchicalContent[] components;
+    private IHierarchicalContent[] components; // real contents
 
     // mocks
 
     private Mockery context;
 
+    private IVirtualNodeMergerFactory mergerFactory;
+
+    private IVirtualNodeMerger nodeMerger;
+
+    private IVirtualNodeListMerger nodeListMerger;
+
     private IHierarchicalContent component1;
 
     private IHierarchicalContent component2;
 
     private IHierarchicalContent component3;
 
+    private IHierarchicalContentNode node1;
+
+    private IHierarchicalContentNode node2;
+
+    private IHierarchicalContentNode node3;
+
+    private IHierarchicalContentNode mergedNode;
+
     @BeforeMethod
     public void beforeMethod() throws Exception
     {
         context = new Mockery();
 
-        component1 = context.mock(IHierarchicalContent.class, "component 1");
-        component2 = context.mock(IHierarchicalContent.class, "component 2");
-        component3 = context.mock(IHierarchicalContent.class, "component 3");
+        mergerFactory = context.mock(IVirtualNodeMergerFactory.class);
+        nodeMerger = context.mock(IVirtualNodeMerger.class);
+        nodeListMerger = context.mock(IVirtualNodeListMerger.class);
+
+        component1 = context.mock(IHierarchicalContent.class, "component1");
+        component2 = context.mock(IHierarchicalContent.class, "component2");
+        component3 = context.mock(IHierarchicalContent.class, "component3");
+
+        node1 = context.mock(IHierarchicalContentNode.class, "node1");
+        node2 = context.mock(IHierarchicalContentNode.class, "node2");
+        node3 = context.mock(IHierarchicalContentNode.class, "node3");
+        mergedNode = context.mock(IHierarchicalContentNode.class, "mergedNode");
 
         components = new IHierarchicalContent[]
             { component1, component2, component3 };
@@ -68,9 +95,13 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
         context.assertIsSatisfied();
     }
 
+    //
+    // tests mocking IVirtualNodeMergerFactory
+    //
+
     private IHierarchicalContent createContent(IHierarchicalContent... contents)
     {
-        return new VirtualHierarchicalContent(Arrays.asList(contents));
+        return new VirtualHierarchicalContent(mergerFactory, Arrays.asList(contents));
     }
 
     @Test
@@ -78,7 +109,7 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
     {
         try
         {
-            createContent();
+            new VirtualHierarchicalContent(mergerFactory, null);
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex)
         {
@@ -87,7 +118,7 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
 
         try
         {
-            createContent(new IHierarchicalContent[0]);
+            new VirtualHierarchicalContent(mergerFactory, new ArrayList<IHierarchicalContent>());
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex)
         {
@@ -97,7 +128,28 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
         context.assertIsSatisfied();
     }
 
-    // TODO public void testEqualsAndHashCode()
+    @Test
+    public void testEqualsAndHashCode()
+    {
+        IHierarchicalContent virtualContent = createContent(components);
+        IHierarchicalContent virtualContentSameComponents = createContent(components.clone());
+        assertEquals(virtualContent, virtualContentSameComponents);
+        assertEquals(virtualContent.hashCode(), virtualContentSameComponents.hashCode());
+
+        IHierarchicalContent[] subComponents =
+            { component1, component2 };
+        IHierarchicalContent virtualContentSubComponents = createContent(subComponents);
+        assertFalse(virtualContent.equals(virtualContentSubComponents));
+        assertFalse(virtualContent.hashCode() == virtualContentSubComponents.hashCode());
+
+        IHierarchicalContent[] reorderedComponents = new IHierarchicalContent[]
+            { component1, component3, component2 };
+        IHierarchicalContent virtualContentReorderedComponents = createContent(reorderedComponents);
+        assertFalse(virtualContent.equals(virtualContentReorderedComponents));
+        assertFalse(virtualContent.hashCode() == virtualContentReorderedComponents.hashCode());
+
+        context.assertIsSatisfied();
+    }
 
     @Test
     public void testClose()
@@ -126,18 +178,29 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
         context.checking(new Expectations()
             {
                 {
-                    for (IHierarchicalContent component : components)
-                    {
-                        one(component).getRootNode();
-                    }
+                    one(mergerFactory).createNodeMerger();
+                    will(returnValue(nodeMerger));
+
+                    one(component1).getRootNode();
+                    will(returnValue(node1));
+                    one(component2).getRootNode();
+                    will(returnValue(node2));
+                    // component3 will not be added to merged nodes
+                    one(component3).getRootNode();
+                    will(returnValue(null)); // no exception expected
+
+                    one(nodeMerger).addNode(node1);
+                    one(nodeMerger).addNode(node2);
+                    one(nodeMerger).createMergedNode();
+                    will(returnValue(mergedNode));
                 }
             });
 
-        IHierarchicalContentNode root1 = virtualContent.getRootNode();
-        // 2nd call uses cache (doesn't invoke getRootNode() on components)
-        IHierarchicalContentNode root2 = virtualContent.getRootNode();
-        assertSame(root1, root2);
-        // TODO merging & handling exceptions not tested
+        IHierarchicalContentNode virtualRoot = virtualContent.getRootNode();
+        // 2nd call uses cache (no method in expectation should be invoked twice)
+        IHierarchicalContentNode virtualRoot2 = virtualContent.getRootNode();
+        assertSame(virtualRoot, virtualRoot2);
+        assertSame(mergedNode, virtualRoot);
 
         context.assertIsSatisfied();
     }
@@ -151,16 +214,26 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
         context.checking(new Expectations()
             {
                 {
+                    one(mergerFactory).createNodeMerger();
+                    will(returnValue(nodeMerger));
+
                     one(component1).getNode(relativePath);
+                    will(returnValue(node1));
                     one(component2).getNode(relativePath);
+                    will(returnValue(node2));
+                    // component3 will not be added to merged nodes
                     one(component3).getNode(relativePath);
                     will(throwException(new IllegalArgumentException("")));
+
+                    one(nodeMerger).addNode(node1);
+                    one(nodeMerger).addNode(node2);
+                    one(nodeMerger).createMergedNode();
+                    will(returnValue(mergedNode));
                 }
             });
 
         IHierarchicalContentNode node = virtualContent.getNode(relativePath);
-        assertNotNull(node);
-        // TODO merging & handling exceptions not tested
+        assertSame(mergedNode, node);
 
         context.assertIsSatisfied();
     }
@@ -171,19 +244,35 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
         final IHierarchicalContent virtualContent = createContent(components);
         final String pattern = "rel.*path.?pattern";
 
+        // contents of these lists is not significang in the test
+        final List<IHierarchicalContentNode> list1 = Arrays.asList(node1, node2);
+        final List<IHierarchicalContentNode> list2 = Arrays.asList(node3);
+        final List<IHierarchicalContentNode> list3 = Arrays.asList();
+        final List<IHierarchicalContentNode> mergedNodeList = Arrays.asList(mergedNode);
+
         context.checking(new Expectations()
             {
                 {
-                    for (IHierarchicalContent component : components)
-                    {
-                        one(component).listMatchingNodes(pattern);
-                    }
+                    one(mergerFactory).createNodeListMerger();
+                    will(returnValue(nodeListMerger));
+
+                    one(component1).listMatchingNodes(pattern);
+                    will(returnValue(list1));
+                    one(component2).listMatchingNodes(pattern);
+                    will(returnValue(list2));
+                    one(component3).listMatchingNodes(pattern);
+                    will(returnValue(list3));
+
+                    one(nodeListMerger).addNodes(list1);
+                    one(nodeListMerger).addNodes(list2);
+                    one(nodeListMerger).addNodes(list3);
+                    one(nodeListMerger).createMergedNodeList();
+                    will(returnValue(mergedNodeList));
                 }
             });
 
         List<IHierarchicalContentNode> nodeList = virtualContent.listMatchingNodes(pattern);
-        assertNotNull(nodeList);
-        // TODO merging & handling exceptions not tested
+        assertSame(mergedNodeList, nodeList);
 
         context.assertIsSatisfied();
     }
@@ -195,20 +284,36 @@ public class VirtualHierarchicalContentTest extends AbstractFileSystemTestCase
         final String startingPath = "some/dir";
         final String pattern = "file.*name.?pattern";
 
+        // contents of these lists is not important in the test
+        final List<IHierarchicalContentNode> list1 = Arrays.asList(node1, node2);
+        final List<IHierarchicalContentNode> list2 = Arrays.asList(node3);
+        final List<IHierarchicalContentNode> list3 = Arrays.asList();
+        final List<IHierarchicalContentNode> mergedNodeList = Arrays.asList(mergedNode);
+
         context.checking(new Expectations()
             {
                 {
-                    for (IHierarchicalContent component : components)
-                    {
-                        one(component).listMatchingNodes(startingPath, pattern);
-                    }
+                    one(mergerFactory).createNodeListMerger();
+                    will(returnValue(nodeListMerger));
+
+                    one(component1).listMatchingNodes(startingPath, pattern);
+                    will(returnValue(list1));
+                    one(component2).listMatchingNodes(startingPath, pattern);
+                    will(returnValue(list2));
+                    one(component3).listMatchingNodes(startingPath, pattern);
+                    will(returnValue(list3));
+
+                    one(nodeListMerger).addNodes(list1);
+                    one(nodeListMerger).addNodes(list2);
+                    one(nodeListMerger).addNodes(list3);
+                    one(nodeListMerger).createMergedNodeList();
+                    will(returnValue(mergedNodeList));
                 }
             });
 
         List<IHierarchicalContentNode> nodeList =
                 virtualContent.listMatchingNodes(startingPath, pattern);
-        assertNotNull(nodeList);
-        // TODO merging & handling exceptions not tested
+        assertSame(mergedNodeList, nodeList);
 
         context.assertIsSatisfied();
     }
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java
new file mode 100644
index 00000000000..6f6be3fac76
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.io;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeListMerger;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeMergerFactory;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.VirtualNode;
+
+/**
+ * Unit tests for {@link VirtualNode}
+ * 
+ * @author Piotr Buczek
+ */
+public class VirtualNodeTest extends AssertJUnit
+{
+
+    private List<IHierarchicalContentNode> nodes; // real nodes
+
+    // mocks
+
+    private Mockery context;
+
+    private IVirtualNodeMergerFactory mergerFactory;
+
+    @SuppressWarnings("unused")
+    private IVirtualNodeListMerger nodeListMerger;
+
+    private IHierarchicalContentNode node1;
+
+    private IHierarchicalContentNode node2;
+
+    private IHierarchicalContentNode node3;
+
+    @SuppressWarnings("unused")
+    private IHierarchicalContentNode mergedNode;
+
+    @BeforeMethod
+    public void beforeMethod() throws Exception
+    {
+        context = new Mockery();
+
+        mergerFactory = context.mock(IVirtualNodeMergerFactory.class);
+        nodeListMerger = context.mock(IVirtualNodeListMerger.class);
+
+        node1 = context.mock(IHierarchicalContentNode.class, "node1");
+        node2 = context.mock(IHierarchicalContentNode.class, "node2");
+        node3 = context.mock(IHierarchicalContentNode.class, "node3");
+        mergedNode = context.mock(IHierarchicalContentNode.class, "mergedNode");
+
+        nodes = Arrays.asList(node1, node2, node3);
+    }
+
+    @AfterMethod
+    public void tearDown()
+    {
+        // To following line of code should also be called at the end of each test method.
+        // Otherwise one do not known which test failed.
+        context.assertIsSatisfied();
+    }
+
+    //
+    // tests mocking IVirtualNodeMergerFactory
+    //
+
+    private IHierarchicalContentNode createVirtualNode()
+    {
+        return new VirtualNode(mergerFactory, nodes);
+    }
+
+    @Test
+    public void testFailWithNullOrEmptyNodes()
+    {
+        try
+        {
+            new VirtualNode(mergerFactory, null);
+            fail("Expected AssertionError");
+        } catch (AssertionError ex)
+        {
+            assertEquals("Undefined nodes.", ex.getMessage());
+        }
+
+        try
+        {
+            new VirtualNode(mergerFactory, new ArrayList<IHierarchicalContentNode>());
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException ex)
+        {
+            assertEquals("Resource doesn't exist.", ex.getMessage());
+        }
+
+        context.assertIsSatisfied();
+    }
+
+    // @Test
+    // public void testEqualsAndHashCode()
+    // {
+    // IHierarchicalContent virtualContent = createContent(components);
+    // IHierarchicalContent virtualContentSameComponents = createContent(components.clone());
+    // assertEquals(virtualContent, virtualContentSameComponents);
+    // assertEquals(virtualContent.hashCode(), virtualContentSameComponents.hashCode());
+    //
+    // IHierarchicalContent[] subComponents =
+    // { component1, component2 };
+    // IHierarchicalContent virtualContentSubComponents = createContent(subComponents);
+    // assertFalse(virtualContent.equals(virtualContentSubComponents));
+    // assertFalse(virtualContent.hashCode() == virtualContentSubComponents.hashCode());
+    //
+    // IHierarchicalContent[] reorderedComponents = new IHierarchicalContent[]
+    // { component1, component3, component2 };
+    // IHierarchicalContent virtualContentReorderedComponents = createContent(reorderedComponents);
+    // assertFalse(virtualContent.equals(virtualContentReorderedComponents));
+    // assertFalse(virtualContent.hashCode() == virtualContentReorderedComponents.hashCode());
+    //
+    // context.assertIsSatisfied();
+    // }
+
+    @Test
+    public void testGetName()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final String nodeName = "some name";
+
+        context.checking(new Expectations()
+            {
+                {
+                    // first nodes name is taken
+                    one(node1).getName();
+                    will(returnValue(nodeName));
+                }
+            });
+
+        String virtualName = virtualNode.getName();
+        assertEquals(nodeName, virtualName);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetRelativePath()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final String relativePath = "relative/path";
+
+        context.checking(new Expectations()
+            {
+                {
+                    // first nodes path is taken
+                    one(node1).getRelativePath();
+                    will(returnValue(relativePath));
+                }
+            });
+
+        String virtualRelativePath = virtualNode.getRelativePath();
+        assertEquals(relativePath, virtualRelativePath);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetParentRelativePath()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final String parentRelativePath = "parent/relative/path";
+
+        context.checking(new Expectations()
+            {
+                {
+                    // first nodes path is taken
+                    one(node1).getParentRelativePath();
+                    will(returnValue(parentRelativePath));
+                }
+            });
+
+        String virtualParentRelativePath = virtualNode.getParentRelativePath();
+        assertEquals(parentRelativePath, virtualParentRelativePath);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testExists()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        // contract: at least one node needs to exist for virtual node to exist
+
+        // 1st case: 2nd out of 3 nodes exist, 3rd node is not asked at all
+        context.checking(new Expectations()
+            {
+                {
+                    one(node1).exists();
+                    will(returnValue(false));
+
+                    one(node2).exists();
+                    will(returnValue(true));
+                }
+            });
+        assertTrue(virtualNode.exists());
+
+        // 2st case: all nodes don't exist
+        context.checking(new Expectations()
+            {
+                {
+                    for (IHierarchicalContentNode node : nodes)
+                    {
+                        one(node).exists();
+                        will(returnValue(false));
+                    }
+                }
+            });
+        assertFalse(virtualNode.exists());
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testIsDirectory()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        context.checking(new Expectations()
+            {
+                {
+                    // first node is asked twice giving different answer
+                    one(node1).isDirectory();
+                    will(returnValue(true));
+
+                    one(node1).isDirectory();
+                    will(returnValue(false));
+                }
+            });
+
+        // check twice with different answer from mocked node
+        assertTrue(virtualNode.isDirectory());
+        assertFalse(virtualNode.isDirectory());
+
+        context.assertIsSatisfied();
+    }
+
+    // TODO 2011-05-24, Piotr Buczek: write remaining tests
+}
-- 
GitLab