Commit 35b1bdd346b05fec163c959cb8cc27985adb4c74

Authored by Jürgen Knödlseder
1 parent d7f7af88

Add base class unit test to enhance coverage (#1828)

pyext/ctlikelihood.i
... ... @@ -26,9 +26,44 @@
26 26  
27 27 %{
28 28 /* Put headers and other declarations here that are needed for compilation */
  29 +#define SWIG
29 30 #include "ctlikelihood.hpp"
  31 +
  32 +
  33 +/***********************************************************************//**
  34 + * @class cslikelihood
  35 + *
  36 + * @brief cslikelihood base class
  37 + *
  38 + * This class is a non-abstract C++ implementation of the ctlikelihood base
  39 + * class. It serves as Python base class since Python does not know about
  40 + * abstract classes.
  41 + ***************************************************************************/
  42 +class cslikelihood : public ctlikelihood {
  43 +public:
  44 + // Constructors and destructors
  45 + cslikelihood(const std::string& name, const std::string& version) :
  46 + ctlikelihood(name, version) {}
  47 + cslikelihood(const std::string& name, const std::string& version,
  48 + const GObservations& obs) :
  49 + ctlikelihood(name, version, obs) {}
  50 + cslikelihood(const std::string& name, const std::string& version,
  51 + int argc, char* argv[]) :
  52 + ctlikelihood(name, version, argc, argv){}
  53 + cslikelihood(const cslikelihood& app) : ctlikelihood(app) {}
  54 + virtual ~cslikelihood(void) {}
  55 +
  56 + // Dummy methods (implementation makes this class non-abstract)
  57 + virtual void clear(void) {}
  58 + virtual void run(void) {}
  59 + virtual void save(void) {}
  60 +};
30 61 %}
31 62  
  63 +// Include (int ARGC, char **ARGV) typemap to allow passing command line
  64 +// arguments to GApplication constructor
  65 +%include "argcargv.i"
  66 +
32 67  
33 68 /***********************************************************************//**
34 69 * @class ctlikelihood
... ... @@ -54,11 +89,65 @@ public:
54 89  
55 90 // Methods
56 91 const GOptimizer* opt(void) const;
  92 +
  93 + // Make methods private in Python by prepending an underscore
  94 + %rename(_evaluate) evaluate;
  95 +
  96 + // Protected methods
  97 + double evaluate(GModelPar& par, const double& value);
  98 +};
  99 +
  100 +
  101 +/***********************************************************************//**
  102 + * @class cslikelihood
  103 + *
  104 + * @brief Base class for likelihood scripts
  105 + *
  106 + * This is the base class from which likelihood scripts should derive.
  107 + ***************************************************************************/
  108 +class cslikelihood : public ctlikelihood {
  109 +public:
  110 + // Constructors and destructors
  111 + cslikelihood(const std::string& name, const std::string& version);
  112 + cslikelihood(const std::string& name, const std::string& version,
  113 + const GObservations& obs);
  114 + cslikelihood(const std::string& name, const std::string& version,
  115 + int ARGC, char **ARGV);
  116 + cslikelihood(const cslikelihood& app);
  117 + virtual ~cslikelihood(void);
  118 +
  119 + // Methods
  120 + virtual void clear(void);
  121 + virtual void run(void);
  122 + virtual void save(void);
57 123 };
58 124  
59 125  
60 126 /***********************************************************************//**
61   - * @brief Likelihood tool Python extensions
  127 + * @brief Likelihood tool base class C++ extensions
62 128 ***************************************************************************/
63 129 %extend ctlikelihood {
64 130 }
  131 +
  132 +
  133 +/***********************************************************************//**
  134 + * @brief Likelihood tool base class Python extensions
  135 + ***************************************************************************/
  136 +%pythoncode %{
  137 +
  138 +# Initialise application by calling the appropriate base class constructor.
  139 +# The function supports either an observation container, or an argument
  140 +# list or no argument as "argv" parameter. The function also writes the
  141 +# header in the log file and switches the date on for logging.
  142 +def _init_cslikelihood(self, argv):
  143 + if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
  144 + cslikelihood.__init__(self, self._name, self._version, argv[0])
  145 + elif len(argv) > 0:
  146 + cslikelihood.__init__(self, self._name, self._version, *argv)
  147 + else:
  148 + cslikelihood.__init__(self, self._name, self._version)
  149 + # Set logger properties
  150 + self._log_header()
  151 + self._log.date(True)
  152 +cslikelihood._init_cslikelihood = _init_cslikelihood
  153 +%}
... ...
pyext/ctobservation.i
... ... @@ -26,14 +26,49 @@
26 26  
27 27 %{
28 28 /* Put headers and other declarations here that are needed for compilation */
  29 +#define SWIG
29 30 #include "ctobservation.hpp"
  31 +
  32 +
  33 +/***********************************************************************//**
  34 + * @class csobservation
  35 + *
  36 + * @brief csobservation base class
  37 + *
  38 + * This class is a non-abstract C++ implementation of the ctobservation base
  39 + * class. It serves as Python base class since Python does not know about
  40 + * abstract classes.
  41 + ***************************************************************************/
  42 +class csobservation : public ctobservation {
  43 +public:
  44 + // Constructors and destructors
  45 + csobservation(const std::string& name, const std::string& version) :
  46 + ctobservation(name, version) {}
  47 + csobservation(const std::string& name, const std::string& version,
  48 + const GObservations& obs) :
  49 + ctobservation(name, version, obs) {}
  50 + csobservation(const std::string& name, const std::string& version,
  51 + int argc, char* argv[]) :
  52 + ctobservation(name, version, argc, argv){}
  53 + csobservation(const csobservation& app) : ctobservation(app) {}
  54 + virtual ~csobservation(void) {}
  55 +
  56 + // Dummy methods (implementation makes this class non-abstract)
  57 + virtual void clear(void) {}
  58 + virtual void run(void) {}
  59 + virtual void save(void) {}
  60 +};
30 61 %}
31 62  
  63 +// Include (int ARGC, char **ARGV) typemap to allow passing command line
  64 +// arguments to GApplication constructor
  65 +%include "argcargv.i"
  66 +
32 67  
33 68 /***********************************************************************//**
34 69 * @class ctobservation
35 70 *
36   - * @brief Base class for likelihood tools
  71 + * @brief Base class for observation tools
37 72 ***************************************************************************/
38 73 class ctobservation : public ctool {
39 74  
... ... @@ -43,7 +78,7 @@ public:
43 78 ctobservation(const std::string& name, const std::string& version,
44 79 const GObservations& obs);
45 80 ctobservation(const std::string& name, const std::string& version,
46   - int argc, char* argv[]);
  81 + int ARGC, char **ARGV);
47 82 ctobservation(const ctobservation& app);
48 83 virtual ~ctobservation(void);
49 84  
... ... @@ -66,6 +101,31 @@ public:
66 101  
67 102  
68 103 /***********************************************************************//**
  104 + * @class csobservation
  105 + *
  106 + * @brief Base class for observation scripts
  107 + *
  108 + * This is the base class from which observation scripts should derive.
  109 + ***************************************************************************/
  110 +class csobservation : public ctobservation {
  111 +public:
  112 + // Constructors and destructors
  113 + csobservation(const std::string& name, const std::string& version);
  114 + csobservation(const std::string& name, const std::string& version,
  115 + const GObservations& obs);
  116 + csobservation(const std::string& name, const std::string& version,
  117 + int ARGC, char **ARGV);
  118 + csobservation(const csobservation& app);
  119 + virtual ~csobservation(void);
  120 +
  121 + // Methods
  122 + virtual void clear(void);
  123 + virtual void run(void);
  124 + virtual void save(void);
  125 +};
  126 +
  127 +
  128 +/***********************************************************************//**
69 129 * @brief Observation tool base class C++ extensions
70 130 ***************************************************************************/
71 131 %extend ctobservation {
... ... @@ -76,6 +136,24 @@ public:
76 136 * @brief Observation tool base class Python extensions
77 137 ***************************************************************************/
78 138 %pythoncode %{
  139 +
  140 +# Initialise application by calling the appropriate base class constructor.
  141 +# The function supports either an observation container, or an argument
  142 +# list or no argument as "argv" parameter. The function also writes the
  143 +# header in the log file and switches the date on for logging.
  144 +def _init_csobservation(self, argv):
  145 + if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
  146 + csobservation.__init__(self, self._name, self._version, argv[0])
  147 + elif len(argv) > 0:
  148 + csobservation.__init__(self, self._name, self._version, *argv)
  149 + else:
  150 + csobservation.__init__(self, self._name, self._version)
  151 + # Set logger properties
  152 + self._log_header()
  153 + self._log.date(True)
  154 +csobservation._init_csobservation = _init_csobservation
  155 +
  156 +# Define an iterator over all observations
79 157 def _unbinned_observations(self):
80 158 obs = self._first_unbinned_observation()
81 159 while obs != None:
... ...
pyext/ctool.i
... ... @@ -86,12 +86,12 @@ public:
86 86 %rename(_time_reference) time_reference;
87 87 %rename(_get_observations) get_observations;
88 88 %rename(_setup_observations) setup_observations;
  89 + %rename(_setup_models) setup_models;
89 90 %rename(_create_ebounds) create_ebounds;
90 91 %rename(_create_map) create_map;
91 92 %rename(_create_cube) create_cube;
92 93 %rename(_create_cta_obs) create_cta_obs;
93 94 %rename(_require_inobs) require_inobs;
94   - %rename(_require_inobs_nolist) require_inobs_nolist;
95 95 %rename(_require_inobs_nocube) require_inobs_nocube;
96 96 %rename(_log_observations) log_observations;
97 97 %rename(_log_models) log_models;
... ... @@ -128,9 +128,11 @@ public:
128 128  
129 129 // Protected methods that check user parameters
130 130 void require_inobs(const std::string& method);
131   - void require_inobs_nolist(const std::string& method);
132 131 void require_inobs_nocube(const std::string& method);
133 132  
  133 + // Protected methods that extract user parameters
  134 + GCTARoi get_roi(void);
  135 +
134 136 // Protected methods for logging
135 137 void log_observations(const GChatter& chatter,
136 138 const GObservations& obs,
... ... @@ -139,9 +141,6 @@ public:
139 141 const GModels& models,
140 142 const std::string& what = "Model");
141 143  
142   - // Protected methods that extract user parameters
143   - GCTARoi get_roi(void);
144   -
145 144 // Protected support methods
146 145 void set_response(GObservations& obs);
147 146 std::vector<bool> set_edisp(GObservations& obs, const bool& edisp) const;
... ...
src/ctulimit/ctulimit.par
... ... @@ -38,7 +38,7 @@ eref, r, h, 1.0,,, &quot;Reference energy for differential limit (TeV)&quot;
38 38 emin, r, h, 1.0,,, "Minimum energy for integral flux limit (TeV)"
39 39 emax, r, h, 100.0,,, "Maximum energy for integral flux limit (TeV)"
40 40 tol, r, h, 1e-5,,, "Computation tolerance"
41   -max_iter, i, h, 50,,, "Maximum number of iterations"
  41 +max_iter, i, h, 50,1,1000, "Maximum number of iterations"
42 42  
43 43 #
44 44 # Standard parameters
... ...
src/support/ctlikelihood.hpp
... ... @@ -61,15 +61,20 @@ public:
61 61 // Methods
62 62 const GOptimizer* opt(void) const;
63 63  
  64 +#ifndef SWIG
  65 +protected:
  66 +#endif
  67 + // Protected methods in C++, public in SWIG interface
  68 + double evaluate(GModelPar& par, const double& value);
  69 +
64 70 protected:
65 71 // Protected methods
66 72 void init_members(void);
67 73 void copy_members(const ctlikelihood& app);
68 74 void free_members(void);
69   - double evaluate(GModelPar& par, const double& value);
70 75  
71 76 // Protected members
72   - GOptimizerLM m_opt; //!< Optimizer
  77 + GOptimizerLM m_opt; //!< Optimizer
73 78 };
74 79  
75 80  
... ...
src/support/ctobservation.hpp
... ... @@ -64,9 +64,7 @@ public:
64 64 // Methods
65 65 const GObservations& obs(void) const;
66 66  
67   -#ifdef SWIG
68   -public:
69   -#else
  67 +#ifndef SWIG
70 68 protected:
71 69 #endif
72 70 // Iterator methods and members
... ...
src/support/ctool.cpp
... ... @@ -549,10 +549,10 @@ GEbounds ctool::create_ebounds(void)
549 549 file.close();
550 550 std::string msg = "No extension with name \"EBOUNDS\" or"
551 551 " \"ENERGYBINS\" found in FITS file"
552   - " \""+ebinfile+"\".\n"
553   - "An \"EBOUNDS\" or \"ENERGYBINS\" extension"
554   - " is required if the parameter \"ebinalg\""
555   - " is set to \"FILE\".";
  552 + " \""+ebinfile+"\". An \"EBOUNDS\" or "
  553 + "\"ENERGYBINS\" extension is required if "
  554 + "the parameter \"ebinalg\" is set to "
  555 + "\"FILE\".";
556 556 throw GException::invalid_value(G_CREATE_EBOUNDS, msg);
557 557 }
558 558 }
... ... @@ -788,6 +788,9 @@ GCTAObservation ctool::create_cta_obs(void)
788 788 *
789 789 * Throw an exception if the "inobs" parameter is either "NONE" or an empty
790 790 * string.
  791 + *
  792 + * @todo This method is only used by csobsinfo, csresmap and csspec. We
  793 + * should try to get rid of it
