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="[92;1m" + YELLOW="[93;1m" + RED="[97;101;1m" + RESET="[m" +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 + """