diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java
new file mode 100644
index 0000000000000000000000000000000000000000..50118fc9daeaae0fd2f341bbddb667e09f442af0
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/IDataSetMover.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 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.etlserver.plugins;
+
+import java.io.File;
+
+
+/**
+ * Strategy of moving a data set to another share.
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IDataSetMover
+{
+    /**
+     * Moves the specified data set to the specified share. The data set is folder in the store
+     * its name is the data set code. The destination folder is <code>share</code>. Its name is
+     * the share id. Share id and size will be updated on openBIS.
+     */
+    public void moveDataSetToAnotherShare(File dataSetDirInStore, File share);
+}
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java
index 731c6a61c473f1a6446199ff90cb12dd9ecbf03a..faaef689d9c58c3e23520e50005564da5ca65e4b 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/ISegmentedStoreBalancer.java
@@ -23,12 +23,18 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share;
 
 /**
- * 
+ * Strategy of balancing data sets between shares in segmented store.
  *
  * @author Franz-Josef Elmer
  */
 public interface ISegmentedStoreBalancer
 {
+    /**
+     * Balances sizes of specified shares by moving data sets between them using specified data set mover. 
+     * 
+     * @param service
+     * 
+     */
     public void balanceStore(List<Share> shares, IEncapsulatedOpenBISService service,
-            ISimpleLogger logger);
+            IDataSetMover dataSetMover, ISimpleLogger logger);
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java
index f983dfad13d47de0931571d62d5f1b30c067550f..fd16a31c2271bc4fa9548f63e0a10d408edb1b1b 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/NonBalancer.java
@@ -39,21 +39,26 @@ public class NonBalancer implements ISegmentedStoreBalancer
     private static final int N = 3;
 
     public void balanceStore(List<Share> shares, IEncapsulatedOpenBISService service,
-            ISimpleLogger logger)
+            IDataSetMover dataSetMover, ISimpleLogger logger)
     {
         logger.log(INFO, "Data Store Shares:");
         for (Share share : shares)
         {
             List<SimpleDataSetInformationDTO> dataSets = share.getDataSetsOrderedBySize();
-            logger.log(INFO, "   Share " + share.getShareId() + " (free space: "
-                    + FileUtils.byteCountToDisplaySize(share.calculateFreeSpace()) + ") has "
-                    + dataSets.size() + " data sets occupying "
-                    + FileUtilities.byteCountToDisplaySize(share.getTotalSizeOfDataSets()) + ".");
+            logger.log(
+                    INFO,
+                    "   Share " + share.getShareId() + " (free space: "
+                            + FileUtils.byteCountToDisplaySize(share.calculateFreeSpace())
+                            + ") has " + dataSets.size() + " data sets occupying "
+                            + FileUtilities.byteCountToDisplaySize(share.getTotalSizeOfDataSets())
+                            + ".");
             for (int i = 0, n = Math.min(N, dataSets.size()); i < n; i++)
             {
                 SimpleDataSetInformationDTO dataSet = dataSets.get(i);
-                logger.log(INFO, "      " + dataSet.getDataSetCode() + " "
-                        + FileUtilities.byteCountToDisplaySize(dataSet.getDataSetSize()));
+                logger.log(
+                        INFO,
+                        "      " + dataSet.getDataSetCode() + " "
+                                + FileUtilities.byteCountToDisplaySize(dataSet.getDataSetSize()));
             }
             if (dataSets.size() > N)
             {
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java
index 9353a079e24f8e3f27ff9d68dc1a48fd23ea01ee..1d496ba5c2723f417e42e9d5a663bffc3e3a822f 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTask.java
@@ -22,6 +22,7 @@ import java.util.Properties;
 
 import org.apache.log4j.Logger;
 
+import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider;
@@ -42,38 +43,48 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.utils.SegmentedStoreUtils;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share;
 
 /**
- * 
+ * Maintenance task which tries to balance a segmented store.
  *
  * @author Franz-Josef Elmer
  */
 public class SegmentedStoreBalancingTask implements IMaintenanceTask
 {
-    private static final String BALANCER_SECTION_NAME = "balancer";
-    private static final String CLASS_PROPERTY_NAME = "class";
+    @Private static final String BALANCER_SECTION_NAME = "balancer";
+    @Private static final String CLASS_PROPERTY_NAME = "class";
     
     private static final Logger operationLog =
         LogFactory.getLogger(LogCategory.OPERATION, SegmentedStoreBalancingTask.class);
     
     private final IEncapsulatedOpenBISService service;
+    private final IDataSetMover dataSetMover;
     private final IFreeSpaceProvider freeSpaceProvider;
     private final ISimpleLogger operationLogger;
     
     private File storeRoot;
     private String dataStoreCode;
-    private ISegmentedStoreBalancer balancer;
+    @Private ISegmentedStoreBalancer balancer;
     
     public SegmentedStoreBalancingTask()
     {
         this(ServiceProvider.getOpenBISService(), new SimpleFreeSpaceProvider(),
-                new Log4jSimpleLogger(operationLog));
+                new IDataSetMover()
+                    {
+
+                        public void moveDataSetToAnotherShare(File dataSetDirInStore, File share)
+                        {
+                            SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, share,
+                                    ServiceProvider.getOpenBISService());
+                        }
+                    }, new Log4jSimpleLogger(operationLog));
     }
 
-    SegmentedStoreBalancingTask(IEncapsulatedOpenBISService service,
-            IFreeSpaceProvider freeSpaceProvider, ISimpleLogger logger)
+    SegmentedStoreBalancingTask(final IEncapsulatedOpenBISService service,
+            IFreeSpaceProvider freeSpaceProvider, IDataSetMover dataSetMover, ISimpleLogger logger)
     {
         LogInitializer.init();
         this.freeSpaceProvider = freeSpaceProvider;
         this.service = service;
+        this.dataSetMover = dataSetMover;
         operationLogger = logger;
     }
 
@@ -126,7 +137,7 @@ public class SegmentedStoreBalancingTask implements IMaintenanceTask
         List<Share> shares =
                 SegmentedStoreUtils.getDataSetsPerShare(storeRoot, dataStoreCode,
                         freeSpaceProvider, service, operationLogger);
-        balancer.balanceStore(shares, service, operationLogger);
+        balancer.balanceStore(shares, service, dataSetMover, operationLogger);
         operationLog.info("Segmented store balancing finished.");
     }
 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java
index 0cae99a78d5a311904ff4127939f041bcac30665..9141324724868bfb6d8171b9863751b588546711 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancer.java
@@ -26,22 +26,26 @@ import java.util.List;
 import java.util.Properties;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.time.StopWatch;
 
+import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.logging.ISimpleLogger;
+import ch.systemsx.cisd.common.utilities.ITimeProvider;
 import ch.systemsx.cisd.common.utilities.PropertyUtils;
+import ch.systemsx.cisd.common.utilities.SystemTimeProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
-import ch.systemsx.cisd.openbis.dss.generic.shared.utils.SegmentedStoreUtils;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO;
 
 /**
+ * Simple balancer which moves data sets from full shares to the share with initial most free space
+ * until it is full.
  * 
- *
  * @author Franz-Josef Elmer
  */
 public class SimpleBalancer implements ISegmentedStoreBalancer
 {
+    @Private static final String MINIMUM_FREE_SPACE_KEY = "minimum-free-space-in-MB";
+
     private static final class ShareState
     {
         private final Share share;
@@ -88,16 +92,24 @@ public class SimpleBalancer implements ISegmentedStoreBalancer
     }
     
     private final long minimumFreeSpace;
+    private final ITimeProvider timeProvider;
 
     public SimpleBalancer(Properties properties)
     {
+        this(properties, SystemTimeProvider.SYSTEM_TIME_PROVIDER);
+    }
+    
+    SimpleBalancer(Properties properties, ITimeProvider timeProvider)
+    {
+        this.timeProvider = timeProvider;
         minimumFreeSpace =
-                FileUtils.ONE_MB
-                        * PropertyUtils.getLong(properties, "minimum-free-space-in-MB", 1024);
+            FileUtils.ONE_MB
+            * PropertyUtils.getLong(properties, MINIMUM_FREE_SPACE_KEY, 1024);
+        
     }
     
     public void balanceStore(List<Share> shares, IEncapsulatedOpenBISService service,
-            ISimpleLogger logger)
+            IDataSetMover dataSetMover, ISimpleLogger logger)
     {
         List<ShareState> shareStates = getSortedShares(shares);
         ShareState shareWithMostFree = shareStates.get(shareStates.size() - 1);
@@ -119,7 +131,7 @@ public class SimpleBalancer implements ISegmentedStoreBalancer
                 long dataSetSize = dataSets.get(i).getDataSetSize();
                 if (shareWithMostFree.getFreeSpace() - dataSetSize > minimumFreeSpace)
                 {
-                    copy(fullShare, 0, shareWithMostFree, service, logger);
+                    copy(fullShare, 0, shareWithMostFree, dataSetMover, logger);
                 }
             }
         }
