diff --git a/AtlasTest/TestTools/TestTools/FLOATassert.h b/AtlasTest/TestTools/TestTools/FLOATassert.h
new file mode 100644
index 0000000000000000000000000000000000000000..828a98b212f69a48325a7456b2e48bbe34b25d54
--- /dev/null
+++ b/AtlasTest/TestTools/TestTools/FLOATassert.h
@@ -0,0 +1,42 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**  functions & macros to test the difference between floats */
+#ifndef TESTTOOLS_FLOATASSERT_H
+#define TESTTOOLS_FLOATASSERT_H
+
+#include <iostream>
+#include <cassert>
+#include <cfloat>
+#include <cmath>
+
+#undef NDEBUG
+
+namespace Athena_test {
+  inline
+  bool floatEQ(float lhs, float rhs) {
+    return fabs(lhs-rhs)<=FLT_EPSILON;
+  }
+  inline
+  bool floatNEQ(float lhs, float rhs) {
+    return fabs(lhs-rhs)>FLT_EPSILON;
+  }
+
+  inline
+  bool isEqual (double x1, double x2, double thresh = 1e-6)
+  {
+    double den = std::abs(x1+x2);
+    if (den < thresh) return true;
+    if (std::abs (x1-x2) / den < thresh)
+      return true;
+    std::cout << "Match failure: " << x1 << " " << x2 << "\n";
+    return false;
+  }
+
+}
+
+#define FLOAT_NEQassert( LHS, RHS ) assert(Athena_test::floatNEQ(LHS, RHS));	
+#define FLOAT_EQassert( LHS, RHS ) assert(Athena_test::floatEQ(LHS, RHS));	
+
+#endif
diff --git a/AtlasTest/TestTools/TestTools/SGassert.h b/AtlasTest/TestTools/TestTools/SGassert.h
new file mode 100755
index 0000000000000000000000000000000000000000..3ab5c54d1e0a48dfe6c4ef2892a619a34464e15d
--- /dev/null
+++ b/AtlasTest/TestTools/TestTools/SGassert.h
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/***************************************************************************
+ macro to assert an error condition
+ ----------------------------------
+ ATLAS Collaboration
+ ***************************************************************************/
+
+// $Id: SGassert.h,v 1.2 2005-11-29 00:51:33 calaf Exp $
+
+
+#ifndef TEST_SGASSERT_H
+# define TEST_SGASSERT_H
+
+#include <cassert>
+#include <iostream>
+
+#undef NDEBUG
+
+#define SGASSERT( TRUEEXPR ) assert(TRUEEXPR)
+#define SGASSERTERROR( FALSEEXPR )   \
+    std::cerr << "Now we expect to see an error message:" << std::endl \
+              << "----Error Message Starts--->>" << std::endl; \
+    assert(!FALSEEXPR); \
+    std::cerr<< "<<---Error Message Ends-------" << std::endl
+
+
+#endif // TEST_SGASSERT_H
diff --git a/AtlasTest/TestTools/TestTools/expect_exception.h b/AtlasTest/TestTools/TestTools/expect_exception.h
new file mode 100644
index 0000000000000000000000000000000000000000..afdcc68056d6ecd735d7f940b141c447ed2c957f
--- /dev/null
+++ b/AtlasTest/TestTools/TestTools/expect_exception.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id$
+/**
+ * @file TestTools/expect_exception.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Aug, 2014
+ * @brief Helper to check that an exception is thrown.
+ */
+
+
+#ifndef TESTTOOLS_EXPECT_EXCEPTION_H
+#define TESTTOOLS_EXPECT_EXCEPTION_H
+
+
+#include <cassert>
+
+
+/**
+ * @brief Helper to check that an exception is thrown.
+ *
+ * Use like this:
+ *
+ *@code
+ *  EXPECT_EXCEPTION (std::runtime_error, doSomething());
+ @endcode
+ *
+ * This will produce an exception failure if @c doSomething()
+ * does _not_ throw a @c std::runtime_error exception.
+ */
+#define EXPECT_EXCEPTION(EXC, CODE) do { \
+  bool caught = false;                   \
+  try {                                  \
+    CODE;                                \
+  }                                      \
+  catch (const EXC&) {                   \
+    caught = true;                       \
+  }                                      \
+  assert (caught);                       \
+} while(0)
+
+
+#endif // not TESTTOOLS_EXPECT_EXCEPTION_H
diff --git a/AtlasTest/TestTools/TestTools/initGaudi.h b/AtlasTest/TestTools/TestTools/initGaudi.h
new file mode 100755
index 0000000000000000000000000000000000000000..93b8c3b00a0f6d742f90331c677b2a02dd6a26d0
--- /dev/null
+++ b/AtlasTest/TestTools/TestTools/initGaudi.h
@@ -0,0 +1,34 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TEST_INITGAUDI_H
+# define TEST_INITGAUDI_H
+/** @file initGaudi.h
+ * @brief  minimal gaudi initialization for AthenaServices unit testing
+ *
+ * @author Paolo Calafiura <pcalafiura@lbl.gov> -ATLAS Collaboration
+ * $Id: initGaudi.h,v 1.4 2005-11-29 00:51:33 calaf Exp $
+ **/
+
+#include <string>
+
+#undef NDEBUG
+
+class ISvcLocator;
+
+
+namespace Athena_test {
+  /** @fn bool initGaudi(ISvcLocator*& pSvcLoc)
+   *  @brief  minimal gaudi initialization for AthenaServices unit testing
+   *  @param pSvcLoc returns a pointer to the Gaudi ServiceLocator
+   */
+  bool initGaudi(ISvcLocator*& pSvcLoc);
+  /** @fn initGaudi(const std::string& jobOptsFile, ISvcLocator*& pSvcLoc);
+   *  @brief  minimal gaudi initialization for AthenaServices unit testing
+   *  @param jobOptsFile job opts file name (located at ../share/jobOptFiles)
+   *  @param pSvcLoc returns a pointer to the Gaudi ServiceLocator
+   */
+  bool initGaudi(const std::string& jobOptsFile, ISvcLocator*& pSvcLoc);
+}
+#endif // TEST_INITGAUDI_H
diff --git a/AtlasTest/TestTools/TestTools/random.h b/AtlasTest/TestTools/TestTools/random.h
new file mode 100644
index 0000000000000000000000000000000000000000..4b44f9f32f059016cd77f8107b4f4309779689b8
--- /dev/null
+++ b/AtlasTest/TestTools/TestTools/random.h
@@ -0,0 +1,89 @@
+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+// $Id$
+/**
+ * @file TestTools/random.h
+ * @author scott snyder <snyder@bnl.gov>
+ * @date Aug, 2014
+ * @brief Very simple random numbers for regression testing.
+ *
+ * This file provides a few very simple random number generators
+ * useful for regression testing.  These are 32-bit LCGs, with constants
+ * taken from Numerical Recipes.  These numbers will have poor quality;
+ * however, the results should be completely reproducible across platforms.
+ * For regression testing, that's often all that's really needed.
+ */
+
+
+#ifndef TESTTOOLS_RANDOM_H
+#define TESTTOOLS_RANDOM_H
+
+
+#include <stdint.h>
+
+
+namespace Athena_test {
+
+
+/// Maximum number generated.
+static uint32_t rngmax = static_cast<uint32_t> (-1);
+
+
+/// Generate a random number between 0 and @c rngmax
+uint32_t rng_seed (uint32_t& seed)
+{
+  seed = (1664525*seed + 1013904223);
+  return seed;
+}
+
+
+/// Generate a floating-point random number between @c rmin and @c rmax.
+float randf_seed (uint32_t& seed, float rmax, float rmin = 0)
+{
+  return static_cast<float>(rng_seed(seed)) / rngmax * (rmax-rmin) + rmin;
+}
+
+
+/// Generate an integer random number between @c rmin and @c rmax.
+int randi_seed (uint32_t& seed, int rmax, int rmin = 0)
+{
+  return static_cast<int> (randf_seed (seed, rmax, rmin));
+}
+
+
+/// Generator compatible with the STL RandomNumberGenerator.
+struct RNG
+{
+  RNG() : seed(1) {}
+  int operator() (int n) const { return randi_seed (seed, n); }
+  mutable uint32_t seed;
+};
+
+
+/// Generator compatible with the C++11 STL UniformRandomNumberGenerator.
+struct URNG
+{
+  typedef uint32_t result_type;
+  URNG() : seed(1) {}
+  static result_type min() { return 0; }
+  static result_type max() { return 1000000; }
+  result_type operator()() const { return randi_seed (seed, max()); }
+  mutable uint32_t seed;
+};
+
+
+uint32_t seed = 1;
+uint32_t rng() { return rng_seed(seed); }
+int randi (int rmax, int rmin = 0) { return randi_seed (seed, rmax, rmin); }
+float randf (float rmax, float rmin = 0) { return randf_seed (seed, rmax, rmin); }
+
+
+
+} // namespace Athena_test
+
+
+#endif // not TESTTOOLS_RANDOM_H
diff --git a/AtlasTest/TestTools/cmt/Makefile.RootCore b/AtlasTest/TestTools/cmt/Makefile.RootCore
new file mode 100644
index 0000000000000000000000000000000000000000..d59368a1625a5812f517e989b2fa6791d82bdebd
--- /dev/null
+++ b/AtlasTest/TestTools/cmt/Makefile.RootCore
@@ -0,0 +1,24 @@
+# this makefile also gets parsed by shell scripts
+# therefore it does not support full make syntax and features
+# edit with care
+
+# for full documentation check:
+# https://twiki.cern.ch/twiki/bin/viewauth/Atlas/RootCore#Package_Makefile
+
+PACKAGE          = TestTools
+PACKAGE_PRELOAD  =
+PACKAGE_CXXFLAGS = 
+PACKAGE_OBJFLAGS = 
+PACKAGE_LDFLAGS  = 
+PACKAGE_BINFLAGS = 
+PACKAGE_LIBFLAGS = 
+PACKAGE_DEP      = 
+PACKAGE_TRYDEP   = 
+PACKAGE_CLEAN    = 
+PACKAGE_NOGRID   = 
+PACKAGE_PEDANTIC = 0
+PACKAGE_NOOPT    = 0
+PACKAGE_NOCC     = 1
+PACKAGE_REFLEX   = 0
+
+include $(ROOTCOREDIR)/Makefile-common
diff --git a/AtlasTest/TestTools/cmt/requirements b/AtlasTest/TestTools/cmt/requirements
new file mode 100755
index 0000000000000000000000000000000000000000..75171b7d5833913186dd078489b024930ccf2bf1
--- /dev/null
+++ b/AtlasTest/TestTools/cmt/requirements
@@ -0,0 +1,46 @@
+package TestTools
+author Paolo Calafiura   <Paolo.Calafiura@cern.ch>
+author Sebastien Binet   <binet@cern.ch>
+
+use AtlasPolicy    AtlasPolicy-*
+use AtlasPython    AtlasPython-*        External -no_auto_imports
+
+use AthenaCommon   AthenaCommon-*          Control -no_auto_imports
+
+use TestPolicy      TestPolicy-*
+
+private
+use GaudiInterface GaudiInterface-*     External 
+branches python share src TestTools test
+end_private
+
+library TestTools *.cxx
+apply_pattern installed_library
+
+apply_pattern declare_scripts files="runUnitTests.sh post.sh"
+apply_pattern declare_python_modules files="*.py"
+apply_pattern declare_joboptions files="*.py"
+
+macro whichGroup check
+#macro whichGroup "NONE" \
+#	debug    "check"
+
+pattern UnitTest_run \
+	application <unit_test>_test -group=$(whichGroup) ../test/<unit_test>_test.cxx ; \
+	document athenarun_launcher <unit_test>_utest -group=$(whichGroup) \
+        athenarun_exe="'../${CMTCONFIG}/<unit_test>_test.exe'" \
+        athenarun_pre="'. ../cmt/setup.sh'" \
+        athenarun_opt="" \
+        athenarun_out="' > <unit_test>_test.log 2>&1'" \
+        athenarun_post="'post.sh <unit_test>_test $(q)<extrapatterns>$(q)'" ; \
+        private ; \
+        macro_append <unit_test>_utest_dependencies " <unit_test>_test " ; \
+        end_private
+
+private
+
+macro_append DOXYGEN_INPUT " ../doc" 
+macro_append DOXYGEN_INPUT " ../share" 
+macro_append DOXYGEN_FILE_PATTERNS    " *.sh"
+macro_append DOXYGEN_FILE_PATTERNS    " *.txt"
+
diff --git a/AtlasTest/TestTools/doc/MainPage.h b/AtlasTest/TestTools/doc/MainPage.h
new file mode 100755
index 0000000000000000000000000000000000000000..df825d7670f211ff84389b902e42d5af2f093df0
--- /dev/null
+++ b/AtlasTest/TestTools/doc/MainPage.h
@@ -0,0 +1,89 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/**
+
+   \mainpage
+
+This package contains a few tools to help writing unit tests. 
+
+\section UnitTest_run The UnitTest_run cmt pattern
+
+TestTools requirements file defines the cmt pattern UnitTest_run. UnitTest_run 
+will compile, link and run a standalone C++ program when a "gmake check" 
+command is issued. It will then run the share/post.sh script to compare the 
+program output with a reference one, if available.
+ The pattern takes one parameter <unit_test> which is used to identify a number of files:
+ -# $PKGROOT/test/<unit_test>_test.cxx		C++ program to be run
+ -# $PKGROOT/share/<unit_test>_test.ref		optional reference output
+ -# $PKGROOT/run/<unit_test>_test.log		program output (stdout & stderr)
+ -# $PKGROOT/$CMTCONFIG/<unit_test>_test.exe	executable
+
+So for example 
+<PRE>
+  apply_pattern UnitTest_run unit_test=DataPool
+will compile and link 
+ ../test/DataPool_test.cxx 
+into 
+ ../$CMTCONFIG/DataPool_test.exe 
+which will be run and produce 
+ ../run/DataPool_test.log 
+If you have created the reference output
+ ../share/DataPool_test.ref
+this will be compared to the log file at the end of the job.
+</PRE>
+
+Notice that the comparison tries to ignore a certain 
+number of differences that are usually harmless (e.g. the execution time 
+reported by ChronoStatSvc or the package versions and even in certain cases
+pointer addresses). This is currently done in a very naive fashion (using 
+diff -I option) but a more sophisticated "diff" script is in the plans
+
+\section initGaudi The initGaudi functions
+TestTools/initGaudi.h defines two functions in the namespace Athena_test 
+to initialize Gaudi ApplicationMgr 
+and be able to run using core Gaudi services. An optional string argument
+<jobOptsFile> instructs initGaudi to read in the job options file
+ $PKGROOT/share/<jobOptsFile>
+to configure your job
+
+\section scripts Scripts
+ - share/runUnitTests.sh is a sh script that cmt broadcasts gmake check
+ and filter its output. It is 
+installed in the run area. It accepts one or more arguments that it passes
+to cmt broadcast, for example
+<PRE>
+ ../run/%runUnitTests.sh -select=StoreGate
+</PRE>
+ - share/post.sh is a script used by the UnitTest_run pattern to 
+analize a job output 
+
+\section toys Toys 
+
+The TestTools component library provides toy implementations of a number of
+typical Gaudi classes, namely ToyConverter, ToyConversionSvc (and soon 
+ToyAlgorithm). These are made available via the job opts file
+ $TESTTOOLSROOT/share/ToyConversionOpts.txt
+
+
+
+\section Examples Examples
+
+The package AthenaTests/ControlTests contains several examples that use
+the initGaudi function.
+Control/StoreGate has a couple of non-Gaudi-based,
+very simple unit tests (e.g. KeyConcept) as well as more sophisticated ones
+that show 
+ - how to link the test program with an extra library (e.g. Clear_Store) 
+ - how to use the ToyConversionSvc provided by TestTools (ProxyProviderSvc)
+
+\section links More info
+The package can be browsed using LXR 
+(http://atlassw1.phy.bnl.gov/lxr/source/atlas/AtlasTest/TestTools/)
+
+To generate doxygen doc, run (from the cmt dir) gmake doxygen and point 
+your browser to .../doc/Doxygen/html/index.html
+
+\author Paolo Calafiura <Paolo.Calafiura@cern.ch>
+*/
diff --git a/AtlasTest/TestTools/python/__init__.py b/AtlasTest/TestTools/python/__init__.py
new file mode 100755
index 0000000000000000000000000000000000000000..a40f3f01e208dffb9ea9461ed14a0690f3ca0756
--- /dev/null
+++ b/AtlasTest/TestTools/python/__init__.py
@@ -0,0 +1,3 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# hook for the TestTools python module
diff --git a/AtlasTest/TestTools/python/iobench.py b/AtlasTest/TestTools/python/iobench.py
new file mode 100755
index 0000000000000000000000000000000000000000..f248f3bbc77580bdd06e720998e9f5805f3d71b0
--- /dev/null
+++ b/AtlasTest/TestTools/python/iobench.py
@@ -0,0 +1,477 @@
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# @file : iobench.py
+# @author: Sebastien Binet <binet@cern.ch>
+# @purpose: Measure the I/O performances of an application
+#
+# @date: July 2006
+#
+
+"""
+A set of python objects to measure the I/O performances of an application.
+"""
+
+__author__  = "$Author: binet $"
+__version__ = "$Revision: 1.4 $"
+
+import sys
+import resource
+import time
+import os
+
+###-----------------------------------------------------
+def getResources( usageMode = resource.RUSAGE_CHILDREN ):
+    """
+    A factory method to fetch the used resources at a given 't' time.
+    Default mode is 'resource.RUSAGE_CHILDREN'
+    """
+    return Resource( utime = resource.getrusage( usageMode ).ru_utime,
+                     stime = resource.getrusage( usageMode ).ru_stime,
+                     rtime = time.time() )
+
+class Resource(object):
+    """
+    A simple class to hold the resources used by a program:
+     - user   time
+     - system time
+     - real   time
+    """
+    
+    def __init__( self,
+                  utime, stime, rtime ):
+        object.__init__(self)
+
+        self.utime = utime
+        self.stime = stime
+        self.rtime = rtime
+
+        return
+
+    def __repr__(self):
+        s = os.linesep.join( [ "user : %s" % str( self.user() ),
+                               "sys  : %s" % str( self.sys()  ),
+                               "real : %s" % str( self.real() ) ] )
+        return s
+
+    def __add__(self, rhs):
+        return Resource( self.utime + rhs.utime,
+                         self.stime + rhs.stime,
+                         self.rtime + rhs.rtime )
+
+    def __iadd__(self, rhs):
+        self.utime += rhs.utime
+        self.stime += rhs.stime
+        self.rtime += rhs.rtime
+        return self
+
+    def __sub__(self, rhs):
+        return Resource( self.utime - rhs.utime,
+                         self.stime - rhs.stime,
+                         self.rtime - rhs.rtime )
+
+    def __isub__(self, rhs):
+        self.utime -= rhs.utime
+        self.stime -= rhs.stime
+        self.rtime -= rhs.rtime
+        return self
+    
+    def user( self ):
+        return self.utime
+
+    def sys( self ):
+        return self.stime
+
+    def real( self ):
+        return self.rtime
+
+    pass # Resource
+
+class Bench(object):
+
+    def __init__( self,
+                  nTimes = 1,
+                  stdout = sys.stdout,
+                  stderr = sys.stderr ):
+        object.__init__(self)
+
+        self.nTimes = nTimes
+        self.data = []
+
+    def run( self, fct, *args, **kwargs ):
+
+        self.data = []
+
+        for i in range(self.nTimes):
+            iStart = getResources()
+            out = fct( *args, **kwargs )
+            self.data.append( getResources() - iStart )
+            pass
+
+        return
+
+    def stats( self ):
+        
+        mean = Resource( 0., 0., 0. )
+        for d in self.data:
+            mean += d
+            pass
+
+        print ""
+        print "## stats : (nbenchs = %i)" % self.nTimes
+        print " time<User> = %8.3f s" % ( mean.user() /  float( self.nTimes ) )
+        print " time<Sys > = %8.3f s" % ( mean.sys()  /  float( self.nTimes ) )
+        print " time<Real> = %8.3f s" % ( mean.real() /  float( self.nTimes ) )
+        
+        return
+
+    def save( self, fileName = "iobench.log" ):
+        f = open( fileName, "w" )
+        for data in self.data:
+            f.writelines( "%s %s %s %s" % ( data.user(),
+                                            data.sys(),
+                                            data.real(),
+                                            os.linesep ) )
+            pass
+        f.close()
+        return
+    
+    pass # Bench
+
+##
+## ---- Athena specific part ----
+##
+
+import os
+import ConfigParser
+class ChronoStatsOutputParser( ConfigParser.ConfigParser ):
+    """Subclass and specialize ConfigParser to make it case-sensitive
+    """
+    def optionxform( self, optionStr ):
+        return optionStr
+
+class ChronoStat(object):
+    """
+    A class mirroring the one from the (Gaudi) ChronoStatSvc which models a
+    chrono measurement.
+    It holds the following informations:
+      - total time
+      - min
+      - mean
+      - RMS
+      - max
+      - number of 'events'
+    """
+    def __init__( self, nbr, min, max, mean, rms, total ):
+        object.__init__(self)
+        self.total = total
+        self.min   = min
+        self.mean  = mean
+        self.rms   = rms
+        self.max   = max
+        self.nbr   = nbr
+        pass
+
+    def __repr__(self):
+        return os.linesep.join( [
+            "min   = %s" % str( self.min   ),
+            "max   = %s" % str( self.max   ),
+            "mean  = %s" % str( self.mean  ),
+            "RMS   = %s" % str( self.RMS   ),
+            "total = %s" % str( self.total ),
+            "#evts = %s" % str( self.nbr   ),
+            ] )
+    
+class ChronoStatReport(object):
+
+    ReadKeyHdr  = "cObj_"
+    WriteKeyHdr = "cRep_"
+    KeySep      = "#"
+    
+    """
+    This class stores the report of the ChronoStatSvc dump from Athena/Gaudi.
+    """
+    def __init__( self, fileName ):
+        """
+        fileName is supposed to contain the output of the ChronoStatSvc which
+        has been configured with 'ChronoStatSvc.ChronoDestinationCout = True'
+        """
+        object.__init__( self )
+
+        self.logFile = ChronoStatsOutputParser()
+        self.logFile.read( fileName )
+
+        self.stats = { }
+        self.chronoStats = { }
+        self.buildStats()
+        pass
+
+    def buildStats( self ):
+
+        for hdr in self.logFile.sections():
+            stat = [
+                ChronoStat(
+                    nbr  = int  (self.logFile.get(hdr, "cpu_%s_nbr"   % item)),
+                    min  = float(self.logFile.get(hdr, "cpu_%s_min"   % item)),
+                    max  = float(self.logFile.get(hdr, "cpu_%s_max"   % item)),
+                    mean = float(self.logFile.get(hdr, "cpu_%s_mean"  % item)),
+                    rms  = float(self.logFile.get(hdr, "cpu_%s_RMS"   % item)),
+                    total= float(self.logFile.get(hdr, "cpu_%s_total" % item))
+                    ) \
+                for item in ['user', 'system', 'real']
+                ]
+                     
+            self.chronoStats[hdr] = {
+                'user'   : stat[0],
+                'system' : stat[1],
+                'real'   : stat[2],
+                }
+            del stat
+            self.stats[hdr] = Resource(
+                float(self.logFile.get(hdr, "cpu_user_mean")),
+                float(self.logFile.get(hdr, "cpu_system_mean")),
+                float(self.logFile.get(hdr, "cpu_real_mean"))
+                )
+        pass
+
+    def getStats(self, key):
+        return self.stats[key]
+
+    def getPoolStats(self, storeGateKey, ioMode = "r" ):
+        ioKey = storeGateKey
+        # 'normalize' key
+        if ioMode.lower() == "r":
+            ioKey = storeGateKey.replace(ChronoStatReport.ReadKeyHdr,"")
+            ioKey = ChronoStatReport.ReadKeyHdr + ioKey
+        elif ioMode.lower() == "w":
+            ioKey = storeGateKey.replace(ChronoStatReport.WriteKeyHdr,"")
+            ioKey = ChronoStatReport.WriteKeyHdr + ioKey
+        else:
+            pass
+
+        if not self.stats.has_key( ioKey ):
+            print "Warning: no such key [%s] in stats !" % ioKey
+            print "Available keys:",self.stats.keys()
+            pass
+        return self.stats[ ioKey ]
+
+    def getPoolKeys(self):
+        return [ k for k in self.stats.keys() \
+                 if k.count(ChronoStatReport.WriteKeyHdr) > 0 ]
+    
+    pass # ChronoStatReport
+
+            
+###-----------------------------------------------------
+## For compatibility with ATN tests
+def workDir( fileName ):
+    """Function to provide an automatic work dir, compatible with ATN tests
+    """
+    if os.environ.has_key('ATN_WORK_AREA'):
+        workArea = os.environ['ATN_WORK_AREA']
+    else:
+        workArea = "/tmp"
+        pass
+    if not os.path.exists(workArea):
+        os.makedirs(workArea)
+    return os.path.join( workArea, fileName )
+
+class ScOutput(object):
+
+    def __init__(self, statusCode = 0, outputLog = ""):
+        object.__init__(self)
+        self.sc  = statusCode
+        self.out = outputLog
+        return
+
+    pass # ScOutput
+
+###-----------------------------------------------------
+## Little helper to validate output of jobs
+def doValidation( dbFiles, key ):
+    """Helper function to validate output of jobs against 'registered' ASCII
+    files.
+    """
+    import commands
+    from TestTools.iobench import ScOutput
+    print "## Validation of ASCII files [%s]:" % key
+    print "## %15s : %s" % ( 'ref', dbFiles[key]['ref'] )
+    print "## %15s : %s" % ( 'chk', dbFiles[key]['chk'] )
+    sc,out = commands.getstatusoutput( "diff %s %s" %
+                                       ( dbFiles[key]['ref'],
+                                         dbFiles[key]['chk'] ) )
+    if sc == 0 and len(out) == 0:
+        print "==> Validation [OK]"
+    else:
+        print "==> Validation [ERROR]"
+        pass
+    return ScOutput(sc,out)
+
+###-----------------------------------------------------
+## Little helper to validate output of jobs
+def doPostCheck( validationName, refFileName, chkFileName, chkFilter ):
+    import commands
+    print "## Validation of [%s]" % validationName
+    print "## ref:    %s"   % refFileName
+    print "## chk:    %s"   % chkFileName
+    print "## filter: [%s]" % chkFilter
+    sc, out = commands.getstatusoutput( "cat %s | %s | diff -u %s -" % \
+                                        ( chkFileName, chkFilter,
+                                          refFileName ) )
+    if sc == 0 and len(out) == 0: print "==> Validation [OK]"
+    else:                         print "==> Validation [ERROR]\n",\
+                                        "*"*80,out,"*"*80
+    return ScOutput(sc, out)
+
+###-----------------------------------------------------
+from AthenaCommon import ChapPy
+from tempfile import NamedTemporaryFile
+class AthBench(object):
+
+    def __init__( self,
+                  athenaJob,
+                  logFile,
+                  nTimes = 1,
+                  stdout = sys.stdout,
+                  stderr = sys.stderr,
+                  ioStatsLogFile = workDir("ioStats.out") ):
+        object.__init__(self)
+
+        self.athena = athenaJob
+        self.logFileName = logFile
+        self.nTimes = nTimes
+        self.data = []
+        self.chronoStats= []
+        self.ioStatsFileName = ioStatsLogFile
+        
+    def run( self ):
+
+        self.data = []
+        self.chronoStats= []
+        self.athena.jobOptions += [
+            ChapPy.JobOptions( "TestTools/IoAuditor_fragment.py" ),
+            ChapPy.JobOptionsCmd( "svcMgr.ChronoStatSvc.AsciiStatsOutputFile = \"%s\"" % self.ioStatsFileName )
+            ]
+        for i in range(self.nTimes):
+            self.athena.logFile = open( "%s.%s" % (self.logFileName,i), "w" )
+            iStart = getResources()
+            out = self.athena.run()
+            self.data.append( getResources() - iStart )
+            self.chronoStats.append( ChronoStatReport(self.ioStatsFileName) )
+            pass
+
+        return
+
+    def __printStat(self, res, title="Overall", isIO = False, keyType = ""):
+        if isIO:
+            u = "us/evt"
+        else:
+            u = "s"
+            pass
+        if   keyType == ChronoStatReport.WriteKeyHdr: keyType = "[WRITE]"
+        elif keyType == ChronoStatReport.ReadKeyHdr:  keyType = "[READ]"
+        else:
+            pass
+        print ""
+        print "## stats : [%s] (nbenchs = %i) %s" % (title,
+                                                     self.nTimes,
+                                                     keyType)
+        print " time<User> = %12.3f %s" % (res.user() / float(self.nTimes), u)
+        print " time<Sys > = %12.3f %s" % (res.sys()  / float(self.nTimes), u)
+        print " time<Real> = %12.3f %s" % (res.real() / float(self.nTimes), u)
+        
+    def ioStats(self, ioKeys = [], ioMode = "r"):
+
+        if len(ioKeys) == 0:
+            ioKeys = self.chronoStats[0].getPoolKeys()
+            pass
+        keyHdr = ""
+        if ioMode.lower() == "r":
+            keyHdr = ChronoStatReport.ReadKeyHdr
+        elif ioMode.lower() == "w":
+            keyHdr = ChronoStatReport.WriteKeyHdr
+        for ioKey in ioKeys:
+            mean = Resource( 0., 0., 0. )
+            for d in self.chronoStats:
+                mean += d.getPoolStats(ioKey, ioMode)
+                pass
+            self.__printStat(mean,
+                             ioKey.replace(keyHdr,""),
+                             isIO = True,
+                             keyType = keyHdr)
+            pass
+        
+        return
+    
+    def stats( self, doIoStats = False ):
+
+        ## IO stats
+        if doIoStats:
+            self.ioStats()
+        
+        ## Overall stats
+        mean = Resource( 0., 0., 0. )
+        for d in self.data:
+            mean += d
+            pass
+        self.__printStat(mean)
+        return
+
+    def save( self, fileName = "iobench.log" ):
+        f = open( fileName, "w" )
+        for data in self.data:
+            f.writelines( "%s %s %s %s" % ( data.user(),
+                                            data.sys(),
+                                            data.real(),
+                                            os.linesep ) )
+            pass
+        f.close()
+        return
+    
+    pass # AthBench
+
+class BenchSequence(object):
+    """
+    Class which holds results of benchs (exit codes) and declares victory
+    when all the benchs returned successfully.
+    """
+
+    def __init__(self, name = "My Bench suite"):
+        object.__init__(self)
+        self.name   = name
+        self.benchs = []
+
+        return
+
+    def __iadd__(self, rhs):
+        if not isinstance( rhs, ScOutput ) and \
+           not isinstance( rhs, list ):
+            raise Exception, \
+                  "attempt to add a '%s' to the BenchSuite !" % \
+                  type(rhs).__name__
+        
+        if isinstance( rhs, ScOutput ):
+            self.benchs.append( rhs )
+            return self
+        
+        if isinstance( rhs, list ):
+            for i in rhs:
+                self += i
+                pass
+            return self
+
+        return self
+
+    def status(self):
+        import math
+        sc = 0
+        for i in self.benchs:
+            sc += math.fabs(i.sc)
+            pass
+        return sc == 0
+
+    def printStatus(self):
+        if self.status(): print "## [All tests SUCCESSFULLY completed]"
+        else:             print "## [ERROR in at least one test !!]"
+        return
+    
+    pass # BenchSequence
diff --git a/AtlasTest/TestTools/scripts/nightlies/CppUnitSGServiceTestExample.sh b/AtlasTest/TestTools/scripts/nightlies/CppUnitSGServiceTestExample.sh
new file mode 100755
index 0000000000000000000000000000000000000000..10aff1f8f737f2c7345ba6a1cfaef007cf5d870a
--- /dev/null
+++ b/AtlasTest/TestTools/scripts/nightlies/CppUnitSGServiceTestExample.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# MAILTO : undrus@bnl.gov
+echo " ======================================================== "
+echo " Starting CppUnitTestExample "
+echo " ======================================================== "
+if [ "$CMTSTRUCTURINGSTYLE" = "without_version_directory" ]; then
+cd ${NIGHTLYAREA}/Atlas*Release/cmt
+else
+cd ${NIGHTLYAREA}/Atlas*Release/*/cmt
+fi
+cmt broadcast -select=CppUnitSGServiceExample make CppUnit
+stat=$? 
+if [ "$stat" != "0"  ]; then
+        echo " ------------------------------------------ "
+        echo " FAILURE : test CppUnitSGServiceTestExample "
+        echo " ------------------------------------------ "
+fi
+
+
diff --git a/AtlasTest/TestTools/scripts/nightlies/CppUnitTestExample.sh b/AtlasTest/TestTools/scripts/nightlies/CppUnitTestExample.sh
new file mode 100755
index 0000000000000000000000000000000000000000..bf9bd180282bcb32b199fbede92a33df6cb2d09e
--- /dev/null
+++ b/AtlasTest/TestTools/scripts/nightlies/CppUnitTestExample.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# MAILTO : undrus@bnl.gov
+echo " ======================================================== "
+echo " Starting CppUnitTestExample "
+echo " ======================================================== "
+if [ "$CMTSTRUCTURINGSTYLE" = "without_version_directory" ]; then
+cd ${NIGHTLYAREA}/Atlas*Release/cmt
+else
+cd ${NIGHTLYAREA}/Atlas*Release/*/cmt
+fi
+cmt broadcast -select=CppUnitExample make CppUnit
+stat=$? 
+if [ "$stat" != "0"  ]; then
+        echo " -------------------------------- "
+        echo " FAILURE : test CppUnitTestExample "
+        echo " -------------------------------- "
+fi
+
+
diff --git a/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld.sh b/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a7cc188137f3dd52dea2c2588219a603955a6465
--- /dev/null
+++ b/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# ERROR_MESSAGE :FAILURE (ERROR)
+# SUCCESS_MESSAGE :FATAL A FATAL
+# MAILTO : undrus@bnl.gov
+
+# -----------------------------------------------------------
+# Author: Alex Undrus
+# -----------------------------------------------------------
+
+echo " ======================================================== "
+echo " Starting test with TestHelloWorld.py "
+echo " ======================================================== "
+athena.py AthExHelloWorld/HelloWorldOptions.py 
+stat=$? 
+if [ "$stat" != "0" ]; then
+        echo " -------------------------------- "
+        echo " FAILURE (ERROR) : test HelloWorld.py "
+        echo " -------------------------------- "
+fi
diff --git a/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld_XML.xml b/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld_XML.xml
new file mode 100755
index 0000000000000000000000000000000000000000..4f3ae79ce108e1b590e602d4c429d2f9e526b37a
--- /dev/null
+++ b/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld_XML.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<atn>
+   <TEST name="HelloWorld" type="athena" suite="Examples">
+      <options_atn>AthExHelloWorld/HelloWorldOptions.py</options_atn>
+      <timelimit>2</timelimit>
+      <author> Atlas Developer </author> 
+      <mailto> somebody@somewhere.ch </mailto>   
+      <expectations>
+         <errorMessage>FAILURE (ERROR)</errorMessage>
+         <successMessage>FATAL A FATAL</successMessage>
+         <returnValue>0</returnValue>
+      </expectations>
+   </TEST>
+</atn>
diff --git a/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld_script.xml b/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld_script.xml
new file mode 100755
index 0000000000000000000000000000000000000000..4c709f0cae21a4524e246f3eff8edcca55fb7620
--- /dev/null
+++ b/AtlasTest/TestTools/scripts/nightlies/TestHelloWorld_script.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<atn>
+   <TEST name="HelloWorld" type="script" suite="Examples">
+      <options_atn>AtlasTest/TestTools/scripts/nightlies/TestHelloWorld.sh</options_atn>
+      <timelimit>2</timelimit>
+      <author> Atlas Developer </author> 
+      <mailto> somebody@somewhere.ch </mailto>   
+      <expectations>
+         <errorMessage>FAILURE (ERROR)</errorMessage>
+         <successMessage>FATAL A FATAL</successMessage>
+         <returnValue>0</returnValue>
+      </expectations>
+   </TEST>
+</atn>
diff --git a/AtlasTest/TestTools/share/IoAuditor_fragment.py b/AtlasTest/TestTools/share/IoAuditor_fragment.py
new file mode 100755
index 0000000000000000000000000000000000000000..7b369c3f3e70a1db8ae447952722fb33338cc555
--- /dev/null
+++ b/AtlasTest/TestTools/share/IoAuditor_fragment.py
@@ -0,0 +1,19 @@
+#
+# write out a summary of the time spent
+#
+from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+theAuditorSvc = svcMgr.AuditorSvc
+theAuditorSvc.Auditors  += [ "ChronoAuditor"]
+
+svcMgr.ChronoStatSvc.ChronoDestinationCout = True
+
+svcMgr.ChronoStatSvc.PrintUserTime     = True
+svcMgr.ChronoStatSvc.PrintSystemTime   = True
+svcMgr.ChronoStatSvc.PrintEllapsedTime = True
+
+svcMgr.ChronoStatSvc.AsciiStatsOutputFile = "chronoStats.ascii"
+
+svcMgr.AthenaPoolCnvSvc.UseDetailChronoStat = True
+
+from AthenaCommon.AppMgr import theApp
+theApp.AuditAlgorithms = True
diff --git a/AtlasTest/TestTools/share/post.sh b/AtlasTest/TestTools/share/post.sh
new file mode 100755
index 0000000000000000000000000000000000000000..631343afa7b0eb175c6014ff9272116d41189545
--- /dev/null
+++ b/AtlasTest/TestTools/share/post.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#/** @file post.sh
+# @brief sh script that check the return code of an executable and compares
+# its output with a reference (if available).
+# @param test_name
+#
+# @author Scott Snyder <snyder@fnal.gov> - ATLAS Collaboration.
+# @author Paolo Calafiura <pcalafiura@lbl.gov> - ATLAS Collaboration.
+# $Id: post.sh,v 1.23 2007-11-10 00:00:07 calaf Exp $
+# **/
+test=$1
+extrapatterns="$2"
+#verbose="1"
+if [ "$POST_SH_NOCOLOR" = "" ]; then
+ GREEN=""
+ YELLOW=""
+ RED=""
+ RESET=""
+else
+ GREEN=""
+ YELLOW=""
+ RED=""
+ RESET=""
+fi
+
+# ignore diff annotations
+PP='^---|^[[:digit:]]+[acd,][[:digit:]]+'
+
+# ignore hex addresses
+PP="$PP"'|0x\w{4,}'
+
+# ignore package names e.g. Package-00-00-00
+PP="$PP"'|\w+-[[:digit:]]{2}-[[:digit:]]{2}-[[:digit:]]{2}'
+# ignore trunk package names e.g. Package-r123456
+PP="$PP"'|\w+-r[[:digit:]]+'
+# ignore cpu usage printouts
+PP="$PP"'|ChronoStatSvc +INFO Time'
+PP="$PP"'|Time left.+ Seconds'
+PP="$PP"'|Timeleft.+ sec'
+PP="$PP"'|INFO Time User'
+# ignore clid db file name
+PP="$PP"'|from CLIDDB file'
+# ignore slug machine printout
+PP="$PP"'| Machine: .* System and Processor Info'
+PP="$PP"'| Jobname = .* Machine ='
+# ignore slug pid printout
+PP="$PP"'|Atlas Detector Simulation, Reconstruction and Analysis Running on'
+PP="$PP"'|Program:  Slug-Dice-Arecon .+ pid +[[:digit:]]+'
+#ignore DllClassManager DEBUG messages
+PP="$PP"'|DllClassManager     DEBUG'
+# ignore slug Library printout
+PP="$PP"'|Library of +[[:digit:]]+ at +[[:digit:]]+'
+PP="$PP"'|Library compiled on +[[:digit:]]'
+# ignore ClassIDSvc "in memory db" printouts
+PP="$PP"'|CLID: .* - type name:'
+# ignore ClassIDSvc "already set" printouts
+PP="$PP"'|ClassIDSvc .* setTypeNameForID: .* already set for'
+# ignore ClassIDSvc finalize output
+PP="$PP"'|ClassIDSvc .* finalize: wrote .*'
+# ignore rcs version comments
+PP="$PP"'|Id: .+ Exp \$'
+# ignore plugin count
+PP="$PP"'|PluginMgr            INFO loaded plugin info for'
+# ignore HistorySvc registered count
+PP="$PP"'|HistorySvc           INFO Registered'
+# ignore clid registry entries count
+PP="$PP"'|ClassIDSvc           INFO  getRegistryEntries: read'
+# ignore existsDir path WARNINGS
+PP="$PP"'|DirSearchPath::existsDir: WARNING not a directory'
+# ignore warnings about duplicate services/converters.
+PP="$PP"'|Service factory for type [^ ]+ already declared'
+PP="$PP"'|Converter for class:[^ ]+ already exists'
+# Number of configurables read can vary from build to build.
+PP="$PP"'|INFO Read module info for'
+# ignore ApplicationMgr header.
+PP="$PP"'|^ApplicationMgr *SUCCESS *$'
+PP="$PP"'|^=+$'
+PP="$PP"'|^ *Welcome to ApplicationMgr'
+PP="$PP"'|^ *running on .* on '
+PP="$PP"'|//GP: '
+#ignore which malloc we are using
+PP="$PP"'|^Preloading tcmalloc'
+PP="$PP"'|^WARNING: TCMALLOCDIR not defined'
+#Sebastien says not to worry about this...
+PP="$PP"'|^Py:AthFile .*shutting down athfile-server'
+PP="$PP"'|^HistogramPersis...   INFO *.CnvServices.:'
+PP="$PP"'|StatusCodeSvc        INFO initialize'
+PP="$PP"'|^ApplicationMgr +INFO Successfully loaded'
+PP="$PP"'|^IncidentSvc +DEBUG Service base class'
+PP="$PP"'|^ClassIDSvc +WARNING Could not resolve clid DB path notthere.db'
+PP="$PP"'|^IncidentSvc         DEBUG Adding .* listener '.*' with priority .*'
+PP="$PP"'|MessageSvc not found, will use std::cerr'
+PP="$PP"'|^AtRndmGenSvc         INFO Initializing AtRndmGenSvc'
+PP="$PP"'|^AtRanluxGenSvc2      INFO Initializing AtRanluxGenSvc2'
+PP="$PP"'|^AtRanluxGenSvc       INFO Initializing AtRanluxGenSvc'
+#ignore personal .athenarc files
+PP="$PP"'|including file \"\$HOME/.athenarc'
+#ignore known gaudi python warning
+PP="$PP"'|Bindings.py:660: DeprecationWarning'
+
+# Gaudi 31
+PP="$PP"'|PluginService::SetDebug|setting LC_ALL'
+
+
+if [ "$extrapatterns" != "" ]; then
+ PP="$PP""|$extrapatterns"
+fi
+
+if [ -z "$testStatus" ]
+   then
+   echo "$YELLOW post.sh> Warning: athena exit status is not available $RESET"
+else
+   # check exit status
+   joblog=${test}.log
+   if [ "$testStatus" = 0 ]
+       then
+       if [ "$verbose" != "" ]; then
+         echo "$GREEN post.sh> OK: ${test} exited normally. Output is in $joblog $RESET"
+       fi
+       reflog=../share/${test}.ref
+       if [ -r $reflog ]
+           then
+           jobdiff=${joblog}-todiff
+           refdiff=`basename ${reflog}`-todiff
+           egrep -a -v "$PP" < $joblog > $jobdiff
+           egrep -a -v "$PP" < $reflog > $refdiff
+           diff -a -b -B -u $jobdiff $refdiff
+           diffStatus=$?
+           if [ $diffStatus != 0 ] ; then
+               echo "$RED post.sh> ERROR: $joblog and $reflog differ $RESET"
+           else
+               if [ "$verbose" != "" ]; then
+                 echo "$GREEN post.sh> OK: $joblog and $reflog identical $RESET"
+               fi
+           fi
+       else
+           tail $joblog
+           echo "$YELLOW post.sh> WARNING: reference output $reflog not available $RESET"
+           echo  " post.sh> Please check ${PWD}/$joblog"
+       fi
+   else
+       tail $joblog
+       echo  "$RED post.sh> ERROR: Athena exited abnormally! Exit code: $testStatus $RESET"
+       echo  " post.sh> Please check ${PWD}/$joblog"
+   fi
+fi
+exit $testStatus
diff --git a/AtlasTest/TestTools/share/runUnitTests.sh b/AtlasTest/TestTools/share/runUnitTests.sh
new file mode 100755
index 0000000000000000000000000000000000000000..acba3fbbf2c50c5dc3a566d3f57c5b99f9f32567
--- /dev/null
+++ b/AtlasTest/TestTools/share/runUnitTests.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#/** @file runUnitTests.sh
+# @brief sh script that cmt broadcasts gmake check and filters out its output
+# @param opts options to be passed to cmt (eg -select=Store)
+# @author Paolo Calafiura <pcalafiura@lbl.gov> - ATLAS Collaboration.
+# $Id: runUnitTests.sh,v 1.1 2003-04-04 01:31:24 calaf Exp $
+# **/
+#if [ "$#" = 0 ]
+#    then
+#    opts=-
+#else
+    opts="$@"
+#fi
+
+joblog=gmakecheck.log
+cmt bro "$opts" gmake check 2>&1 | tee $joblog | egrep "(post.sh>|Now trying)" 
+tail -1 $joblog | grep -q "check ok"
+rc=$?
+if [ "$rc" = 0 ]
+    then 
+    echo "OK: gmake check exited normally. Output is in $joblog"
+else
+    tail $joblog
+    echo  "ERROR: gmake check exited abnormally!"
+
+    echo  " Please check ${PWD}/$joblog"
+fi
diff --git a/AtlasTest/TestTools/src/initGaudi.cxx b/AtlasTest/TestTools/src/initGaudi.cxx
new file mode 100755
index 0000000000000000000000000000000000000000..67a622f14cc049c6921632baf9cc6e26bf57956f
--- /dev/null
+++ b/AtlasTest/TestTools/src/initGaudi.cxx
@@ -0,0 +1,77 @@
+/*
+  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+*/
+
+/***************************************************************************
+ minimal gaudi initialization for AthenaServices unit testing
+ ------------------------------------------------------------
+ ATLAS Collaboration
+ ***************************************************************************/
+
+// $Id: initGaudi.cxx,v 1.5 2005-05-04 00:39:51 calaf Exp $
+
+//<<<<<< INCLUDES                                                       >>>>>>
+
+#include "TestTools/initGaudi.h"
+
+#include "GaudiKernel/Bootstrap.h"
+#include "GaudiKernel/IProperty.h"
+#include "GaudiKernel/ISvcManager.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/IAppMgrUI.h"
+#include "GaudiKernel/SmartIF.h"
+using std::cout;
+using std::endl;
+using std::string;
+
+namespace Athena_test {
+  bool initGaudi(ISvcLocator*& pSvcLoc) {
+    return initGaudi(string(), pSvcLoc); //wily hack
+  }
+  bool initGaudi(const std::string& jobOptsFile, ISvcLocator*& pSvcLoc) {
+    string jobOptsPath = jobOptsFile;
+    if (access (jobOptsPath.c_str(), R_OK) != 0)
+      jobOptsPath = "../share/"+jobOptsFile;
+    bool noJobOpts(jobOptsFile.empty());
+    if (!noJobOpts) {
+      cout << "\n\nInitializing Gaudi ApplicationMgr using job opts " << jobOptsPath << endl;
+    }
+
+    // Create an instance of an application manager
+    IInterface* iface = Gaudi::createApplicationMgr();
+    if( 0 == iface ) {
+      cout << "Fatal error while creating the ApplicationMgr " << endl;
+      return false;
+    }
+
+    SmartIF<ISvcManager> svcMgr(iface);
+    SmartIF<IAppMgrUI> appMgr(iface);
+    SmartIF<IProperty> propMgr(iface);
+    SmartIF<ISvcLocator> svcLoc(iface);
+    if(!svcLoc.isValid() || !appMgr.isValid() || !svcMgr.isValid() || !propMgr.isValid()) {
+      cout << "Fatal error while creating the AppMgr smart if " << endl;
+      return false;
+    }
+
+    pSvcLoc = svcLoc.pRef();
+
+    (propMgr->setProperty( "EvtSel",         "NONE" )).ignore();
+    if (noJobOpts) {
+      (propMgr->setProperty( "JobOptionsType", "NONE" )).ignore();
+    } else {
+      (propMgr->setProperty( "JobOptionsType", "FILE" )).ignore();
+      (propMgr->setProperty( "JobOptionsPath", jobOptsPath )).ignore();
+    }
+
+    if ((appMgr->configure()).isSuccess() &&
+	(appMgr->initialize()).isSuccess()) {
+      cout<<"ApplicationMgr Ready"<<endl;
+      return true;
+    } else {
+      cout << "Fatal error while initializing the AppMgr" << endl;
+      return false;
+    }
+  }
+}
+
+
diff --git a/AtlasTest/TestTools/test/test_iobench.py b/AtlasTest/TestTools/test/test_iobench.py
new file mode 100755
index 0000000000000000000000000000000000000000..3b23150c85d3a687663b0f422aa73375a6066c3b
--- /dev/null
+++ b/AtlasTest/TestTools/test/test_iobench.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+
+# @file:    test_iobench.py
+# @purpose: unit test file for the iobench module
+# @author:  Sebastien Binet <binet@cern.ch>
+# @date:    July 2006
+
+import user
+import sys
+from TestTools    import iobench
+from AthenaCommon import ChapPy
+
+if __name__ == "__main__":
+    print "#"*80
+    print "## testing iobench ..."
+
+    jobOptions = [
+        ChapPy.JobOptionsCmd( "OUTPUT=\"/tmp/slimmed.aod.pool\"" ),
+        ChapPy.JobOptions( "McParticleAlgs/test_WriteMcAod_jobOptions.py" )
+        ]
+    athena = ChapPy.Athena( jobOptions = jobOptions,
+                            checkLeak  = True )
+    athena.EvtMax = 100
+
+    bench = iobench.AthBench( athena,
+                              nTimes = 10 )
+    print "## bench:"
+    print bench.athena
+    bench.run()
+
+    bench.ioStats( [ "GEN_AOD", "GEN_EVENT", "SpclMC" ], "w" )
+    bench.save( "iobench-%ievts.log" % athena.EvtMax )
+    bench.stats()
+
+    print ""
+    print "## Bye."
+    print "#"*80
+
+    # a possible output...
+    """
+    ## stats : [GEN_AOD] (nbenchs = 10)
+     time<User> =     1231.000 us/evt
+     time<Sys > =        2.000 us/evt
+     time<Real> =     1210.000 us/evt
+    
+    ## stats : [GEN_EVENT] (nbenchs = 10)
+     time<User> =     1553.000 us/evt
+     time<Sys > =        4.000 us/evt
+     time<Real> =     1577.000 us/evt
+    
+    ## stats : [SpclMC] (nbenchs = 10)
+     time<User> =     1457.000 us/evt
+     time<Sys > =        4.000 us/evt
+     time<Real> =     1560.000 us/evt
+
+    ## stats : [Overall] (nbenchs = 10)
+     time<User> =       26.931 s
+     time<Sys > =        0.749 s
+     time<Real> =       41.064 s
+    """