unit_test_base.h
Go to the documentation of this file.
1 /**
2  * @file unit_test_base.h
3  * @brief Base class for unit tests using FHiCL configuration
4  * @date December 1st, 2015
5  * @author petrillo@fnal.gov
6  *
7  * Provides an environment for easy set up of a message-facility-aware test.
8  *
9  * For an example of how to expand it to host services,
10  * see larcore/test/Geometry/geometry_unit_test_base.h
11  *
12  * Currently provides:
13  * - BasicEnvironmentConfiguration: a test environment configuration
14  * - TestSharedGlobalResource: mostly internal use
15  * - TesterEnvironment: a prepacked test environment with some provider support
16  *
17  * This is a pure template header. It will require the following libraries:
18  *
19  * * `MF_MessageLogger`
20  * * `MF_Utilities`
21  * * `fhiclcpp`
22  * * `cetlib`
23  *
24  */
25 
26 
27 #ifndef TEST_UNIT_TEST_BASE_H
28 #define TEST_UNIT_TEST_BASE_H
29 
30 // GArSoft libraries
31 #include "TestUtils/ProviderTestHelpers.h"
32 #include "TestUtils/ProviderList.h"
33 #include "CoreUtils/ProviderPack.h"
34 
35 // utility libraries
36 #include "fhiclcpp/ParameterSet.h"
39 #include "fhiclcpp/parse.h"
40 // #include "fhiclcpp/exception.h"
42 
43 // CET libraries
44 #include "cetlib/filesystem.h" // cet::is_absolute_filepath()
45 #include "cetlib/filepath_maker.h"
46 #include "cetlib/search_path.h"
47 
48 // C/C++ standard libraries
49 #include <iostream> // for output before message facility is set up
50 #include <string>
51 #include <memory> // std::unique_ptr<>
52 #include <utility> // std::move(), std::forward()
53 #include <map>
54 #include <type_traits> // std::add_rvalue_reference()
55 #include <stdexcept> // std::logic_error
56 
57 
58 namespace testing {
59 
60  namespace details {
61 
62  /// Reads and makes available the command line parameters
64  public:
65  /// Constructor: automatically parses from Boost arguments
67 
68  /// Constructor: parses from specified arguments
69  CommandLineArguments(int argc, char** argv)
70  { ParseArguments(argc, argv); }
71 
72  /// Parses arguments
73  void ParseArguments(int argc, char** argv);
74 
75  /// Returns the name of the executable as started
76  std::string Executable() const { return exec_name; }
77 
78  /// Returns the list of non-Boost-test arguments on the command line
79  std::vector<std::string> const& Arguments() const { return args; }
80 
81  /// Returns whether we have arguments up to the iArg-th (0-based)
82  bool hasArgument(size_t iArg) const { return iArg < args.size(); }
83 
84  /// Returns the value of the iArg-th (0-based; no range check!)
85  std::string const& Argument(size_t iArg) const { return args[iArg]; }
86 
87  private:
88  std::string exec_name; ///< name of the test executable (from argv[0])
89  std::vector<std::string> args; ///< command line arguments (from argv[0])
90 
91  /// Erases the stored arguments
92  void Clear() { exec_name.clear(); args.clear(); }
93 
94  }; // class CommandLineArguments
95 
96 
97  void CommandLineArguments::ParseArguments(int argc, char** argv) {
98  Clear();
99  if (argc == 0) return;
100 
101  exec_name = argv[0];
102 
103  args.resize(argc - 1);
104  std::copy(argv + 1, argv + argc, args.begin());
105 
106  } // CommandLineArguments:ParseArguments()
107 
108 
109  // forward declaration
110  template
111  <typename TestEnv, typename Pack, typename... Provs>
113 
114  } // namespace details
115 
116 
117  /** **************************************************************************
118  * @brief Class holding a configuration for a test environment
119  *
120  * This class needs to be fully constructed by the default constructor
121  * in order to be useful as Boost unit test fixture.
122  * It is supposed to be passed as a template parameter to another class
123  * that can store an instance of it and extract configuration information
124  * from it.
125  */
127 
128  /// Default constructor; this is what is used in Boost unit test
129  BasicEnvironmentConfiguration() { DefaultInit(); }
130 
131  /// Constructor: acquires parameters from the command line
134  { ParseCommandLine(argc, argv); }
135 
136  /// Constructor; accepts the name as parameter
139  { SetApplicationName(name); }
140 
143  { SetApplicationName(name); }
144 
145  /// @{
146  /// @name Access to configuration
147  /// Name of the application
148  std::string ApplicationName() const { return appl_name; }
149 
150  /// Path to the configuration file
151  std::string ConfigurationPath() const { return config_path; }
152 
153  /// FHiCL path for the configuration of the test algorithm
155  {
156  auto iPath = test_paths.find(name);
157  return (iPath == test_paths.end())
158  ? ("physics.analyzers." + name): iPath->second;
159  }
160 
161  /// Name of the test algorithm instance
162  std::string MainTesterParameterSetName() const { return main_test_name; }
163 
164  /// FHiCL path for the configuration of the test algorithm
166  {
167  return MainTesterParameterSetName().empty()
168  ? "": TesterParameterSetPath(MainTesterParameterSetName());
169  }
170 
171  /// FHiCL path for the configuration of the service
173  {
174  auto iPath = service_paths.find(name);
175  return (iPath == service_paths.end())
176  ? ("services." + name): iPath->second;
177  } // ServiceParameterSetPath()
178 
179  /// A string describing default parameter set to configure specified test
181  { return analyzers_default_cfg.at(tester_name); }
182 
183  /// A string describing the default parameter set to configure the test
185  { return services_default_cfg.at(service_name); }
186 
187  /// A string describing the full default parameter set
189  { return BuildDefaultConfiguration(); }
190 
191  /// Returns the name of the executable as started
192  std::string ExecutablePath() const { return arguments.Executable(); }
193 
194  /// Returns the list of non-Boost-test arguments on the command line
195  std::vector<std::string> const& EexcutableArguments() const
196  { return arguments.Arguments(); }
197 
198  ///@}
199 
200 
201  /// @{
202  /// @name Set configuration
203 
204  /// Sets the name of the application
205  void SetApplicationName(std::string name) { appl_name = name; }
206 
207  /// Sets the path to the configuration file
208  void SetConfigurationPath(std::string path) { config_path = path; }
209 
210  /// Sets the FHiCL name for the configuration of the test algorithm
212  { main_test_name = name; }
213 
214  /// Sets the FHiCL path for the configuration of a test algorithm
216  { test_paths[test_name] = path; }
217 
218  /// Sets the FHiCL path for the configuration of the main test algorithm
220  {
221  if (MainTesterParameterSetName().empty()) {
222  throw std::logic_error
223  ("Request setting configuration of non-existent main tester");
224  }
225  SetTesterParameterSetPath(MainTesterParameterSetName(), path);
226  }
227 
228  /// Sets the FHiCL path for the configuration of a test algorithm
230  { service_paths[service_name] = path; }
231 
232  /// Adds a default configuration for the specified service
233  void AddDefaultServiceConfiguration
234  (std::string service_name, std::string service_cfg)
235  { services_default_cfg[service_name] = service_cfg; }
236 
237  /// Adds a default configuration for the specified tester
238  void AddDefaultTesterConfiguration
239  (std::string tester_name, std::string tester_cfg)
240  { analyzers_default_cfg[tester_name] = tester_cfg; }
241 
242  /// Adds a default configuration for the main tester
244  {
245  if (MainTesterParameterSetName().empty()) {
246  throw std::logic_error
247  ("Request adding configuration of non-existent main tester");
248  }
249  AddDefaultTesterConfiguration(MainTesterParameterSetName(), tester_cfg);
250  }
251 
252  ///@}
253 
254 
255 
256  protected:
257  using ConfigurationMap_t = std::map<std::string, std::string>;
258  using PathMap_t = std::map<std::string, std::string>;
259 
260  std::string appl_name; ///< name of the application
261  std::string config_path; ///< configuration file path
262  std::string main_test_name; ///< name of main test algorithm
263  std::string main_test_path; ///< path of main test algorithm configuration
264 
265  /// Returns the default test name
266  static std::string DefaultApplicationName() { return "Test"; }
267 
268  /// Configuration of all the services
270  /// Configuration of all the analyzer modules
272 
273  /// Set of paths for tester configuration
275  /// Set of paths for service configuration
277 
278  /// Extracts arguments from the command line, uses first one as config path
279  void ParseCommandLine(int argc, char** argv)
280  {
281  arguments.ParseArguments(argc, argv);
282  if (arguments.hasArgument(0))
283  SetConfigurationPath(arguments.Argument(0)); // first argument
284  }
285 
286  /// Initialize with some default values
287  void DefaultInit()
288  {
289  SetApplicationName(DefaultApplicationName());
290  SetMainTesterParameterSetName("");
291  // a destination which will react to all messages from DEBUG up:
292  AddDefaultServiceConfiguration("message",
293  R"(
294  debugModules: [ '*' ]
295  destinations : {
296  stdout: {
297  type: cout
298  threshold: DEBUG
299  categories: {
300  default: {
301  limit: -1
302  }
303  } // categories
304  } // stdout
305  } // destinations
306  statistics: cout
307  )");
308  } // DefaultInit()
309 
310  /// A string describing the full default parameter set
312  { return BuildServiceConfiguration(services_default_cfg); }
313 
314  /// A string describing the full default parameter set
316  { return BuildTestConfiguration(analyzers_default_cfg); }
317 
318  /// A string describing the full default parameter set
320  {
321  return BuildConfiguration(services_default_cfg, analyzers_default_cfg);
322  }
323 
324 
325  /// A string with the service section from service parameter sets
326  static std::string BuildServiceConfiguration
327  (ConfigurationMap_t const& services)
328  {
329  std::string cfg;
330  cfg += "\nservices: {";
331  for (auto const& service_info: services) {
332  cfg += "\n " + service_info.first + ": {";
333  cfg += "\n" + service_info.second;
334  cfg += "\n } # " + service_info.first;
335  } // for services
336  cfg +=
337  "\n} # services"
338  "\n";
339  return cfg;
340  } // BuildServiceConfiguration()
341 
342  /// A string with the physics section from analyzer parameter sets
343  static std::string BuildTestConfiguration
344  (ConfigurationMap_t const& analyzers)
345  {
346  std::string cfg;
347  cfg +=
348  "\nphysics: {"
349  "\n analyzers: {"
350  ;
351  for (auto const& module_info: analyzers) {
352  cfg += "\n " + module_info.first + ": {";
353  cfg += "\n" + module_info.second;
354  cfg += "\n } # " + module_info.first;
355  } // for analyzers
356  cfg +=
357  "\n } # analyzers"
358  "\n} # physics";
359  return cfg;
360  } // BuildServiceConfiguration()
361 
362  /// A string describing the full default parameter set
363  static std::string BuildConfiguration
364  (ConfigurationMap_t const& services, ConfigurationMap_t const& modules)
365  {
366  std::string cfg;
367  cfg += BuildServiceConfiguration(services);
368  cfg += BuildTestConfiguration(modules);
369  return cfg;
370  } // BuildConfiguration()
371 
372  private:
373  details::CommandLineArguments arguments; ///< command line arguments
374 
375  }; // class BasicEnvironmentConfiguration<>
376 
377 
378 
379  /** **************************************************************************
380  * @brief Utility class providing singleton objects to the derived classes
381  * @tparam RES the type of object (include constantness if needed)
382  *
383  * The object is expected to be shared.
384  */
385  template <typename RES>
387  using Resource_t = RES;
388 
389  public:
390  using ResourcePtr_t = std::shared_ptr<Resource_t>;
391 
392  /// @name Add and share resources
393  /// @{
394 
395  /// Adds a shared resource to the resource registry
396  static void AddSharedResource(std::string res_name, ResourcePtr_t res_ptr)
397  { Resources[res_name] = res_ptr; }
398 
399  /// Adds a shared resource to the resource registry (empty name)
401  { AddSharedResource(std::string(), res_ptr); }
402 
403  /// Registers a shared resource only if none exists yet
404  template <typename... Args>
405  static ResourcePtr_t ProvideSharedResource
406  (std::string res_name, ResourcePtr_t res_ptr)
407  {
408  if (hasResource(res_name)) return ResourcePtr_t();
409  AddSharedResource(res_name, res_ptr);
410  return res_ptr;
411  }
412 
413  /// Creates a shared resource as default only if none exists yet
414  template <typename... Args>
416  { return ProvideSharedResource(std::string(), res_ptr); }
417 
418  //@{
419  /// Adds a shared resource only if it is old_res_ptr
421  std::string res_name,
422  Resource_t const* old_res_ptr, ResourcePtr_t res_ptr
423  )
424  {
425  ResourcePtr_t current_res_ptr = ShareResource();
426  if (current_res_ptr.get() != old_res_ptr) return false;
427  AddSharedResource(res_name, res_ptr);
428  return true;
429  }
430  static bool ReplaceSharedResource
431  (std::string res_name, ResourcePtr_t old_res_ptr, ResourcePtr_t res_ptr)
432  { return ReplaceSharedResource(res_name, old_res_ptr.get(), res_ptr); }
433  //@}
434 
435  //@{
436  /// Adds a shared resource as default resource only if it is old_res_ptr
437  static bool ReplaceDefaultSharedResource
438  (Resource_t const* old_res_ptr, ResourcePtr_t res_ptr)
439  { return ReplaceSharedResource(std::string(), old_res_ptr, res_ptr); }
440  static bool ReplaceDefaultSharedResource
441  (ResourcePtr_t old_res_ptr, ResourcePtr_t res_ptr)
442  { return ReplaceSharedResource(std::string(), old_res_ptr, res_ptr); }
443  //@}
444 
445  /// Constructs and registers a new resource with a specified name
446  template <typename... Args>
447  static ResourcePtr_t CreateResource(std::string res_name, Args&&... args)
448  {
449  ResourcePtr_t res_ptr(new Resource_t(std::forward<Args>(args)...));
450  AddSharedResource(res_name, res_ptr);
451  return res_ptr;
452  }
453 
454  /// Constructs and registers a new resource with no name
455  template <typename... Args>
456  static void CreateDefaultResource(Args&&... args)
457  { CreateResource(std::string(), std::forward<Args>(args)...); }
458 
459 
460  /// Creates a shared resource only if none exists yet
461  template <typename... Args>
462  static ResourcePtr_t ProposeSharedResource
463  (std::string res_name, Args&&... args)
464  {
465  return hasResource(res_name)?
466  ResourcePtr_t():
467  CreateResource(res_name, std::forward<Args>(args)...);
468  }
469 
470  /// Creates a shared resource as default only if none exists yet
471  template <typename... Args>
473  {
474  return ProposeSharedResource
475  (std::string(), std::forward<Args>(args)...);
476  }
477 
478  /// @}
479 
480  /// @name Resource access
481  /// @{
482 
483  /// Returns whether a resource exists
484  /// @throws std::out_of_range if not available
485  static bool hasResource(std::string name = "")
486  {
487  auto iRes = Resources.find(name);
488  return (iRes != Resources.end()) && bool(iRes->second);
489  }
490 
491  /// Retrieves the specified resource for sharing (nullptr if none)
493  {
494  auto iRes = Resources.find(name);
495  return (iRes == Resources.end())? ResourcePtr_t(): iRes->second;
496  }
497 
498  /// Retrieves the specified resource, or throws if not available
500  { return *(Resources.at(name).get()); }
501 
502  /// @}
503 
504  /// Destroys the specified resource (does nothing if no such resource)
506  { Resources.erase(name); }
507 
508  private:
509  static std::map<std::string, ResourcePtr_t> Resources;
510 
511  }; // class TestSharedGlobalResource<>
512 
513 
514  template <typename RES>
515  std::map<std::string, typename TestSharedGlobalResource<RES>::ResourcePtr_t>
517 
518 
519  /** **************************************************************************
520  * @brief Environment for a test
521  * @tparam ConfigurationClass a class providing compile-time configuration
522  *
523  * The test environment is set up on construction.
524  *
525  * The environment provides:
526  * - Parameters() method returning the complete FHiCL configuration
527  * - TesterParameters() method returning the configuration for the test
528  *
529  * This class or a derived one can be used as global fixture for unit tests.
530  *
531  * Unfortunately Boost does not give any control on the initialization of the
532  * object, so everything must be ready to go as hard coded.
533  * The ConfigurationClass class tries to alleviate that.
534  * That is another, small static class that BasicTesterEnvironment uses to
535  * get its parameters.
536  *
537  * The requirements for the ConfigurationClass are:
538  * - `std::string ApplicationName()`: the application name
539  * - `std::string ConfigurationPath()`: path to the configuration file
540  * - `std::string MainTesterParameterSetName()`: name of the
541  * configuration of the main test (commodity)
542  * - `std::string DefaultTesterConfiguration()` returning a FHiCL string
543  * to be parsed to extract the default test configuration
544  *
545  * Whether the configuration comes from a file or from the two provided
546  * defaults, it is always expected within the parameter set paths:
547  * the default configuration must also contain that path.
548  *
549  * Note that there is no room for polymorphism here since the setup happens
550  * on construction.
551  * Some methods are declared virtual in order to allow to tweak some steps
552  * of the set up, but it's not trivial to create a derived class that works
553  * correctly: the derived class must declare a new default constructor,
554  * and that default constructor must call the protected constructor
555  * (BasicTesterEnvironment<ConfigurationClass>(no_setup))
556  */
557  template <typename ConfigurationClass>
559 
560  public:
561  using Configuration_t = ConfigurationClass;
562 
563  /**
564  * @brief Constructor: sets everything up and declares the test started
565  *
566  * The configuration is from a default-constructed ConfigurationClass.
567  * This is suitable for use as Boost unit test fixture.
568  */
569  BasicTesterEnvironment(bool bSetup = true) { if (bSetup) Setup(); }
570 
571  //@{
572  /**
573  * @brief Setup from a configuration
574  * @param configurer an instance of ConfigurationClass
575  *
576  * The configuration is from the specified configurer class.
577  *
578  * This constructor allows to use a non-default-constructed configuration.
579  * This can't be used (at best of my knowledge) when using this class as
580  * Boost unit test fixture.
581  *
582  * In the r-value-reference constructor, the configurer is moved.
583  */
584  BasicTesterEnvironment(Configuration_t const& cfg_obj, bool bSetup = true):
585  config(cfg_obj)
586  { if (bSetup) Setup(); }
587  BasicTesterEnvironment(Configuration_t&& cfg_obj, bool bSetup = true):
588  config(cfg_obj)
589  { if (bSetup) Setup(); }
590  //@}
591 
592  /// Destructor: closing remarks
593  virtual ~BasicTesterEnvironment();
594 
595 
596  /// @{
597  /// @name Configuration retrieval
598 
599  /// Returns the full configuration
600  fhicl::ParameterSet const& Parameters() const { return params; }
601 
602  /// Returns the configuration of the specified service
604  {
605  return params.get<fhicl::ParameterSet>
606  (config.ServiceParameterSetPath(service_name));
607  }
608 
609  /// Returns the configuration of the specified test
611  {
612  return params.get<fhicl::ParameterSet>
613  (config.TesterParameterSetPath(test_name));
614  }
615 
616  /// Returns the configuration of the main test (undefined if no main test)
618  {
619  if (config.MainTesterParameterSetName().empty()) return {};
620  else return TesterParameters(config.MainTesterParameterSetName());
621  }
622 
623  /// @}
624 
625  static fhicl::ParameterSet CompileParameterSet(std::string cfg);
626 
627  protected:
628 
629  /// Returns a read-only version of the configuration
630  Configuration_t const& Config() const { return config; }
631 
632  /// The complete initialization, ran at construction by default
633  virtual void Setup();
634 
635  /// Reads and translates the configuration
636  virtual void Configure();
637 
638  /**
639  * @brief Creates a full configuration for the test
640  * @return a parameters set with the complete configuration
641  */
643  { return CompileParameterSet(config.DefaultConfiguration()); }
644 
645  //@{
646  /// Sets up the message facility
647  virtual void SetupMessageFacility
648  (fhicl::ParameterSet const& pset, std::string appl_name = "") const;
649  virtual void SetupMessageFacility() const
650  { SetupMessageFacility(Parameters(), config.ApplicationName()); }
651  //@}
652 
653  /**
654  * @brief Fills the test configuration from file or from default
655  *
656  * If a FHiCL configuration file is specified, the configuration of the test
657  * is read from it according to the parameter set path of the test.
658  * Otherwise, it is parsed from the default one provided by the configurer.
659  */
660  /// Parses from file and returns a FHiCL data structure
661  static fhicl::ParameterSet ParseParameters(std::string config_path);
662 
663  private:
664  Configuration_t config; ///< instance of the configurer
665 
666 
667  void FillArgumentsFromCommandLine();
668 
669  fhicl::ParameterSet params; ///< full configuration of the test
670 
671  }; // class BasicTesterEnvironment<>
672 
673 
674 
675  //****************************************************************************
676  /**
677  * @brief A test environment with some support for service providers
678  * @tparam ConfigurationClass a class providing compile-time configuration
679  *
680  * This test environment extends BasicTesterEnvironment with some basic
681  * support for service providers.
682  *
683  *
684  * Service provider support
685  * =========================
686  *
687  * This environment makes it available the method `Provider<Prov>()`, which
688  * returns a pointer to the provider of type Prov.
689  *
690  * All providers must be set up _after_ the test environment is constructed.
691  * The environment provides the following facilities:
692  *
693  * * SetupProvider() to set up a service provider with generic arguments
694  * * SetupProviderFromService() to set up a service provider with a parameter
695  * set extracted from the configuration
696  * * AcquireProvider() to register a service provider already available
697  * * DropProvider() to destroy an existing provider
698  *
699  * The set up methods support a `For` variant (e.g. `SetupProviderFor()`) to
700  * register the provider also under the type of its interface. For example,
701  * if `LArPropertiesStandard` is an implementation of `LArProperties`,
702  * the call:
703  *
704  * env.SetupProviderFor<LArProperties, LArPropertiesStandard>(pset);
705  *
706  * will set up a `LArPropertiesStandard` provider just like
707  *
708  * env.SetupProvider<LArPropertiesStandard>(pset);
709  *
710  * would, and it makes the provider available as `LArProperties` too, so that
711  * both calls:
712  *
713  * env.Provider<LArProperties>();
714  * env.Provider<LArPropertiesStandard>();
715  *
716  * are valid and return the same provider.
717  *
718  *
719  * Use as test fixture
720  * ====================
721  *
722  * The providers must be set up _after_ the test environment is constructed.
723  * This also means an additional complication for fixtures that require to
724  * be constructed in a final state, as it is the case for Boost unit test
725  * suite fixtures.
726  * In these cases, a class should publicly derive from TesterEnvironment, and
727  * the necessary setup should be added into the constructor of this derived
728  * class.
729  *
730  * Note that, as in the case of BasicTesterEnvironment, in this case there is
731  * no room for polymorphism here since the setup need to happen on
732  * construction.
733  */
734  template <typename ConfigurationClass>
736  : public BasicTesterEnvironment<ConfigurationClass>
737  {
740 
741  public:
742  // inherit constructors
743  using TesterEnvBase_t::TesterEnvBase_t;
744 
745  /**
746  * @brief Sets a service provider up by calling its testing::setupProvider()
747  * @tparam Prov type of provider
748  * @tparam Args type of arguments for the setup function
749  * @param args arguments for the setup function
750  * @return a pointer to the provider set up
751  * @throw runtime_error if the provider already exists
752  * @see SetupProviderFor(), AcquireProvider()
753  *
754  * A provider of type Prov is created, set up and recorded.
755  * Provider setup is delegated to `testing::setupProvider` function specific
756  * to the provider itself (that is, `testing::setupProvider<Prov>(args...)`)
757  * to which the setup arguments are forwarded.
758  * If the provider already exists, an exception is thrown.
759  */
760  template <typename Prov, typename... Args>
761  Prov* SetupProvider(Args... args)
762  {
763  if (!providers.setup<Prov>(std::forward<Args>(args)...))
764  throw std::runtime_error("Provider already exists!");
765  return providers.getPointer<Prov>();
766  }
767 
768  /**
769  * @brief Sets a service provider up by calling its testing::setupProvider()
770  * @tparam Prov type of provider
771  * @tparam Args type of arguments for the setup function
772  * @param args arguments for the setup function
773  * @return a pointer to the provider set up
774  * @see SetupProvider()
775  * @throw runtime_error if the provider already exists
776  *
777  * A provider of type Prov is created, set up and recorded.
778  * Provider setup is attempted by constructing the provider with a parameter
779  * set from the registered configuration of service with specified `name`.
780  */
781  template <typename Prov>
783  { return SetupProvider<Prov>(this->ServiceParameters(name)); }
784 
785  /**
786  * @brief Acquires a service provider
787  * @tparam Prov type of provider
788  * @param prov the provider to be acquired
789  * @return a pointer to the provider
790  * @see SetupProvider()
791  * @throw runtime_error if the provider already exists
792  *
793  * This method registers and takes ownership of the specified provider.
794  * It is similar to SetupProvider() except that user is in charge of the
795  * preliminary creation and setup of the provider.
796  */
797  template <typename Prov>
798  Prov* AcquireProvider(std::unique_ptr<Prov>&& prov)
799  {
800  if (!providers.acquire(std::move(prov)))
801  throw std::runtime_error("Provider already exists!");
802  return providers.getPointer<Prov>();
803  }
804 
805  /**
806  * @brief Sets a provider up, recording it as implementation of Interface
807  * @tparam Interface type of provider interface being implemented
808  * @tparam Prov type of provider
809  * @tparam Args type of arguments for the setup function
810  * @param args arguments for the setup function
811  * @return a pointer to the provider set up
812  * @see SetupProvider()
813  *
814  * This method performs the same type of setup as SetupProvider().
815  * In addition, it registers the provider as an implementation of Interface.
816  * This means that the provider can be obtained not only with
817  * `provider<Prov>()`, which returns a pointer to the actual class Prov,
818  * but also as `provider<Interface>()`, which returns a pointer to the base
819  * class Interface.
820  */
821  template <typename Interface, typename Prov, typename... Args>
822  Prov* SetupProviderFor(Args... args)
823  {
824  auto prov = SetupProvider<Prov>(std::forward<Args>(args)...);
825  providers.set_alias<Prov, Interface>();
826  return prov;
827  }
828 
829  /**
830  * @brief Sets a provider up, recording it as implementation of Interface
831  * @tparam Interface type of provider interface being implemented
832  * @tparam Prov type of provider
833  * @tparam Args type of arguments for the setup function
834  * @param args arguments for the setup function
835  * @return a pointer to the provider set up
836  * @see SetupProviderFromService(), SetupProviderFor()
837  * @throw runtime_error if the provider already exists
838  *
839  * This method performs the same type of setup as
840  * SetupProviderFromService().
841  * In addition, it registers the provider as an implementation of Interface.
842  * This means that the provider can be obtained not only with
843  * `provider<Prov>()`, which returns a pointer to the actual class Prov,
844  * but also as `provider<Interface>()`, which returns a pointer to the base
845  * class Interface.
846  */
847  template <typename Interface, typename Prov>
849  {
850  auto prov = SetupProviderFromService<Prov>(name);
851  providers.set_alias<Prov, Interface>();
852  return prov;
853  }
854 
855  /**
856  * @brief Acquires a service provider implementing an interface
857  * @tparam Prov type of provider
858  * @tparam Interface type provider alias
859  * @param prov the provider to be acquired
860  * @return a pointer to the provider
861  * @see SetupProviderFor(), AcquireProvider()
862  *
863  * This method registers and takes ownership of the specified provider,
864  * like AcquireProvider() does. It also registers the provider as an
865  * implementation of Interface class, as SetupProviderFor does.
866  * It is similar to SetupProvider() except that user is in charge of the
867  * preliminar creation and setup of the provider.
868  */
869  template <typename Interface, typename Prov>
870  Prov* AcquireProviderFor(std::unique_ptr<Prov>&& prov)
871  {
872  auto prov_ptr = providers.acquire(prov);
873  providers.set_alias<Prov, Interface>();
874  return prov_ptr;
875  }
876 
877  /**
878  * @brief Oversimplified provider setup
879  * @return a pointer to the provider
880  * @tparam Prov provider type
881  *
882  * This is a one-step setup of the specified provider.
883  *
884  * It is available only if Prov provider comes with an implementation of
885  * testing::SimpleEnvironmentSetupClass that explains how to set up an
886  * environment.
887  *
888  */
889  template <typename Prov>
890  Prov* SimpleProviderSetup() { return simpleEnvironmentSetup<Prov>(*this); }
891 
892  /**
893  * @brief Removes and destroys the specified provider
894  * @tparam Prov type of provider to be destroyed
895  * @throw runtime_error if the provider was not present
896  */
897  template <typename Prov>
899  {
900  if (!providers.erase<Prov>())
901  throw std::runtime_error("Provider not present!");
902  }
903 
904  /// Return the specified provider (throws if not available)
905  template <typename Prov>
906  Prov const* Provider() const
907  { return providers.getPointer<Prov>(); }
908 
909  /**
910  * @brief Fills the specified provider pack with providers
911  * @throw runtime_error and everything provider() method can throw
912  * @see Provider()
913  */
914  template <typename... Provs>
916  {
918  <TesterEnv_t, gar::ProviderPack<Provs...>, Provs...>
919  ::fill
920  (*this, pack);
921  } // FillProviderPack()
922 
923 
924  /**
925  * @brief Returns a provider pack for the specified provider
926  * @tparam Prov type of the provider
927  * @throw runtime_error and everything provider() method can throw
928  * @see FillProviderPack()
929  *
930  * The provider is required to have a `providers_type` type defined as an
931  * specialisation of gar::ProviderPack.
932  */
933  template <typename Prov>
934  typename Prov::providers_type ProviderPackFor() const
935  {
936  typename Prov::providers_type pack;
937  FillProviderPack(pack);
938  return pack;
939  } // ProviderPackFor()
940 
941 
942  protected:
943  ProviderList providers; ///< list of available providers
944  }; // class TesterEnvironment<>
945 
946 
947 
948  /**
949  * @brief Constructs and returns a TesterEnvironment object
950  * @tparam CONFIG type of configuration object (detected from arguments)
951  * @tparam TESTENV type of the tester environment (default: TesterEnvironment)
952  * @tparam ARGS pack of types of the remining constructor arguments (optional)
953  * @param config the configuration object to be used
954  * @param other_args the remaining arguments of the tester constructor
955  *
956  * This function creates and returns a tester environment.
957  * By default, the tester environment class is TesterEnvironment<CONFIG>
958  * and no additional constructor arguments are needed except for special
959  * needs. The simplest way to use the function with an already available
960  * configuration is:
961  *
962  * auto TestEnv = testing::CreateTesterEnvironment(config);
963  *
964  * where TestEnv is assigned a specialization of TesterEnvironment.
965  *
966  * The template class TESTENV undergoes the following requirement:
967  *
968  * - it must have a constructor using a CONFIG constant reference as first
969  * argument
970  *
971  * The CONFIG object is subject to no special requirements besides the ones
972  * from TESTENV constructor.
973  */
974  // Note: this function is expected to be used with automatic type detection;
975  // the rules on "universal references" dictate that if config is a (l-value)
976  // reference, CONFIG itself is a l-value reference. We don't want to create
977  // a TesterEnvironment<Config&>, so we explicitly remove the reference from
978  // CONFIG (decay does that and aso removes the constantness, that we also
979  // don't want to be embedded in CONFIG).
980  template <
981  typename CONFIG,
982  typename TESTENV = TesterEnvironment<std::decay_t<CONFIG>>,
983  typename... ARGS
984  >
985  TESTENV CreateTesterEnvironment(CONFIG&& config, ARGS... other_args)
986  {
987  return TESTENV
988  (std::forward<CONFIG>(config), std::forward<ARGS>(other_args)...);
989  }
990 
991 
992 
993  //****************************************************************************
994  namespace details {
995  // Class to implement FHiCL file search.
996  // This is badly ripped off from ART, but we need to stay out of it
997  // so I have to replicate that functionality.
998  // I used the same class name.
1000  public:
1002  first(true), after_paths(paths)
1003  {}
1004 
1005  virtual std::string operator() (std::string const& filename);
1006 
1007  void reset() { first = true; }
1008 
1009  private:
1010  bool first; ///< whether we are waiting for the first query
1011  cet::search_path after_paths; ///< path for the other queries
1012 
1013  }; // class FirstAbsoluteOrLookupWithDotPolicy
1014 
1015 
1016  std::string FirstAbsoluteOrLookupWithDotPolicy::operator()
1018  {
1019  if (first) {
1020  first = false;
1022  return cet::search_path("./:" + after_paths.to_string())
1023  .find_file(filename);
1024  } else {
1025  return after_paths.find_file(filename);
1026  }
1027  } // FirstAbsoluteOrLookupWithDotPolicy::operator()
1028 
1029 
1030  /// Helper to fill a provider pack: main specialisation
1031  template
1032  <typename TestEnv, typename Pack, typename Prov, typename... Others>
1033  struct ProviderPackFiller<TestEnv, Pack, Prov, Others...> {
1034  static void fill(TestEnv const& env, Pack& pack)
1035  {
1036  pack.set(env.template Provider<Prov>());
1038  } // fill()
1039 
1040  }; // ProviderPackFiller<TestEnv, Pack, Prov, Others...>
1041 
1042  // end-of-recursion specialisation
1043  template <typename TestEnv, typename Pack>
1044  struct ProviderPackFiller<TestEnv, Pack> {
1045  static void fill(TestEnv const&, Pack&) {}
1046  }; // ProviderPackFiller<>
1047 
1048 
1049  } // namespace details
1050 
1051 
1052  //****************************************************************************
1053  template <typename ConfigurationClass>
1055 
1056  mf::LogInfo("Test") << config.ApplicationName() << " completed.";
1057 
1058  } // BasicTesterEnvironment<>::~BasicTesterEnvironment()
1059 
1060 
1061  /** **************************************************************************
1062  * @brief Compiles a parameter set from a string
1063  * @return a parameters set with the complete configuration
1064  */
1065  template <typename ConfigurationClass>
1069  {
1070  //fhicl::ParameterSet global_pset;
1071  //fhicl::make_ParameterSet(cfg, global_pset);
1072  auto global_pset = fhicl::ParameterSet::make(cfg);
1073  return global_pset;
1074  } // BasicTesterEnvironment<>::CompileParameterSet()
1075 
1076 
1077  /** **************************************************************************
1078  * @brief Returns the configuration from a FHiCL file
1079  * @param config_path full path of the FHiCL configuration file
1080  * @return a parameters set with the complete configuration from the file
1081  */
1082  template <typename ConfigurationClass>
1085  (std::string config_path)
1086  {
1087  // configuration file lookup policy
1088  char const* fhicl_env = getenv("FHICL_FILE_PATH");
1089  std::string search_path = fhicl_env? std::string(fhicl_env) + ":": ".:";
1090  details::FirstAbsoluteOrLookupWithDotPolicy policy(search_path);
1091 
1092  // parse a configuration file; obtain intermediate form
1093  //fhicl::intermediate_table table;
1094  //fhicl::parse_document(config_path, policy, table);
1095  // idiom for art 3.0.9
1096  auto table = fhicl::parse_document(config_path, policy);
1097 
1098  // translate into a parameter set
1099  //fhicl::ParameterSet global_pset;
1100  //fhicl::make_ParameterSet(table, global_pset);
1101  auto global_pset = fhicl::ParameterSet::make(table);
1102 
1103  return global_pset;
1104  } // BasicTesterEnvironment<>::ParseParameters()
1105 
1106 
1107  /** **************************************************************************
1108  * @brief Fills the configuration
1109  *
1110  * The complete configuration (message facility and services) is read and
1111  * saved, hence accessible by Parameters() method.
1112  *
1113  * The configuration file path is taken by default from the first argument
1114  * of the test.
1115  * If that first argument is not present or empty, the default configuration
1116  * path is received from the configurer.
1117  * If the configuration path is still empty, a hard-coded configuration
1118  * is used; otherwise, the FHiCL file specified in that path is parsed and
1119  * used as full configuration.
1120  */
1121  template <typename ConfigurationClass>
1123  std::string config_path = config.ConfigurationPath();
1124  params = config_path.empty()?
1125  DefaultParameters(): ParseParameters(config_path);
1126  } // BasicTesterEnvironment::Configure()
1127 
1128 
1129  /** **************************************************************************
1130  * @brief Sets the message facility up
1131  *
1132  * Message facility configuration is expected in "services.message" parameter
1133  * set. If not there, the default configuration is used.
1134  */
1135  template <typename ConfigurationClass>
1137  (fhicl::ParameterSet const& pset, std::string /* appl_name = "" */) const
1138  {
1139  fhicl::ParameterSet mf_pset;
1140 
1141  if
1142  (!pset.get_if_present(config.ServiceParameterSetPath("message"), mf_pset))
1143  {
1144  mf_pset
1145  = CompileParameterSet(config.DefaultServiceConfiguration("message"));
1146  std::cout << "Using default message facility configuration:\n"
1147  << mf_pset.to_indented_string(1) << std::endl;
1148  } // if no configuration is available
1149 
1150  //mf::StartMessageFacility(mf::MessageFacilityService::SingleThread, mf_pset);
1151  //if (!appl_name.empty()) mf::SetApplicationName(appl_name);
1152  //mf::SetContext("Initialization");
1153  // mf::LogProblem("MessageFacility") << "Error messages are shown.";
1154  // mf::LogPrint("MessageFacility") << "Warning messages are shown.";
1155  // mf::LogVerbatim("MessageFacility") << "Info messages are shown.";
1156  // mf::LogTrace("MessageFacility") << "Debug messages are shown.";
1157  // LOG_TRACE("MessageFacility")
1158  // << "LOG_TRACE/LOG_DEBUG messages are not compiled away.";
1159 
1160  //mf::LogInfo("MessageFacility") << "MessageFacility started.";
1161  //mf::SetModuleName("main");
1162 
1163  } // BasicTesterEnvironment::SetupMessageFacility()
1164 
1165 
1166 
1167  template <typename ConfigurationClass>
1169  ) {
1170 
1171  //
1172  // get the configuration
1173  //
1174  Configure();
1175 
1176  //
1177  // set up the message facility
1178  //
1180 
1181  //
1182  // Optionally print the configuration
1183  //
1184  {
1185  mf::LogInfo msg("Configuration");
1186  msg << "Complete configuration (";
1187  if (config.ConfigurationPath().empty()) msg << "default";
1188  else msg << "'" << config.ConfigurationPath() << "'";
1189  msg << "):\n" << Parameters().to_indented_string(1);
1190  }
1191 
1192 
1193  mf::LogInfo("Test") << config.ApplicationName() << " base setup complete.";
1194 
1195  } // BasicTesterEnvironment<>::Setup()
1196 
1197 
1198 } // namespace testing
1199 
1200 #endif // TEST_UNIT_TEST_BASE_H
static QCString name
Definition: declinfo.cpp:673
Container of service providers accessed by type and optional label.
Definition: ProviderList.h:160
std::string main_test_name
name of main test algorithm
fhicl::ParameterSet const & Parameters() const
Returns the full configuration.
LArSoft test utilities.
fhicl::ParameterSet params
full configuration of the test
Prov const * Provider() const
Return the specified provider (throws if not available)
static fhicl::ParameterSet ParseParameters(std::string config_path)
Fills the test configuration from file or from default.
static void CreateDefaultResource(Args &&...args)
Constructs and registers a new resource with no name.
void ParseArguments(int argc, char **argv)
Parses arguments.
void msg(const char *fmt,...)
Definition: message.cpp:107
std::string BuildDefaultConfiguration() const
A string describing the full default parameter set.
std::map< std::string, std::string > PathMap_t
void SetApplicationName(std::string name)
Sets the name of the application.
std::map< std::string, std::string > ConfigurationMap_t
std::string DefaultConfiguration() const
A string describing the full default parameter set.
BasicTesterEnvironment(Configuration_t const &cfg_obj, bool bSetup=true)
Setup from a configuration.
virtual fhicl::ParameterSet DefaultParameters() const
Creates a full configuration for the test.
fhicl::ParameterSet TesterParameters(std::string test_name) const
Returns the configuration of the specified test.
std::string string
Definition: nybbler.cc:12
static ParameterSet make(intermediate_table const &tbl)
Definition: ParameterSet.cc:68
Prov * SetupProvider(Args...args)
Sets a service provider up by calling its testing::setupProvider()
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
ConfigurationMap_t services_default_cfg
Configuration of all the services.
std::shared_ptr< Resource_t > ResourcePtr_t
BasicEnvironmentConfiguration(std::string name)
Constructor; accepts the name as parameter.
BasicTesterEnvironment(bool bSetup=true)
Constructor: sets everything up and declares the test started.
Configuration_t config
instance of the configurer
bool first
whether we are waiting for the first query
std::string main_test_path
path of main test algorithm configuration
void SetMainTesterParameterSetName(std::string name)
Sets the FHiCL name for the configuration of the test algorithm.
std::string DefaultTesterConfiguration(std::string tester_name) const
A string describing default parameter set to configure specified test.
std::string ExecutablePath() const
Returns the name of the executable as started.
virtual void Configure()
Reads and translates the configuration.
BasicTesterEnvironment(Configuration_t &&cfg_obj, bool bSetup=true)
details::CommandLineArguments arguments
command line arguments
Class holding a configuration for a test environment.
Environment for a test.
static bool hasResource(std::string name="")
BasicEnvironmentConfiguration(int argc, char **argv, std::string name)
std::string Executable() const
Returns the name of the executable as started.
std::string TesterParameterSetPath(std::string name) const
FHiCL path for the configuration of the test algorithm.
static std::string DefaultApplicationName()
Returns the default test name.
Configuration_t const & Config() const
Returns a read-only version of the configuration.
Prov * AcquireProvider(std::unique_ptr< Prov > &&prov)
Acquires a service provider.
static void AddDefaultSharedResource(ResourcePtr_t res_ptr)
Adds a shared resource to the resource registry (empty name)
string filename
Definition: train.py:213
void ParseCommandLine(int argc, char **argv)
Extracts arguments from the command line, uses first one as config path.
CommandLineArguments()
Constructor: automatically parses from Boost arguments.
PathMap_t test_paths
Set of paths for tester configuration.
std::string MainTesterParameterSetPath() const
FHiCL path for the configuration of the test algorithm.
CommandLineArguments(int argc, char **argv)
Constructor: parses from specified arguments.
void SetupMessageFacility(fhicl::ParameterSet const &pset, std::string applName="standalone")
Sets up the message facility service.
std::string ServiceParameterSetPath(std::string name) const
FHiCL path for the configuration of the service.
Prov::providers_type ProviderPackFor() const
Returns a provider pack for the specified provider.
static Resource_t & DestroyResource(std::string name="")
Destroys the specified resource (does nothing if no such resource)
Prov * SimpleProviderSetup()
Oversimplified provider setup.
Prov * SetupProviderFor(Args...args)
Sets a provider up, recording it as implementation of Interface.
ProviderList providers
list of available providers
static std::map< std::string, ResourcePtr_t > Resources
Container for a list of pointers to providers.
Definition: ProviderPack.h:90
bool hasArgument(size_t iArg) const
Returns whether we have arguments up to the iArg-th (0-based)
void DropProvider()
Removes and destroys the specified provider.
std::string config_path
configuration file path
std::string MainTesterParameterSetName() const
Name of the test algorithm instance.
std::vector< std::string > args
command line arguments (from argv[0])
static bool ReplaceSharedResource(std::string res_name, Resource_t const *old_res_ptr, ResourcePtr_t res_ptr)
Adds a shared resource only if it is old_res_ptr.
std::string DefaultServiceConfiguration(std::string service_name) const
A string describing the default parameter set to configure the test.
static Resource_t & Resource(std::string name="")
Retrieves the specified resource, or throws if not available.
static Config * config
Definition: config.cpp:1054
static ResourcePtr_t ShareResource(std::string name="")
Retrieves the specified resource for sharing (nullptr if none)
std::string getenv(std::string const &name)
Definition: getenv.cc:15
def move(depos, offset)
Definition: depos.py:107
std::vector< std::string > const & Arguments() const
Returns the list of non-Boost-test arguments on the command line.
std::string exec_name
name of the test executable (from argv[0])
Prov * SetupProviderFromService(std::string name)
Sets a service provider up by calling its testing::setupProvider()
TESTENV CreateTesterEnvironment(CONFIG &&config, ARGS...other_args)
Constructs and returns a TesterEnvironment object.
bool is_absolute_filepath(std::string const &qualified_filename)
Definition: filesystem.cc:23
std::string to_indented_string() const
fhicl::ParameterSet ServiceParameters(std::string service_name) const
Returns the configuration of the specified service.
std::string const & Argument(size_t iArg) const
Returns the value of the iArg-th (0-based; no range check!)
Reads and makes available the command line parameters.
Utility class providing singleton objects to the derived classes.
void SetTesterParameterSetPath(std::string test_name, std::string path)
Sets the FHiCL path for the configuration of a test algorithm.
std::string BuildDefaultTestConfiguration() const
A string describing the full default parameter set.
PathMap_t service_paths
Set of paths for service configuration.
int operator()() const
cet::search_path after_paths
path for the other queries
virtual void Setup()
The complete initialization, ran at construction by default.
A test environment with some support for service providers.
BasicEnvironmentConfiguration()
Default constructor; this is what is used in Boost unit test.
Prov * AcquireProviderFor(std::unique_ptr< Prov > &&prov)
Acquires a service provider implementing an interface.
void SetConfigurationPath(std::string path)
Sets the path to the configuration file.
Prov * SetupProviderFromServiceFor(std::string name)
Sets a provider up, recording it as implementation of Interface.
fhicl::ParameterSet TesterParameters() const
Returns the configuration of the main test (undefined if no main test)
void AddDefaultTesterConfiguration(std::string tester_cfg)
Adds a default configuration for the main tester.
static ResourcePtr_t ProvideDefaultSharedResource(ResourcePtr_t res_ptr)
Creates a shared resource as default only if none exists yet.
def fill(s)
Definition: translator.py:93
std::string ConfigurationPath() const
Path to the configuration file.
intermediate_table parse_document(std::string const &filename, cet::filepath_maker &maker)
Definition: parse.cc:720
std::optional< T > get_if_present(std::string const &key) const
Definition: ParameterSet.h:224
void Configure(string mesg)
Definition: gEvServ.cxx:196
std::string appl_name
name of the application
T copy(T const &v)
void FillProviderPack(gar::ProviderPack< Provs... > &pack) const
Fills the specified provider pack with providers.
std::string BuildDefaultServiceConfiguration() const
A string describing the full default parameter set.
std::vector< std::string > const & EexcutableArguments() const
Returns the list of non-Boost-test arguments on the command line.
void Clear()
Erases the stored arguments.
void SetServiceParameterSetPath(std::string service_name, std::string path)
Sets the FHiCL path for the configuration of a test algorithm.
void SetMainTesterParameterSetPath(std::string path)
Sets the FHiCL path for the configuration of the main test algorithm.
void DefaultInit()
Initialize with some default values.
static fhicl::ParameterSet CompileParameterSet(std::string cfg)
Compiles a parameter set from a string.
static ResourcePtr_t ProposeDefaultSharedResource(Args &&...args)
Creates a shared resource as default only if none exists yet.
ConfigurationMap_t analyzers_default_cfg
Configuration of all the analyzer modules.
void SetApplicationName(string const &applicationName)
static ResourcePtr_t CreateResource(std::string res_name, Args &&...args)
Constructs and registers a new resource with a specified name.
static void AddSharedResource(std::string res_name, ResourcePtr_t res_ptr)
Adds a shared resource to the resource registry.
virtual ~BasicTesterEnvironment()
Destructor: closing remarks.
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
QTextStream & endl(QTextStream &s)
virtual void SetupMessageFacility() const
BasicEnvironmentConfiguration(int argc, char **argv)
Constructor: acquires parameters from the command line.