@@ -137,11 +149,11 @@ public class SimpleBalancer implements ISegmentedStoreBalancer
             }
             freeSpaceAboveMinimum += dataSets.get(i).getDataSetSize();
         }
-        return -1;
+        return freeSpaceAboveMinimum > 0 ? dataSets.size() : -1;
     }
 
     private void copy(ShareState from, int dataSetIndex, ShareState to,
-            IEncapsulatedOpenBISService service, ISimpleLogger logger)
+            IDataSetMover mover, ISimpleLogger logger)
     {
         Share fromShare = from.getShare();
         Share toShare = to.getShare();
@@ -149,17 +161,15 @@ public class SimpleBalancer implements ISegmentedStoreBalancer
                 fromShare.getDataSetsOrderedBySize().get(dataSetIndex);
         File dataSetDirInStore = new File(fromShare.getShare(), dataSet.getDataSetLocation());
         String commonMessage =
-                "data set " + dataSet.getDataSetCode() + " from share " + fromShare.getShareId()
+                "Moving data set " + dataSet.getDataSetCode() + " from share " + fromShare.getShareId()
                         + " to share " + toShare.getShareId();
-        logger.log(INFO, "Move " + commonMessage + " ...");
-        StopWatch stopWatch = new StopWatch();
-        stopWatch.start();
-        SegmentedStoreUtils.moveDataSetToAnotherShare(dataSetDirInStore, toShare.getShare(),
-                service);
+        logger.log(INFO, commonMessage + " ...");
+        long t0 = timeProvider.getTimeInMilliseconds();
+        mover.moveDataSetToAnotherShare(dataSetDirInStore, toShare.getShare());
         from.removeDataSet(dataSetIndex);
         to.addDataSet(dataSet);
-        stopWatch.stop();
-        logger.log(INFO, "Moving " + commonMessage + " took " + stopWatch.toString());
+        logger.log(INFO, commonMessage + " took "
+                + ((timeProvider.getTimeInMilliseconds() - t0 + 500) / 1000) + " seconds.");
     }
 
     private List<ShareState> getFullShares(List<ShareState> shareStates)
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java
index becaf8bffab3c4b33e2d8a6ba9976ca72bdf1b33..7ced2d3554982ab96f89064a10794da7d2a6c703 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/utils/Share.java
@@ -59,7 +59,7 @@ public final class Share
 
     private long size;
 
