From 7cfd6016908506256eb746e00ff5d65d3d8a8ef9 Mon Sep 17 00:00:00 2001
From: buczekp <buczekp>
Date: Wed, 25 May 2011 20:28:14 +0000
Subject: [PATCH] [LMS-2104] more tests of virtual data sets

SVN: 21476
---
 .../common/io/VirtualHierarchicalContent.java |  45 ++-
 .../io/VirtualHierarchicalContentTest.java    |   2 +-
 .../common/io/VirtualNodeMergingTest.java     | 212 ++++++++++
 .../cisd/common/io/VirtualNodeTest.java       | 366 ++++++++++++++++--
 ...hInfoProviderBasedHierarchicalContent.java |   2 +-
 5 files changed, 583 insertions(+), 44 deletions(-)
 create mode 100644 common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeMergingTest.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 142dcac332d..ce4300bc420 100644
--- a/common/source/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContent.java
+++ b/common/source/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContent.java
@@ -353,7 +353,8 @@ class VirtualHierarchicalContent implements IHierarchicalContent
                     return node;
                 }
             }
-            throw new IllegalStateException("Resource is unavailable.");
+            throw new IllegalStateException(
+                    "Resource is currently unavailable. It might be in an archive.");
         }
 
         public String getName()
@@ -436,6 +437,48 @@ class VirtualHierarchicalContent implements IHierarchicalContent
             return lastExistingNode().getInputStream();
         }
 
+        //
+        // Object
+        //
+
+        @Override
+        public String toString()
+        {
+            return "VirtualNode [nodes="
+                    + Arrays.toString(nodes.toArray(new IHierarchicalContentNode[0])) + "]";
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (this == obj)
+            {
+                return true;
+            }
+            if (obj == null)
+            {
+                return false;
+            }
+            if (!(obj instanceof VirtualNode))
+            {
+                return false;
+            }
+            VirtualNode other = (VirtualNode) obj;
+            return nodes.equals(other.nodes);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            final int prime = 31;
+            int result = 1;
+            for (IHierarchicalContentNode node : nodes)
+            {
+                result = prime * result + node.hashCode();
+            }
+            return result;
+        }
+
     }
 
 }
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 8ad4711581f..d30461ca164 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContentTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualHierarchicalContentTest.java
@@ -244,7 +244,7 @@ public class VirtualHierarchicalContentTest extends AssertJUnit
         final IHierarchicalContent virtualContent = createContent(components);
         final String pattern = "rel.*path.?pattern";
 