791 794 ***************************************************************************/
792 795 void ctool::require_inobs(const std::string& method)
793 796 {
... ... @@ -810,63 +813,13 @@ void ctool::require_inobs(const std::string&amp; method)
810 813  
811 814  
812 815 /***********************************************************************//**
813   - * @brief Throws exception if inobs parameter is an event list
814   - *
815   - * @param[in] method Method name.
816   - *
817   - * Throw an exception if the inobs parameter is an event list.
818   - ***************************************************************************/
819   -void ctool::require_inobs_nolist(const std::string& method)
820   -{
821   - // Get inobs filename
822   - GFilename filename = (*this)["inobs"].filename();
823   -
824   - // Continue only if we have a FITS file
825   - if (filename.is_fits()) {
826   -
827   - // Signal no list
828   - bool is_list = false;
829   -
830   - // Try loading file as counts cube. If this is successful then
831   - // throw an exception
832   - try {
833   -
834   - // Load list from file
835   - GCTAEventList list(filename);
836   -
837   - // If we're still alive then signal that we have a list
838   - is_list = true;
839   -
840   - }
841   -
842   - // Catch any exceptions
843   - catch (...) {
844   - ;
845   - }
846   -
847   - // If we have an event list then throw an exception
848   - if (is_list) {
849   - std::string msg = "An event list has been specified for the "
850   - "\"inobs\" parameter, yet no event list "
851   - "can be specified as input observation."
852   - " Instead, specify a counts cube or an "
853   - "observation definition file.";
854   - throw GException::invalid_value(method, msg);
855   - }
856   -
857   - } // endif: we had a FITS file
858   -
859   - // Return
860   - return;
861   -}
862   -
863   -
864   -/***********************************************************************//**
865 816 * @brief Throws exception if inobs parameter is a counts cube
866 817 *
867 818 * @param[in] method Method name.
868 819 *
869 820 * Throw an exception if the inobs parameter is a counts cube.
  821 + *
  822 + * @todo This method is only used by ctobssim. We should try to get rid of it
870 823 ***************************************************************************/
871 824 void ctool::require_inobs_nocube(const std::string& method)
872 825 {
... ... @@ -1542,6 +1495,9 @@ GSkyDir ctool::get_mean_pointing(const GObservations&amp; obs)
1542 1495 * @brief Get current resident set size (physical memory use) in Bytes
1543 1496 *
1544 1497 * @return Physical memory use in Bytes.
  1498 + *
  1499 + * @todo This method is currently not used and can be removed if we do not
  1500 + * use it
1545 1501 ***************************************************************************/
1546 1502 size_t ctool::get_current_rss(void)
1547 1503 {
... ...
test/test_cscript.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This scripts performs unit tests for the cspull script.
  4 +#
  5 +# Copyright (C) 2016 Juergen Knoedlseder
  6 +#
  7 +# This program is free software: you can redistribute it and/or modify
  8 +# it under the terms of the GNU General Public License as published by
  9 +# the Free Software Foundation, either version 3 of the License, or
  10 +# (at your option) any later version.
  11 +#
  12 +# This program is distributed in the hope that it will be useful,
  13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 +# GNU General Public License for more details.
  16 +#
  17 +# You should have received a copy of the GNU General Public License
  18 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  19 +#
  20 +# ==========================================================================
  21 +import gammalib
  22 +import ctools
  23 +from testing import test
  24 +
  25 +
  26 +# ================== #
  27 +# cscript_test class #
  28 +# ================== #
  29 +class cscript_test(ctools.cscript):
  30 + """
  31 + Test class for the cscript base class
  32 + """
  33 + # Constructor
  34 + def __init__(self, *argv):
  35 + """
  36 + Constructor
  37 + """
  38 + # Set name
  39 + self._name = 'cscript_test'
  40 + self._version = '1.2.0'
  41 +
  42 + # Initialise observation container from constructor arguments
  43 + self._obs, argv = self._set_input_obs(argv)
  44 +
  45 + # Initialise application by calling the appropriate class constructor
  46 + self._init_cscript(argv)
  47 +
  48 + # Return
  49 + return
  50 +
  51 + # Check is_valid_filename() method
  52 + def _check_is_valid_filename(self, filename):
  53 + return self._is_valid_filename(gammalib.GFilename(filename))
  54 +
  55 + # Check setup_observations() method
  56 + def _check_setup_observations(self, filename, list=True, cube=True):
  57 + self.pars()['inobs'].value(filename)
  58 + obs = gammalib.GObservations()
  59 + self._setup_observations(obs, True, list, cube)
  60 + return
  61 +
  62 + # Check setup_models() method
  63 + def _check_setup_models(self):
  64 + obs = gammalib.GObservations()
  65 + self._setup_models(obs)
  66 + return
  67 +
  68 + # Check create_ebounds() method
  69 + def _check_create_ebounds(self, filename):
  70 + self.pars()['ebinfile'].value(filename)
  71 + return self._create_ebounds()
  72 +
  73 + # Check require_inobs() method
  74 + def _check_require_inobs(self, filename):
  75 + self.pars()['inobs'].value(filename)
  76 + self._require_inobs('_check_require_inobs')
  77 + return
  78 +
  79 + # Check get_roi() method
  80 + def _check_get_roi(self):
  81 + return self._get_roi()
  82 +
  83 + # Check restore_edisp() method
  84 + def _check_restore_edisp(self):
  85 + obs = gammalib.GObservations()
  86 + edisp = [True, True]
  87 + self._restore_edisp(obs, edisp)
  88 + return
  89 +
  90 + # Check set_obs_bounds() method
  91 + def _check_set_obs_bounds(self):
  92 + obs = gammalib.GObservations()
  93 + list = gammalib.GCTAEventList()
  94 + cta = gammalib.GCTAObservation()
  95 + cta.events(list)
  96 + obs.append(cta)
  97 + self._set_obs_bounds(obs)
  98 + return obs.copy()
  99 +
  100 + # Check get_mean_pointing() method
  101 + def _check_get_mean_pointing(self):
  102 + obs = gammalib.GObservations()
  103 + return self._get_mean_pointing(obs)
  104 +
  105 +
  106 +# ======================== #
  107 +# ctobservation_test class #
  108 +# ======================== #
  109 +class ctobservation_test(ctools.csobservation):
  110 + """
  111 + Test class for the ctobservation base class
  112 + """
  113 + # Constructor
  114 + def __init__(self, *argv):
  115 + """
  116 + Constructor
  117 + """
  118 + # Set name
  119 + self._name = 'ctobservation_test'
  120 + self._version = '1.2.0'
  121 +
  122 + # Initialise application by calling the appropriate class constructor
  123 + self._init_csobservation(argv)
  124 +
  125 + # Return
  126 + return
  127 +
  128 +
  129 +# ======================= #
  130 +# ctlikelihood_test class #
  131 +# ======================= #
  132 +class ctlikelihood_test(ctools.cslikelihood):
  133 + """
  134 + Test class for the ctlikelihood base class
  135 + """
  136 + # Constructor
  137 + def __init__(self, *argv):
  138 + """
  139 + Constructor
  140 + """
  141 + # Set name
  142 + self._name = 'ctlikelihood_test'
  143 + self._version = '1.2.0'
  144 +
  145 + # Initialise application by calling the appropriate class constructor
  146 + self._init_cslikelihood(argv)
  147 +
  148 + # Return
  149 + return
  150 +
  151 + # Check evaluate() method
  152 + def _check_evaluate(self, value):
  153 + par = gammalib.GModelPar()
  154 + par.value(2.0)
  155 + par.range(1.0, 3.0)
  156 + return self._evaluate(par, value)
  157 +
  158 +
  159 +# ============================ #
  160 +# Test class for base classes #
  161 +# ============================ #
  162 +class Test(test):
  163 + """
  164 + Test class for base classes
  165 + """
  166 + # Constructor
  167 + def __init__(self):
  168 + """
  169 + Constructor
  170 + """
  171 + # Call base class constructor
  172 + test.__init__(self)
  173 +
  174 + # Create parameter file for cscript_test application
  175 + pars = gammalib.GApplicationPars()
  176 + pars.append(gammalib.GApplicationPar('inobs','f','h','NONE','','',''))
  177 + pars.append(gammalib.GApplicationPar('inmodel','f','h','NONE','','',''))
  178 + pars.append(gammalib.GApplicationPar('ebinalg','s','h','FILE','','',''))
  179 + pars.append(gammalib.GApplicationPar('ebinfile','f','h','NONE','','',''))
  180 + pars.append(gammalib.GApplicationPar('ra','r','h','NONE','','',''))
  181 + pars.append(gammalib.GApplicationPar('dec','r','h','NONE','','',''))
  182 + pars.append(gammalib.GApplicationPar('rad','r','h','1.0','','',''))
  183 + pars.append(gammalib.GApplicationPar('emin','r','h','1.0','','',''))
  184 + pars.append(gammalib.GApplicationPar('emax','r','h','10.0','','',''))
  185 + pars.append_standard()
  186 + pars.save('cscript_test.par')
  187 +
  188 + # Create parameter file for ctobservation_test.par application
  189 + pars = gammalib.GApplicationPars()
  190 + pars.append_standard()
  191 + pars.save('ctobservation_test.par')
  192 +
  193 + # Create parameter file for ctlikelihood_test application
  194 + pars = gammalib.GApplicationPars()
  195 + pars.append_standard()
  196 + pars.save('ctlikelihood_test.par')
  197 +
  198 + # Create energy boundaries file
  199 + ebounds = gammalib.GEbounds(1, gammalib.GEnergy(1.0,'TeV'),
  200 + gammalib.GEnergy(10.0,'TeV'))
  201 + ebounds.save('test_ebinfile_ebounds.fits[EBOUNDS]', True)
  202 + ebounds.save('test_ebinfile_energybins.fits[ENERGYBINS]', True)
  203 + ebounds.save('test_ebinfile_energies.fits[ENERGIES]', True)
  204 +
  205 + # Return
  206 + return
  207 +
  208 + # Set test functions
  209 + def set(self):
  210 + """
  211 + Set all test functions
  212 + """
  213 + # Set test name
  214 + self.name('cscript')
  215 +
  216 + # Append tests
  217 + self.append(self._test_cscript, 'Test cscript base class')
  218 + self.append(self._test_ctobservation, 'Test ctobservation base class')
  219 + self.append(self._test_ctlikelihood, 'Test ctlikelihood base class')
  220 +
  221 + # Return
  222 + return
  223 +
  224 + # Test cscript base class
  225 + def _test_cscript(self):
  226 + """
  227 + Test cscript base class
  228 + """
  229 + # Allocate empty
  230 + empty = cscript_test()
  231 +
  232 + # Test is_valid_filename() method
  233 + self.test_assert(empty._check_is_valid_filename(self._model),
  234 + 'Check model definiton XML file')
  235 + self.test_assert(not empty._check_is_valid_filename(''),
  236 + 'Check empty filename')
  237 + self.test_assert(not empty._check_is_valid_filename('NONE'),
  238 + 'Check "NONE" filename')
  239 +
  240 + # Test setup_observations() for invalid file name
  241 + self.test_try('Check setup_observations() for "NONE"')
  242 + try:
  243 + empty._check_setup_observations('NONE')
  244 + self.test_try_failure('Exception not thrown')
  245 + except ValueError:
  246 + self.test_try_success()
  247 +
  248 + # Test setup_observations() for unacceptable event list
  249 + self.test_try('Check setup_observations() for unacceptable event list')
  250 + try:
  251 + empty._check_setup_observations(self._events, list=False)
  252 + self.test_try_failure('Exception not thrown')
  253 + except ValueError:
  254 + self.test_try_success()
  255 +
  256 + # Test setup_observations() for unacceptable counts cube
  257 + self.test_try('Check setup_observations() for unacceptable counts cube')
  258 + try:
  259 + empty._check_setup_observations(self._cntcube, cube=False)
  260 + self.test_try_failure('Exception not thrown')
  261 + except ValueError:
  262 + self.test_try_success()
  263 +
  264 + # Test setup_models() for invalid model definition file name
  265 + self.test_try('Check setup_models() for invalid model definition file name')
  266 + try:
  267 + empty._check_setup_models()
  268 + self.test_try_failure('Exception not thrown')
  269 + except ValueError:
  270 + self.test_try_success()
  271 +
  272 + # Test create_ebounds() method for "EBOUNDS" extension
  273 + ebounds = empty._check_create_ebounds('test_ebinfile_ebounds.fits')
  274 + self.test_value(ebounds.size(), 1, 'Check number of energy boundaries')
  275 + self.test_value(ebounds.emin().TeV(), 1.0, 'Check minimum energy')
  276 + self.test_value(ebounds.emax().TeV(), 10.0, 'Check maximum energy')
  277 +
  278 + # Test create_ebounds() method for "ENERGYBINS" extension
  279 + ebounds = empty._check_create_ebounds('test_ebinfile_energybins.fits')
  280 + self.test_value(ebounds.size(), 1, 'Check number of energy boundaries')
  281 + self.test_value(ebounds.emin().TeV(), 1.0, 'Check minimum energy')
  282 + self.test_value(ebounds.emax().TeV(), 10.0, 'Check maximum energy')
  283 +
  284 + # Test create_ebounds() method for invalid extension
  285 + self.test_try('Check create_ebounds() method for invalid extension')
  286 + try:
  287 + empty._check_create_ebounds('test_ebinfile_energies.fits')
  288 + self.test_try_failure('Exception not thrown')
  289 + except ValueError:
  290 + self.test_try_success()
  291 +
  292 + # Test require_inobs() method for invalid file name
  293 + self.test_try('Check require_inobs() method for invalid file name')
  294 + try:
  295 + empty._check_require_inobs('NONE')
  296 + self.test_try_failure('Exception not thrown')
  297 + except ValueError:
  298 + self.test_try_success()
  299 +
  300 + # Test get_roi() method
  301 + roi = empty._check_get_roi()
  302 + self.test_assert(not roi.is_valid(), 'Check get_roi() method')
  303 +
  304 + # Test restore_edisp() method for invalid vector size
  305 + #self.test_try('Check restore_edisp() method for invalid vector size')
  306 + #try:
  307 + # empty._check_restore_edisp()
  308 + # self.test_try_failure('Exception not thrown')
  309 + #except ValueError:
  310 + # self.test_try_success()
  311 + #
  312 + # NOTE: This code does not work because of a missing list to
  313 + # integer vector conversion.
  314 +
  315 + # Test set_obs_bounds() method
  316 + obs = empty._check_set_obs_bounds()
  317 + self.test_value(obs.size(), 1, 'Check number of observations')
  318 + self.test_value(obs[0].events().ebounds().size(), 1,
  319 + 'Check number of energy boundaries')
  320 + self.test_value(obs[0].events().ebounds().emin().TeV(), 1.0,
  321 + 'Check minimum energy')
  322 + self.test_value(obs[0].events().ebounds().emax().TeV(), 10.0,
  323 + 'Check maximum energy')
  324 + self.test_value(obs[0].events().roi().radius(), 1.0,
  325 + 'Check RoI radius')
  326 +
  327 + # Test get_mean_pointing() method for empty observation container
  328 + self.test_try('Check get_mean_pointing() method for empty observation '
  329 + 'container')
  330 + try:
  331 + empty._check_get_mean_pointing()
  332 + self.test_try_failure('Exception not thrown')
  333 + except ValueError:
  334 + self.test_try_success()
  335 +
  336 + # Return
  337 + return
  338 +
  339 + # Test ctobservation base class
  340 + def _test_ctobservation(self):
  341 + """
  342 + Test ctobservation base class
  343 + """
  344 + # Allocate empty
  345 + empty = ctobservation_test()
  346 +
  347 + # Test first_unbinned_observation() method
  348 + self.test_assert(empty._first_unbinned_observation() is None,
  349 + 'Check first_unbinned_observation() method')
  350 +
  351 + # Test next_unbinned_observation() method
  352 + self.test_assert(empty._next_unbinned_observation() is None,
  353 + 'Check next_unbinned_observation() method')
  354 +
  355 + # Return
  356 + return
  357 +
  358 + # Test ctlikelihood base class
  359 + def _test_ctlikelihood(self):
  360 + """
  361 + Test ctlikelihood base class
  362 + """
  363 + # Allocate empty
  364 + empty = ctlikelihood_test()
  365 +
  366 + # Test evaluate() method for minimum value violation
  367 + self.test_try('Check evaluate() for minimum value violation')
  368 + try:
  369 + empty._check_evaluate(0.0)
  370 + self.test_try_failure('Exception not thrown')
  371 + except ValueError:
  372 + self.test_try_success()
  373 +
  374 + # Test evaluate() method for maximum value violation
  375 + self.test_try('Check evaluate() for maximum value violation')
  376 + try:
  377 + empty._check_evaluate(4.0)
  378 + self.test_try_failure('Exception not thrown')
  379 + except ValueError:
  380 + self.test_try_success()
  381 +
  382 + # Return
  383 + return
... ...
test/test_ctskymap.py
... ... @@ -181,10 +181,10 @@ class Test(test):
181 181 skymap['nxpix'] = 200
182 182 skymap['nypix'] = 200
183 183 skymap['binsz'] = 0.02
184   - skymap['coordsys'] = 'CEL'
  184 + skymap['coordsys'] = 'GAL'
185 185 skymap['proj'] = 'CAR'
186   - skymap['xref'] = 83.63
187   - skymap['yref'] = 22.01
  186 + skymap['xref'] = 184.5575
  187 + skymap['yref'] = -5.7844
188 188 skymap['outmap'] = 'ctskymap_py3.fits'
189 189 skymap['logfile'] = 'ctskymap_py3.log'
190 190 skymap['chatter'] = 4
... ...
test/test_ctulimit.py
... ... @@ -111,6 +111,7 @@ class Test(test):
111 111 'Check upper limit on energy flux')
112 112  
113 113 # Check that saving does not nothing
  114 + ulimit['logfile'] = 'ctulimit_py0.log'
114 115 ulimit.logFileOpen()
115 116 ulimit.save()
116 117  
... ... @@ -157,13 +158,6 @@ class Test(test):
157 158 self.test_value(cpy_ulimit.eflux_ulimit(), 2.75669e-11, 1.0e-16,
158 159 'Check upper limit on energy flux')
159 160  
160   - # Execute copy of ctulimit tool again, now with a higher chatter
161   - # level than before
162   - cpy_ulimit['logfile'] = 'ctulimit_py2.log'
163   - cpy_ulimit['chatter'] = 3
164   - cpy_ulimit.logFileOpen() # Needed to get a new log file
165   - cpy_ulimit.execute()
166   -
167 161 # Check results
168 162 self.test_value(cpy_ulimit.diff_ulimit(), 8.86803e-18, 1.0e-21,
169 163 'Check differential upper limit')
... ... @@ -172,9 +166,6 @@ class Test(test):
172 166 self.test_value(cpy_ulimit.eflux_ulimit(), 2.75669e-11, 1.0e-16,
173 167 'Check upper limit on energy flux')
174 168  
175   - # Save copy of observation container for later
176   - obs = cpy_ulimit.obs().copy()
177   -
178 169 # Now clear copy of ctulimit tool
179 170 cpy_ulimit.clear()
180 171  
... ... @@ -186,13 +177,21 @@ class Test(test):
186 177 self.test_value(cpy_ulimit.eflux_ulimit(), 0.0, 1.0e-16,
187 178 'Check upper limit on energy flux')
188 179  
189   - # Now set ctulimit tool using the observation container from last
190   - # time. This should avoid the necessity to recompute the maximum
191   - # likelihood
192   - ulimit = ctools.ctulimit(obs)
  180 + # Run ctlike to get an initial log-likelihood solution
  181 + like = ctools.ctlike()
  182 + like['inobs'] = self._events
  183 + like['inmodel'] = self._model
  184 + like['caldb'] = self._caldb
  185 + like['irf'] = self._irf
  186 + like.run()
  187 +
  188 + # Now set ctulimit tool using the observation container from the
  189 + # previous run. This should avoid the necessity to recompute the
  190 + # maximum likelihood
  191 + ulimit = ctools.ctulimit(like.obs())
193 192 ulimit['srcname'] = 'Crab'
194   - ulimit['logfile'] = 'ctulimit_py3.log'
195   - ulimit['chatter'] = 4
  193 + ulimit['logfile'] = 'ctulimit_py2.log'
  194 + ulimit['chatter'] = 3
196 195  
197 196 # Execute ctulimit tool
198 197 ulimit.logFileOpen() # Needed to get a new log file
... ... @@ -207,11 +206,11 @@ class Test(test):
207 206 'Check upper limit on energy flux')
208 207  
209 208 # Test invalid model name
  209 + ulimit['srcname'] = 'Weihnachtsstern'
  210 + ulimit['logfile'] = 'ctulimit_py3.log'
  211 + ulimit.logFileOpen()
210 212 self.test_try('Test invalid model name')
211 213 try:
212   - ulimit['srcname'] = 'Weihnachtsstern'
213   - ulimit['logfile'] = 'ctulimit_py4.log'
214   - ulimit.logFileOpen()
215 214 ulimit.execute()
216 215 self.test_try_failure('Exception not thrown')
217 216 except ValueError:
... ... @@ -219,7 +218,7 @@ class Test(test):
219 218  
220 219 # Test specification of background model
221 220 ulimit['srcname'] = 'Background'
222   - ulimit['logfile'] = 'ctulimit_py5.log'
  221 + ulimit['logfile'] = 'ctulimit_py4.log'
223 222 ulimit.logFileOpen()
224 223 self.test_try('Test invalid model name')
225 224 try:
... ... @@ -228,6 +227,18 @@ class Test(test):
228 227 except ValueError:
229 228 self.test_try_success()
230 229  
  230 + # Test run with too few iterations
  231 + ulimit['srcname'] = 'Crab'
  232 + ulimit['max_iter'] = 1
  233 + ulimit['logfile'] = 'ctulimit_py5.log'
  234 + ulimit.logFileOpen()
  235 + self.test_try('Test ctulimit with too few iterations')
  236 + try:
  237 + ulimit.execute()
  238 + self.test_try_failure('Exception not thrown')
  239 + except ValueError:
  240 + self.test_try_success()
  241 +
231 242 # Return
232 243 return
233 244  
... ...
test/test_python_cscripts.py
... ... @@ -23,6 +23,7 @@ import sys
23 23 import gammalib
24 24 import cscripts
25 25 sys.path.append(os.path.dirname(os.path.abspath(__file__)))
  26 +import test_cscript
26 27 import test_cscaldb
27 28 import test_csfindobs
28 29 import test_csinfo
... ... @@ -105,65 +106,25 @@ def test(installed=False, debug=False):
105 106 os.system('cp -r %s/syspfiles/*.par pfiles/' % (os.environ['CTOOLS']))
106 107 os.system('chmod u+w pfiles/*')
107 108  
108   - # Allocate test suite container
109   - suites = gammalib.GTestSuites('cscripts unit testing')
110   -
111   - # Allocate test suites
112   - suite_cscaldb = test_cscaldb.Test()
113   - suite_csinfo = test_csinfo.Test()
114   - suite_cslightcrv = test_cslightcrv.Test()
115   - suite_csmodelinfo = test_csmodelinfo.Test()
116   - suite_csmodelmerge = test_csmodelmerge.Test()
117   - suite_csobs2caldb = test_csobs2caldb.Test()
118   - suite_csobsdef = test_csobsdef.Test()
119   - suite_csobsinfo = test_csobsinfo.Test()
120   - suite_cspull = test_cspull.Test()
121   - suite_csresmap = test_csresmap.Test()
122   - suite_csroot2caldb = test_csroot2caldb.Test()
123   - suite_cssens = test_cssens.Test()
124   - suite_csspec = test_csspec.Test()
125   - suite_cstsdist = test_cstsdist.Test()
126   - suite_cstsmapmerge = test_cstsmapmerge.Test()
127   - suite_cstsmapsplit = test_cstsmapsplit.Test()
128   - suite_csworkflow = test_csworkflow.Test()
129   -
130   - # Setup unit tests
131   - suite_cscaldb.set()
132   - suite_csinfo.set()
133   - suite_cslightcrv.set()
134   - suite_csmodelinfo.set()
135   - suite_csmodelmerge.set()
136   - suite_csobs2caldb.set()
137   - suite_csobsdef.set()
138   - suite_csobsinfo.set()
139   - suite_cspull.set()
140   - suite_csresmap.set()
141   - suite_csroot2caldb.set()
142   - suite_cssens.set()
143   - suite_csspec.set()
144   - suite_cstsdist.set()
145   - suite_cstsmapmerge.set()
146   - suite_cstsmapsplit.set()
147   - suite_csworkflow.set()
148   -
149   - # Append tests to container
150   - suites.append(suite_cscaldb)
151   - suites.append(suite_csinfo)
152   - suites.append(suite_cslightcrv)
153   - suites.append(suite_csmodelinfo)
154   - suites.append(suite_csmodelmerge)
155   - suites.append(suite_csobs2caldb)
156   - suites.append(suite_csobsdef)
157   - suites.append(suite_csobsinfo)
158   - suites.append(suite_cspull)
159   - suites.append(suite_csresmap)
160   - suites.append(suite_csroot2caldb)
161   - suites.append(suite_cssens)
162   - suites.append(suite_csspec)
163   - suites.append(suite_cstsdist)
164   - suites.append(suite_cstsmapmerge)
165   - suites.append(suite_cstsmapsplit)
166   - suites.append(suite_csworkflow)
  109 + # Define list of test suites
  110 + tests = [test_cscript.Test(),
  111 + test_cscaldb.Test(),
  112 + test_csinfo.Test(),
  113 + test_cslightcrv.Test(),
  114 + test_csmodelinfo.Test(),
  115 + test_csmodelmerge.Test(),
  116 + test_csobs2caldb.Test(),
  117 + test_csobsdef.Test(),
  118 + test_csobsinfo.Test(),
  119 + test_cspull.Test(),
  120 + test_csresmap.Test(),
  121 + test_csroot2caldb.Test(),
  122 + test_cssens.Test(),
  123 + test_csspec.Test(),
  124 + test_cstsdist.Test(),
  125 + test_cstsmapmerge.Test(),
  126 + test_cstsmapsplit.Test(),
  127 + test_csworkflow.Test()]
167 128  
168 129 # Append tests for Python 2.6+ (the IACT cscripts depend on the json
169 130 # module which is only available since Python 2.6+)
... ... @@ -178,23 +139,19 @@ def test(installed=False, debug=False):
178 139 # impact the environment variable in the parent shell.
179 140 del os.environ['VHEFITS']
180 141  
181   - # Allocate test suites
182   - suite_csfindobs = test_csfindobs.Test()
183   - suite_csiactcopy = test_csiactcopy.Test()
184   - suite_csiactdata = test_csiactdata.Test()
185   - suite_csiactobs = test_csiactobs.Test()
186   -
187   - # Setup unit tests
188   - suite_csfindobs.set()
189   - suite_csiactcopy.set()
190   - suite_csiactdata.set()
191   - suite_csiactobs.set()
192   -
193   - # Append tests to container
194   - suites.append(suite_csfindobs)
195   - suites.append(suite_csiactcopy)
196   - suites.append(suite_csiactdata)
197   - suites.append(suite_csiactobs)
  142 + # Append IACT script tests to list of test suites
  143 + tests.extend([test_csfindobs.Test(),
  144 + test_csiactcopy.Test(),
  145 + test_csiactdata.Test(),
  146 + test_csiactobs.Test()])
  147 +
  148 + # Allocate test suite container
  149 + suites = gammalib.GTestSuites('cscripts unit testing')
  150 +
  151 + # Set test suites and append them to suite container
  152 + for suite in tests:
  153 + suite.set()
  154 + suites.append(suite)
198 155  
199 156 # Run test suite
200 157 success = suites.run()
... ...
test/test_python_ctools.py
... ... @@ -99,65 +99,32 @@ def test(installed=False):
99 99 os.system('cp -r %s/syspfiles/*.par pfiles/' % (os.environ['CTOOLS']))
100 100 os.system('chmod u+w pfiles/*')
101 101  
  102 + # Define list of test suites
  103 + tests = [test_ctobssim.Test(),
  104 + test_ctselect.Test(),
  105 + test_ctbin.Test(),
  106 + test_ctlike.Test(),
  107 + test_cttsmap.Test(),
  108 + test_ctmodel.Test(),
  109 + test_ctskymap.Test(),
  110 + test_ctexpcube.Test(),
  111 + test_ctpsfcube.Test(),
  112 + test_ctedispcube.Test(),
  113 + test_ctbkgcube.Test(),
  114 + test_ctmapcube.Test(),
  115 + test_ctcubemask.Test(),
  116 + test_ctbutterfly.Test(),
  117 + test_ctulimit.Test(),
  118 + test_cterror.Test(),
  119 + test_pipelines.Test()]
  120 +
102 121 # Allocate test suite container
103 122 suites = gammalib.GTestSuites('ctools unit testing')
104 123  
105   - # Allocate test suites and append them to the container
106   - suite_ctobssim = test_ctobssim.Test()
107   - suite_ctselect = test_ctselect.Test()
108   - suite_ctbin = test_ctbin.Test()
109   - suite_ctlike = test_ctlike.Test()
110   - suite_cttsmap = test_cttsmap.Test()
111   - suite_ctmodel = test_ctmodel.Test()
112   - suite_ctskymap = test_ctskymap.Test()
113   - suite_ctexpcube = test_ctexpcube.Test()
114   - suite_ctpsfcube = test_ctpsfcube.Test()
115   - suite_ctedispcube = test_ctedispcube.Test()
116   - suite_ctbkgcube = test_ctbkgcube.Test()
117   - suite_ctmapcube = test_ctmapcube.Test()
118   - suite_ctcubemask = test_ctcubemask.Test()
119   - suite_ctbutterfly = test_ctbutterfly.Test()
120   - suite_ctulimit = test_ctulimit.Test()
121   - suite_cterror = test_cterror.Test()
122   - suite_pipelines = test_pipelines.Test()
123   -
124   - # Setup unit tests
125   - suite_ctobssim.set()
126   - suite_ctselect.set()
127   - suite_ctbin.set()
128   - suite_ctlike.set()
129   - suite_cttsmap.set()
130   - suite_ctmodel.set()
131   - suite_ctskymap.set()
132   - suite_ctexpcube.set()
133   - suite_ctpsfcube.set()
134   - suite_ctedispcube.set()
135   - suite_ctbkgcube.set()
136   - suite_ctmapcube.set()
137   - suite_ctcubemask.set()
138   - suite_ctbutterfly.set()
139   - suite_ctulimit.set()
140   - suite_cterror.set()
141   - suite_pipelines.set()
142   -
143   - # Append tests to container
144   - suites.append(suite_ctobssim)
145   - suites.append(suite_ctselect)
146   - suites.append(suite_ctbin)
147   - suites.append(suite_ctlike)
148   - suites.append(suite_cttsmap)
149   - suites.append(suite_ctmodel)
150   - suites.append(suite_ctskymap)
151   - suites.append(suite_ctexpcube)
152   - suites.append(suite_ctpsfcube)
153   - suites.append(suite_ctedispcube)
154   - suites.append(suite_ctbkgcube)
155   - suites.append(suite_ctmapcube)
156   - suites.append(suite_ctcubemask)
157   - suites.append(suite_ctbutterfly)
158   - suites.append(suite_ctulimit)
159   - suites.append(suite_cterror)
160   - suites.append(suite_pipelines)
  124 + # Set test suites and append them to suite container
  125 + for suite in tests:
  126 + suite.set()
  127 + suites.append(suite)
161 128  
162 129 # Run test suite
163 130 success = suites.run()
... ...