-    Share(File share, IFreeSpaceProvider freeSpaceProvider)
+    public Share(File share, IFreeSpaceProvider freeSpaceProvider)
     {
         this.share = share;
         this.freeSpaceProvider = freeSpaceProvider;
@@ -96,7 +96,7 @@ public final class Share
         }
     }
     
-    void addDataSet(SimpleDataSetInformationDTO dataSet)
+    public void addDataSet(SimpleDataSetInformationDTO dataSet)
     {
         dataSets.add(dataSet);
         size += dataSet.getDataSetSize();
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTaskTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTaskTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5416e4dd82342eabd251394950cf9735d2490666
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SegmentedStoreBalancingTaskTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2011 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.etlserver.plugins;
+
+import static ch.systemsx.cisd.etlserver.plugins.SegmentedStoreBalancingTask.BALANCER_SECTION_NAME;
+import static ch.systemsx.cisd.etlserver.plugins.SegmentedStoreBalancingTask.CLASS_PROPERTY_NAME;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.rinn.restrictions.Friend;
+import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
+import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider;
+import ch.systemsx.cisd.common.logging.ISimpleLogger;
+import ch.systemsx.cisd.common.logging.LogLevel;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
+import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil;
+import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+@Friend(toClasses=SegmentedStoreBalancingTask.class)
+public class SegmentedStoreBalancingTaskTest extends AbstractFileSystemTestCase
+{
+    public static final class Balancer implements ISegmentedStoreBalancer
+    {
+        private final Properties properties;
+        private List<Share> shares;
+        private IEncapsulatedOpenBISService service;
+        private IDataSetMover dataSetMover;
+        private ISimpleLogger logger;
+        
+        public Balancer(Properties properties)
+        {
+            this.properties = properties;
+        }
+
+        public void balanceStore(List<Share> list, IEncapsulatedOpenBISService openBisService,
+                IDataSetMover mover, ISimpleLogger simpleLogger)
+        {
+            shares = list;
+            service = openBisService;
+            dataSetMover = mover;
+            logger = simpleLogger;
+        }
+    }
+    
+    private Mockery context;
+    private IEncapsulatedOpenBISService service;
+    private IFreeSpaceProvider spaceProvider;
+    private IDataSetMover dataSetMover;
+    private ISimpleLogger logger;
+    private SegmentedStoreBalancingTask balancerTask;
+    private File storeRoot;
+
+    @BeforeMethod
+    public void beforeMethod()
+    {
+        context = new Mockery();
+        service = context.mock(IEncapsulatedOpenBISService.class);
+        spaceProvider = context.mock(IFreeSpaceProvider.class);
+        dataSetMover = context.mock(IDataSetMover.class);
+        logger = context.mock(ISimpleLogger.class);
+        balancerTask = new SegmentedStoreBalancingTask(service, spaceProvider, dataSetMover, logger);
+        storeRoot = new File(workingDirectory, "store");
+        storeRoot.mkdirs();
+    }
+    
+    @AfterMethod
+    public void afterMethod()
+    {
+        // The 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();
+    }
+
+    @Test
+    public void testExecute()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(DssPropertyParametersUtil.DSS_CODE_KEY, "data-store-1");
+        properties.setProperty(DssPropertyParametersUtil.STOREROOT_DIR_KEY, storeRoot.getPath());
+        properties.setProperty(BALANCER_SECTION_NAME + "." + CLASS_PROPERTY_NAME,
+                SegmentedStoreBalancingTaskTest.Balancer.class.getName());
+        balancerTask.setUp("mock-balancer", properties);
+        context.checking(new Expectations()
+            {
+                {
+                    one(service).listDataSets();
+                    SimpleDataSetInformationDTO ds = new SimpleDataSetInformationDTO();
+                    ds.setDataStoreCode("other data store");
+                    will(returnValue(Arrays.asList(ds)));
+                }
+            });
+        
+        balancerTask.execute();
+        
+        Balancer balancer = (SegmentedStoreBalancingTaskTest.Balancer) balancerTask.balancer;
+        assertEquals("{class=" + balancer.getClass().getName() + "}",
+                balancer.properties.toString());
+        assertEquals("[]", balancer.shares.toString());
+        assertSame(service, balancer.service);
+        assertSame(dataSetMover, balancer.dataSetMover);
+        assertSame(logger, balancer.logger);
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testDefaultBalancer()
+    {
+        Properties properties = new Properties();
+        properties.setProperty(DssPropertyParametersUtil.DSS_CODE_KEY, "data-store-1");
+        properties.setProperty(DssPropertyParametersUtil.STOREROOT_DIR_KEY, storeRoot.getPath());
+        balancerTask.setUp("mock-balancer", properties);
+        context.checking(new Expectations()
+            {
+                {
+                    one(service).listDataSets();
+                    will(returnValue(Arrays.asList()));
+                    
+                    one(logger).log(LogLevel.INFO, "Data Store Shares:");
+                }
+            });
+
+        balancerTask.execute();
+        
+        context.assertIsSatisfied();
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d782b231d21726d5056b73ccc327f84603bd007
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/plugins/SimpleBalancerTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2011 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.etlserver.plugins;
+
+
+import static org.apache.commons.io.FileUtils.ONE_MB;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.rinn.restrictions.Friend;
+import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase;
+import ch.systemsx.cisd.common.filesystem.HostAwareFile;
+import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider;
+import ch.systemsx.cisd.common.logging.ISimpleLogger;
+import ch.systemsx.cisd.common.logging.LogLevel;
+import ch.systemsx.cisd.common.utilities.ITimeProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
+import ch.systemsx.cisd.openbis.dss.generic.shared.utils.Share;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SimpleDataSetInformationDTO;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+@Friend(toClasses=SimpleBalancer.class)
+public class SimpleBalancerTest extends AbstractFileSystemTestCase
+{
+    private static final String STORE_PATH = "01/02/03/";
+
+    private static final class MockSpaceProvider implements IFreeSpaceProvider
+    {
+        private final Map<File, List<Long>> freeSpace = new HashMap<File, List<Long>>();
+        
+        void addFreeSpaceExpectationFor(Share share, long valueInKb)
+        {
+            List<Long> list = freeSpace.get(share.getShare());
+            if (list == null)
+            {
+                list = new ArrayList<Long>();
+                freeSpace.put(share.getShare(), list);
+            }
+            list.add(valueInKb);
+        }
+        
+        @SuppressWarnings("null")
+        public long freeSpaceKb(HostAwareFile path) throws IOException
+        {
+            File file = path.getFile();
+            List<Long> list = freeSpace.get(file);
+            assertTrue("Unknown file " + file, list != null);
+            assertFalse("Unexpected invocation for file " + file, list.isEmpty());
+            return list.remove(0);
+        }
+        
+        void assertIsSatiesfied()
+        {
+            Set<Entry<File, List<Long>>> entrySet = freeSpace.entrySet();
+            for (Entry<File, List<Long>> entry : entrySet)
+            {
+                assertEquals("Unfullfilled free space assertions for " + entry.getKey().getPath(),
+                        0, entry.getValue().size());
+            }
+        }
+    }
+    
+    private MockSpaceProvider spaceProvider;
+    private Mockery context;
+    private IEncapsulatedOpenBISService service;
+    private IDataSetMover dataSetMover;
+    private ISimpleLogger logger;
+    private SimpleBalancer balancer;
+    private File store;
+
+    @BeforeMethod
+    public void beforeMethod()
+    {
+        spaceProvider = new MockSpaceProvider();
+        context = new Mockery();
+        service = context.mock(IEncapsulatedOpenBISService.class);
+        dataSetMover = context.mock(IDataSetMover.class);
+        logger = context.mock(ISimpleLogger.class);
+        final ITimeProvider timeProvider = context.mock(ITimeProvider.class);
+        context.checking(new Expectations()
+            {
+                {
+                    allowing(timeProvider).getTimeInMilliseconds();
+                    will(returnValue(0L));
+                }
+            });
+        Properties properties = new Properties();
+        properties.setProperty(SimpleBalancer.MINIMUM_FREE_SPACE_KEY, "2");
+        balancer = new SimpleBalancer(properties, timeProvider);
+        store = new File(workingDirectory, "store");
+    }
+    
+    @AfterMethod
+    public void afterMethod()
+    {
+        // The 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();
+    }
+
+    @Test
+    public void test()
+    {
+        final Share share1 = new Share(new File(store, "1"), spaceProvider);
+        share1.addDataSet(dataSet("ds1", 2000));
+        share1.addDataSet(dataSet("ds2", ONE_MB));
+        share1.addDataSet(dataSet("ds3", ONE_MB + 100));
+        spaceProvider.addFreeSpaceExpectationFor(share1, 100l);
+        spaceProvider.addFreeSpaceExpectationFor(share1, 1100l);
+        spaceProvider.addFreeSpaceExpectationFor(share1, 2100l);
+        final Share share2 = new Share(new File(store, "2"), spaceProvider);
+        share2.addDataSet(dataSet("ds4", 2 * ONE_MB));
+        spaceProvider.addFreeSpaceExpectationFor(share2, 500l);
+        final Share share3 = new Share(new File(store, "3"), spaceProvider);
+        spaceProvider.addFreeSpaceExpectationFor(share3, 4 * 1024l);
+        spaceProvider.addFreeSpaceExpectationFor(share3, 3 * 1024l);
+        spaceProvider.addFreeSpaceExpectationFor(share3, 1024l);
+        final Share share4 = new Share(new File(store, "4"), spaceProvider);
+        spaceProvider.addFreeSpaceExpectationFor(share4, 3 * 1024l);
+        context.checking(new Expectations()
+            {
+                {
+                    one(logger).log(LogLevel.INFO, "Moving data set ds3 from share 1 to share 3 ...");
+                    one(dataSetMover).moveDataSetToAnotherShare(new File(share1.getShare(), STORE_PATH + "ds3"), share3.getShare());
+                    one(logger).log(LogLevel.INFO, "Moving data set ds3 from share 1 to share 3 took 0 seconds.");
+                    
+                    one(logger).log(LogLevel.INFO, "Moving data set ds2 from share 1 to share 3 ...");
+                    one(dataSetMover).moveDataSetToAnotherShare(new File(share1.getShare(), STORE_PATH + "ds2"), share3.getShare());
+                    one(logger).log(LogLevel.INFO, "Moving data set ds2 from share 1 to share 3 took 0 seconds.");
+                }
+            });
+        
+        balancer.balanceStore(Arrays.asList(share1, share2, share3, share4), service, dataSetMover, logger);
+        
+        assertEquals(1, share1.getDataSetsOrderedBySize().size());
+        assertEquals(1, share2.getDataSetsOrderedBySize().size());
+        assertEquals("ds3", share3.getDataSetsOrderedBySize().get(0).getDataSetCode());
+        assertEquals("ds2", share3.getDataSetsOrderedBySize().get(1).getDataSetCode());
+        assertEquals(2, share3.getDataSetsOrderedBySize().size());
+        assertEquals(0, share4.getDataSetsOrderedBySize().size());
+        spaceProvider.assertIsSatiesfied();
+        context.assertIsSatisfied();
+    }
+    
+    private SimpleDataSetInformationDTO dataSet(String code, long size)
+    {
+        SimpleDataSetInformationDTO dataSet = new SimpleDataSetInformationDTO();
+        dataSet.setDataSetCode(code);
+        dataSet.setDataSetLocation(STORE_PATH + code);
+        dataSet.setDataSetSize(size);
+        return dataSet;
+    }
+}