-        // contents of these lists is not significang in the test
+        // contents of these lists is not significant in the test
         final List<IHierarchicalContentNode> list1 = Arrays.asList(node1, node2);
         final List<IHierarchicalContentNode> list2 = Arrays.asList(node3);
         final List<IHierarchicalContentNode> list3 = Arrays.asList();
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeMergingTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeMergingTest.java
new file mode 100644
index 00000000000..9c35a464d75
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeMergingTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+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.IVirtualNodeMerger;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeMergerFactory;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.VirtualNodeListMerger;
+import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.VirtualNodeMerger;
+
+/**
+ * Unit tests for {@link VirtualNodeMerger} and {@link VirtualNodeListMerger}.
+ * 
+ * @author Piotr Buczek
+ */
+public class VirtualNodeMergingTest extends AssertJUnit
+{
+
+    // mocks
+
+    private Mockery context;
+
+    private IVirtualNodeMergerFactory mergerFactory;
+
+    @BeforeMethod
+    public void beforeMethod() throws Exception
+    {
+        context = new Mockery();
+
+        mergerFactory = context.mock(IVirtualNodeMergerFactory.class);
+
+    }
+
+    @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 IVirtualNodeMerger createNodeMerger()
+    {
+        return new VirtualNodeMerger(mergerFactory);
+    }
+
+    private IVirtualNodeListMerger createNodeListMerger()
+    {
+        return new VirtualNodeListMerger(mergerFactory);
+    }
+
+    private IVirtualNodeMerger createNodeMergerMock(String mockName)
+    {
+        return context.mock(IVirtualNodeMerger.class, mockName);
+    }
+
+    private IHierarchicalContentNode createNodeMock(String mockName)
+    {
+        return context.mock(IHierarchicalContentNode.class, mockName);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testCreateEmptyNodeFails()
+    {
+        final IVirtualNodeMerger merger = createNodeMerger();
+
+        merger.createMergedNode();
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testCreateNode()
+    {
+        final IVirtualNodeMerger merger = createNodeMerger();
+
+        merger.addNode(createNodeMock("node1"));
+        merger.addNode(createNodeMock("node2"));
+        merger.addNode(createNodeMock("node3"));
+
+        IHierarchicalContentNode mergedNode = merger.createMergedNode();
+        // virtual node has the nodes in reversed order to make the first one the most important
+        assertEquals("VirtualNode [nodes=[node3, node2, node1]]", mergedNode.toString());
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testCreateNodeList()
+    {
+        final IVirtualNodeListMerger listMerger = createNodeListMerger();
+
+        // add list1 with 2 nodes (with new relative paths)
+        final String path1 = "some/path1";
+        final String path2 = "some/path2";
+        final IHierarchicalContentNode node1Path1 = createNodeMock("node1Path1");
+        final IHierarchicalContentNode node1Path2 = createNodeMock("node1Path2");
+        final IVirtualNodeMerger nodeMergerPath1 = createNodeMergerMock("mergerPath1");
+        final IVirtualNodeMerger nodeMergerPath2 = createNodeMergerMock("mergerPath2");
+        prepareAddNodeWithNewPath(node1Path1, path1, nodeMergerPath1);
+        prepareAddNodeWithNewPath(node1Path2, path2, nodeMergerPath2);
+        listMerger.addNodes(Arrays.asList(node1Path1, node1Path2));
+
+        // add list2 with 2 nodes with new relative paths
+        final String path3 = "some/path3";
+        final String path4 = "some/path4";
+        final IHierarchicalContentNode node2Path3 = createNodeMock("node2Path3");
+        final IHierarchicalContentNode node2Path4 = createNodeMock("node2Path4");
+        final IVirtualNodeMerger nodeMergerPath3 = createNodeMergerMock("mergerPath3");
+        final IVirtualNodeMerger nodeMergerPath4 = createNodeMergerMock("mergerPath4");
+        prepareAddNodeWithNewPath(node2Path3, path3, nodeMergerPath3);
+        prepareAddNodeWithNewPath(node2Path4, path4, nodeMergerPath4);
+        listMerger.addNodes(Arrays.asList(node2Path3, node2Path4));
+
+        // add list3 with 3 nodes, 2 with same relative paths as in previous lists and 1 new path
+        final String path5 = "some/path5";
+        final IHierarchicalContentNode node3Path1 = createNodeMock("node3Path1");
+        final IHierarchicalContentNode node3Path3 = createNodeMock("node3Path3");
+        final IHierarchicalContentNode node3Path5 = createNodeMock("node3Path5");
+        final IVirtualNodeMerger nodeMergerPath5 = createNodeMergerMock("mergerPath5");
+        // path1 used already in list1
+        prepareAddNodeWithExistingPath(node3Path1, path1, nodeMergerPath1);
+        // path3 used already in list2
+        prepareAddNodeWithExistingPath(node3Path3, path3, nodeMergerPath3);
+        // path5 wasn't yet used - new merger should be created
+        prepareAddNodeWithNewPath(node3Path5, path5, nodeMergerPath5);
+        listMerger.addNodes(Arrays.asList(node3Path1, node3Path3, node3Path5));
+
+        // no work is expected when adding an empty list
+        listMerger.addNodes(Collections.<IHierarchicalContentNode> emptyList());
+
+        context.checking(new Expectations()
+            {
+                {
+                    createMergedNode(nodeMergerPath1, "mergedNode1");
+                    createMergedNode(nodeMergerPath2, "mergedNode2");
+                    createMergedNode(nodeMergerPath3, "mergedNode3");
+                    createMergedNode(nodeMergerPath4, "mergedNode4");
+                    createMergedNode(nodeMergerPath5, "mergedNode5");
+                }
+
+                private void createMergedNode(final IVirtualNodeMerger merger, String nodeName)
+                {
+                    final IHierarchicalContentNode mergedNode = createNodeMock(nodeName);
+                    one(merger).createMergedNode();
+                    will(returnValue(mergedNode));
+                }
+            });
+        List<IHierarchicalContentNode> mergedNode = listMerger.createMergedNodeList();
+        assertEquals("[mergedNode1, mergedNode2, mergedNode3, mergedNode4, mergedNode5]",
+                mergedNode.toString());
+
+        context.assertIsSatisfied();
+    }
+
+    private void prepareAddNodeWithNewPath(final IHierarchicalContentNode node, final String path,
+            final IVirtualNodeMerger merger)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(node).getRelativePath();
+                    will(returnValue(path));
+                    one(mergerFactory).createNodeMerger();
+                    will(returnValue(merger));
+                    one(merger).addNode(node);
+                }
+            });
+    }
+
+    private void prepareAddNodeWithExistingPath(final IHierarchicalContentNode node,
+            final String path, final IVirtualNodeMerger merger)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(node).getRelativePath();
+                    will(returnValue(path));
+                    one(merger).addNode(node);
+                }
+            });
+    }
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java
index 6f6be3fac76..011eff97ee0 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/io/VirtualNodeTest.java
@@ -16,27 +16,34 @@
 
 package ch.systemsx.cisd.common.io;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 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.io.IRandomAccessFile;
+import ch.systemsx.cisd.base.io.RandomAccessFileImpl;
+import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
 import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeListMerger;
 import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.IVirtualNodeMergerFactory;
 import ch.systemsx.cisd.common.io.VirtualHierarchicalContent.VirtualNode;
+import ch.systemsx.cisd.common.utilities.IDelegatedAction;
 
 /**
  * Unit tests for {@link VirtualNode}
  * 
  * @author Piotr Buczek
  */
-public class VirtualNodeTest extends AssertJUnit
+public class VirtualNodeTest extends AbstractFileSystemTestCase
 {
 
     private List<IHierarchicalContentNode> nodes; // real nodes
@@ -47,7 +54,6 @@ public class VirtualNodeTest extends AssertJUnit
 
     private IVirtualNodeMergerFactory mergerFactory;
 
-    @SuppressWarnings("unused")
     private IVirtualNodeListMerger nodeListMerger;
 
     private IHierarchicalContentNode node1;
@@ -56,9 +62,10 @@ public class VirtualNodeTest extends AssertJUnit
 
     private IHierarchicalContentNode node3;
 
-    @SuppressWarnings("unused")
     private IHierarchicalContentNode mergedNode;
 
+    private File dummyFile;
+
     @BeforeMethod
     public void beforeMethod() throws Exception
     {
@@ -73,6 +80,9 @@ public class VirtualNodeTest extends AssertJUnit
         mergedNode = context.mock(IHierarchicalContentNode.class, "mergedNode");
 
         nodes = Arrays.asList(node1, node2, node3);
+
+        dummyFile = new File(workingDirectory, "mergedFile");
+        dummyFile.createNewFile();
     }
 
     @AfterMethod
@@ -87,9 +97,14 @@ public class VirtualNodeTest extends AssertJUnit
     // tests mocking IVirtualNodeMergerFactory
     //
 
+    private IHierarchicalContentNode createVirtualNode(List<IHierarchicalContentNode> nodeList)
+    {
+        return new VirtualNode(mergerFactory, nodeList);
+    }
+
     private IHierarchicalContentNode createVirtualNode()
     {
-        return new VirtualNode(mergerFactory, nodes);
+        return createVirtualNode(nodes);
     }
 
     @Test
@@ -97,7 +112,7 @@ public class VirtualNodeTest extends AssertJUnit
     {
         try
         {
-            new VirtualNode(mergerFactory, null);
+            createVirtualNode(null);
             fail("Expected AssertionError");
         } catch (AssertionError ex)
         {
@@ -106,7 +121,7 @@ public class VirtualNodeTest extends AssertJUnit
 
         try
         {
-            new VirtualNode(mergerFactory, new ArrayList<IHierarchicalContentNode>());
+            createVirtualNode(new ArrayList<IHierarchicalContentNode>());
             fail("Expected IllegalArgumentException");
         } catch (IllegalArgumentException ex)
         {
@@ -116,28 +131,30 @@ public class VirtualNodeTest extends AssertJUnit
         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 testEqualsAndHashCode()
+    {
+        IHierarchicalContentNode virtualNode = createVirtualNode();
+        IHierarchicalContentNode virtualNodeWithSameComponents = createVirtualNode();
+        IHierarchicalContentNode virtualNodeWithSameComponents2 =
+                createVirtualNode(new ArrayList<IHierarchicalContentNode>(nodes));
+        assertEquals(virtualNode, virtualNodeWithSameComponents);
+        assertEquals(virtualNode, virtualNodeWithSameComponents2);
+        assertEquals(virtualNode.hashCode(), virtualNodeWithSameComponents.hashCode());
+        assertEquals(virtualNode.hashCode(), virtualNodeWithSameComponents2.hashCode());
+
+        List<IHierarchicalContentNode> subNodes = Arrays.asList(node1, node2);
+        IHierarchicalContentNode virtualNodeSubComponents = createVirtualNode(subNodes);
+        assertFalse(virtualNode.equals(virtualNodeSubComponents));
+        assertFalse(virtualNode.hashCode() == virtualNodeSubComponents.hashCode());
+
+        List<IHierarchicalContentNode> reorderedNodes = Arrays.asList(node1, node3, node2);
+        IHierarchicalContentNode virtualNodeReorderedComponents = createVirtualNode(reorderedNodes);
+        assertFalse(virtualNode.equals(virtualNodeReorderedComponents));
+        assertFalse(virtualNode.hashCode() == virtualNodeReorderedComponents.hashCode());
+
+        context.assertIsSatisfied();
+    }
 
     @Test
     public void testGetName()
@@ -225,17 +242,7 @@ public class VirtualNodeTest extends AssertJUnit
             });
         assertTrue(virtualNode.exists());
 
-        // 2st case: all nodes don't exist
-        context.checking(new Expectations()
-            {
-                {
-                    for (IHierarchicalContentNode node : nodes)
-                    {
-                        one(node).exists();
-                        will(returnValue(false));
-                    }
-                }
-            });
+        prepareAllNodesNotExist();
         assertFalse(virtualNode.exists());
 
         context.assertIsSatisfied();
@@ -265,5 +272,282 @@ public class VirtualNodeTest extends AssertJUnit
         context.assertIsSatisfied();
     }
 
-    // TODO 2011-05-24, Piotr Buczek: write remaining tests
+    @Test
+    public void testGetChildNodes()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        // contents of these lists is not significant in the test
+        final List<IHierarchicalContentNode> children1 = Arrays.asList(node1, node2);
+        final List<IHierarchicalContentNode> children2 = Arrays.asList(node3);
+        final List<IHierarchicalContentNode> children3 = Arrays.asList();
+        final List<IHierarchicalContentNode> mergedChildren = Arrays.asList(mergedNode);
+
+        context.checking(new Expectations()
+            {
+                {
+                    one(mergerFactory).createNodeListMerger();
+                    will(returnValue(nodeListMerger));
+
+                    one(node1).getChildNodes();
+                    will(returnValue(children1));
+                    one(node2).getChildNodes();
+                    will(returnValue(children2));
+                    one(node3).getChildNodes();
+                    will(returnValue(children3));
+
+                    one(nodeListMerger).addNodes(children1);
+                    one(nodeListMerger).addNodes(children2);
+                    one(nodeListMerger).addNodes(children3);
+                    one(nodeListMerger).createMergedNodeList();
+                    will(returnValue(mergedChildren));
+
+                }
+            });
+
+        List<IHierarchicalContentNode> virtualChildren = virtualNode.getChildNodes();
+        assertEquals(mergedChildren, virtualChildren);
+
+        context.assertIsSatisfied();
+    }
+
+    //
+    // contract: file (and its content) from last non-virtual child that exists is taken
+    // NOTE: the order of nodes in VirtualNode is reversed (first becomes last)
+    //
+
+    @Test
+    public void testGetFile()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        // 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));
+
+                    one(node2).getFile();
+                    will(returnValue(dummyFile));
+                }
+            });
+
+        File virtualFile = virtualNode.getFile();
+        assertSame(dummyFile, virtualFile);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetNormalFileLength()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final long mergedFileSize = 100;
+
+        // case: 2nd out of 3 nodes exist, 3rd node is not asked at all
+        // take lenght of the 2nd file
+        context.checking(new Expectations()
+            {
+                {
+                    one(node1).isDirectory();
+                    will(returnValue(false)); // normal file
+
+                    one(node1).exists();
+                    will(returnValue(false));
+
+                    one(node2).exists();
+                    will(returnValue(true));
+
+                    one(node2).getFileLength();
+                    will(returnValue(mergedFileSize));
+                }
+            });
+
+        long virtualLength = virtualNode.getFileLength();
+        assertSame(mergedFileSize, virtualLength);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetDirectoryFileLength()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final long nodeLength1 = 11;
+        final long nodeLength2 = 22;
+        final long nodeLength3 = 33;
+
+        context.checking(new Expectations()
+            {
+                {
+                    one(node1).isDirectory();
+                    will(returnValue(true)); // directory file
+
+                    one(node1).getFileLength();
+                    will(returnValue(nodeLength1));
+
+                    one(node2).getFileLength();
+                    will(returnValue(nodeLength2));
+
+                    one(node3).getFileLength();
+                    will(returnValue(nodeLength3));
+                }
+            });
+
+        long virtualLength = virtualNode.getFileLength();
+        // contract: for directories return estimated length == sum of all node lengths
+        assertSame(nodeLength1 + nodeLength2 + nodeLength3, virtualLength);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetInputStream() throws IOException
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final InputStream dummyInputStream = new FileInputStream(dummyFile);
+
+        // 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));
+
+                    one(node2).getInputStream();
+                    will(returnValue(dummyInputStream));
+                }
+            });
+
+        InputStream virtualInputStream = virtualNode.getInputStream();
+        assertSame(dummyInputStream, virtualInputStream);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetFileContent() throws IOException
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        final IRandomAccessFile dummyRandomAccessFile = new RandomAccessFileImpl(dummyFile, "r");
+
+        // 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));
+
+                    one(node2).getFileContent();
+                    will(returnValue(dummyRandomAccessFile));
+                }
+            });
+
+        IRandomAccessFile virtualRandomAccessFile = virtualNode.getFileContent();
+        assertSame(dummyRandomAccessFile, virtualRandomAccessFile);
+
+        context.assertIsSatisfied();
+    }
+
+    //
+    // contract: if all nodes don't exist an exception should be thrown when one tries to access
+    // file or its content
+    //
+
+    @Test
+    public void testGetFileFailsWithResourceUnavailable()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        prepareAllNodesNotExist();
+        expectResouceUnavailableException(new IDelegatedAction()
+            {
+                public void execute()
+                {
+                    virtualNode.getFile();
+                }
+            });
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetFileContentFailsWithResourceUnavailable()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        prepareAllNodesNotExist();
+        expectResouceUnavailableException(new IDelegatedAction()
+            {
+                public void execute()
+                {
+                    virtualNode.getFileContent();
+                }
+            });
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testGetInputStreamFailsWithResourceUnavailable()
+    {
+        final IHierarchicalContentNode virtualNode = createVirtualNode();
+
+        prepareAllNodesNotExist();
+        expectResouceUnavailableException(new IDelegatedAction()
+            {
+                public void execute()
+                {
+                    virtualNode.getInputStream();
+                }
+            });
+
+        context.assertIsSatisfied();
+    }
+
+    //
+    // helper functions
+    //
+
+    private void prepareAllNodesNotExist()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    for (IHierarchicalContentNode node : nodes)
+                    {
+                        one(node).exists();
+                        will(returnValue(false));
+                    }
+                }
+            });
+    }
+
+    private static void expectResouceUnavailableException(final IDelegatedAction action)
+    {
+        try
+        {
+            action.execute();
+            fail("Expected IllegalStateException");
+        } catch (IllegalStateException ex)
+        {
+            assertEquals("Resource is currently unavailable. It might be in an archive.",
+                    ex.getMessage());
+        }
+    }
+
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/content/PathInfoProviderBasedHierarchicalContent.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/content/PathInfoProviderBasedHierarchicalContent.java
index fa54a22565f..b83358d9301 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/content/PathInfoProviderBasedHierarchicalContent.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/content/PathInfoProviderBasedHierarchicalContent.java
@@ -305,7 +305,7 @@ class PathInfoProviderBasedHierarchicalContent implements IHierarchicalContent
             return asFileContentProvider(node);
         }
         throw new IllegalArgumentException("Resource '" + FileUtilities.getRelativeFile(root, file)
-                + "' is currently not available. It might be in an archive.");
+                + "' is currently unavailable. It might be in an archive.");
     }
 
     private interface IFileContentProvider
-- 
GitLab