TestHelpers.hpp
Go to the documentation of this file.
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 /// \file
5 /// Commonly used routines, functions and definitions shared amongst unit tests
6 
7 #pragma once
8 
10 
11 #include <algorithm>
12 #include <array>
13 #include <boost/algorithm/string/predicate.hpp>
14 #include <cstddef>
15 #include <iterator>
16 #include <memory>
17 #include <ostream>
18 #include <random>
19 #include <string>
20 #include <tuple>
21 
22 #include "DataStructures/DataBox/TagName.hpp"
24 #include "ErrorHandling/Assert.hpp"
25 #include "ErrorHandling/Error.hpp"
27 #include "Parallel/Serialize.hpp"
28 #include "Utilities/ContainerHelpers.hpp"
30 #include "Utilities/Gsl.hpp"
31 #include "Utilities/MakeArray.hpp"
32 #include "Utilities/MakeString.hpp"
33 #include "Utilities/Requires.hpp"
34 #include "Utilities/StdArrayHelpers.hpp" // IWYU pragma: keep
35 #include "Utilities/TMPL.hpp"
36 #include "Utilities/Tuple.hpp"
37 #include "Utilities/TypeTraits.hpp"
38 #include "Utilities/TypeTraits/HasEquivalence.hpp"
39 
40 /*!
41  * \ingroup TestingFrameworkGroup
42  * \brief Serializes and deserializes an object `t` of type `T`
43  */
44 template <typename T>
46  static_assert(
48  "Cannot use serialize_and_deserialize if a class is not default "
49  "constructible.");
50  return deserialize<T>(serialize<T>(t).data());
51 }
52 
53 /// \ingroup TestingFrameworkGroup
54 /// \brief Tests the serialization of comparable types
55 /// \example
56 /// \snippet Test_PupStlCpp11.cpp example_serialize_comparable
57 template <typename T>
58 void test_serialization(const T& t) {
59  static_assert(tt::has_equivalence_v<T>, "No operator== for T");
60  CHECK(t == serialize_and_deserialize(t));
61 }
62 
63 /// \ingroup TestingFrameworkGroup
64 /// \brief Test the serialization of a derived class via a base class pointer
65 /// \example
66 /// \snippet Test_PupStlCpp11.cpp example_serialize_derived
67 /// \tparam B the base class
68 /// \tparam D the derived class
69 /// \tparam Args deduced from `args`
70 /// \param args arguments passed to a constructor of the derived class
71 template <typename B, typename D, typename... Args>
72 void test_serialization_via_base(Args&&... args) {
73  static_assert(std::is_base_of_v<B, D>,
74  "passed input type is not derived from specified base");
75  static_assert(tt::has_equivalence_v<D>, "No operator== for derived class");
76  Parallel::register_derived_classes_with_charm<B>();
77  std::unique_ptr<B> base = std::make_unique<D>(args...);
79  CHECK_FALSE(nullptr == dynamic_cast<const D*>(pupped_base.get()));
80  const D derived(args...);
81  CHECK(derived == dynamic_cast<const D&>(*pupped_base));
82 }
83 
84 /// Test for copy semantics assuming operator== is implement correctly
85 template <typename T, Requires<tt::has_equivalence<T>::value> = nullptr>
86 void test_copy_semantics(const T& a) {
88  "Class is not copy assignable.");
90  "Class is not copy constructible.");
91  T b = a;
92  CHECK(b == a);
93  // clang-tidy: intentionally not a reference to force invocation of copy
94  // constructor
95  const T c(a); // NOLINT
96  CHECK(c == a);
97 #if defined(__clang__) && __clang_major__ > 6
98 #pragma GCC diagnostic push
99 #pragma GCC diagnostic ignored "-Wself-assign-overloaded"
100 #endif // defined(__clang__) && __clang_major__ > 6
101  // clang-tidy: self-assignment
102  b = b; // NOLINT
103 #if defined(__clang__) && __clang_major__ > 6
104 #pragma GCC diagnostic pop
105 #endif // defined(__clang__) && __clang_major__ > 6
106  CHECK(b == a);
107 }
108 
109 /// Test for move semantics assuming operator== is implemented correctly.
110 /// \requires `std::is_rvalue_reference<decltype(a)>::%value` is true.
111 /// If T is not default constructible, you pass additional
112 /// arguments that are used to construct a T.
113 template <typename T, Requires<tt::has_equivalence<T>::value> = nullptr,
114  typename... Args>
115 void test_move_semantics(T&& a, const T& comparison, Args&&... args) {
116  static_assert(std::is_rvalue_reference<decltype(a)>::value,
117  "Must move into test_move_semantics");
119  "Class is not nothrow move assignable.");
121  "Class is not nothrow move constructible.");
122  if (&a == &comparison or a != comparison) {
123  // We use ERROR instead of ASSERT (which we normally should be using) to
124  // guard against someone writing tests in Release mode where ASSERTs don't
125  // show up.
126  ERROR("'a' and 'comparison' must be distinct (but equal in value) objects");
127  }
128  T b(std::forward<Args>(args)...);
129  // clang-tidy: use std::forward instead of std::move
130  b = std::move(a); // NOLINT
131  CHECK(b == comparison);
132  T c(std::move(b));
133  CHECK(c == comparison);
134 }
135 
136 /// Test for move semantics assuming operator== is implemented correctly.
137 /// \requires `std::is_rvalue_reference<decltype(a)>::%value` is true.
138 /// This version is included for use with the rare type that (likely
139 /// incorrectly) has not marked the move assignment and move construction
140 /// operators `noexcept`
141 template <typename T, Requires<tt::has_equivalence<T>::value> = nullptr,
142  typename... Args>
143 void test_throwing_move_semantics(T&& a, const T& comparison,
144  Args&&... args) noexcept {
145  static_assert(std::is_rvalue_reference<decltype(a)>::value,
146  "Must move into test_move_semantics");
147  static_assert(std::is_move_assignable<T>::value,
148  "Class is not nothrow move assignable.");
150  "Class is not nothrow move constructible.");
151  if (&a == &comparison or a != comparison) {
152  // We use ERROR instead of ASSERT (which we normally should be using) to
153  // guard against someone writing tests in Release mode where ASSERTs don't
154  // show up.
155  ERROR("'a' and 'comparison' must be distinct (but equal in value) objects");
156  }
157  T b(std::forward<Args>(args)...);
158  // clang-tidy: use std::forward instead of std::move
159  b = std::move(a); // NOLINT
160  CHECK(b == comparison);
161  T c(std::move(b));
162  CHECK(c == comparison);
163 }
164 
165 // Test for iterators
166 template <typename Container>
167 void test_iterators(Container& c) {
168  CHECK(std::distance(c.begin(), c.end()) ==
169  static_cast<decltype(std::distance(c.begin(), c.end()))>(c.size()));
170  CHECK(c.begin() == c.cbegin());
171  CHECK(c.end() == c.cend());
172 
173  const auto& const_c = c;
174  CHECK(std::distance(const_c.begin(), const_c.end()) ==
175  static_cast<decltype(std::distance(const_c.begin(), const_c.end()))>(
176  const_c.size()));
177  CHECK(const_c.begin() == const_c.cbegin());
178  CHECK(const_c.end() == const_c.cend());
179 }
180 
181 // Test for reverse iterators
182 template <typename Container>
183 void test_reverse_iterators(Container& c) {
184  CHECK(std::distance(c.rbegin(), c.rend()) ==
185  static_cast<decltype(std::distance(c.rbegin(), c.rend()))>(c.size()));
186 
187  CHECK(c.rbegin() == c.crbegin());
188  CHECK(c.rend() == c.crend());
189 
190  auto it = c.begin();
191  auto rit = c.rbegin();
192  auto end = c.end();
193  auto rend = c.rend();
194  auto cit = c.cbegin();
195  auto cend = c.cend();
196  auto crit = c.crbegin();
197  auto crend = c.crend();
198 
199  for (size_t i = 0; i < c.size(); i++) {
200  CHECK(*it == *(std::prev(rend, 1)));
201  CHECK(*rit == *(std::prev(end, 1)));
202  CHECK(*cit == *(std::prev(crend, 1)));
203  CHECK(*crit == *(std::prev(cend, 1)));
204  it++;
205  rit++;
206  rend--;
207  end--;
208  crit++;
209  cit++;
210  crend--;
211  cend--;
212  }
213 
214  const auto& const_c = c;
215  CHECK(std::distance(const_c.begin(), const_c.end()) ==
216  static_cast<decltype(std::distance(const_c.begin(), const_c.end()))>(
217  const_c.size()));
218  auto c_it = const_c.begin();
219  auto c_rit = const_c.rbegin();
220  auto c_end = const_c.end();
221  auto c_rend = const_c.rend();
222  for (size_t i = 0; i < c.size(); i++) {
223  CHECK(*c_it == *(std::prev(c_rend, 1)));
224  CHECK(*c_rit == *(std::prev(c_end, 1)));
225  c_it++;
226  c_rit++;
227  c_rend--;
228  c_end--;
229  }
230 }
231 
232 /*!
233  * \ingroup TestingFrameworkGroup
234  * \brief Function to test comparison operators. Pass values with
235  * less < greater.
236  */
237 template <typename T, typename U>
238 void check_cmp(const T& less, const U& greater) {
239  CHECK(less == less);
240  CHECK_FALSE(less == greater);
241  CHECK(less != greater);
242  CHECK_FALSE(less != less);
243  CHECK(less < greater);
244  CHECK_FALSE(greater < less);
245  CHECK(greater > less);
246  CHECK_FALSE(less > greater);
247  CHECK(less <= greater);
248  CHECK_FALSE(greater <= less);
249  CHECK(greater >= less);
250  CHECK_FALSE(less >= greater);
251  CHECK(less <= less);
252  CHECK_FALSE(less < less);
253  CHECK(less >= less);
254  CHECK_FALSE(less > less);
255 }
256 
257 /*!
258  * \ingroup TestingFrameworkGroup
259  * \brief Check a op b == c and also the op= version.
260  */
261 #define CHECK_OP(a, op, b, c) \
262  do { \
263  const auto& a_ = a; \
264  const auto& b_ = b; \
265  const auto& c_ = c; \
266  CHECK(a_ op b_ == c_); \
267  auto f = a_; \
268  CHECK((f op## = b_) == c_); \
269  CHECK(f == c_); \
270  } while (false)
271 
272 /*!
273  * \ingroup TestingFrameworkGroup
274  * \brief Calculates the derivative of an Invocable at a point x - represented
275  * by an array of doubles - in the domain of `map` with a sixth-order finite
276  * difference method.
277  *
278  * \details Intended for use with CoordinateMaps taking the domain {xi,eta,zeta}
279  * to the range {x,y,z}. This function calculates the derivative along the
280  * direction given by `direction` with a step size of `h`.
281  *
282  * \requires direction be between 0 and VolumeDim
283  */
284 template <typename Invocable, size_t VolumeDim>
285 std::result_of_t<const Invocable&(const std::array<double, VolumeDim>&)>
286 numerical_derivative(const Invocable& function,
288  const size_t direction, const double delta) noexcept {
289  ASSERT(0 <= direction and direction < VolumeDim,
290  "Trying to take derivative along axis " << direction);
291 
292  const auto dx = [direction, delta]() {
293  auto d = make_array<VolumeDim>(0.);
294  gsl::at(d, direction) = delta;
295  return d;
296  }();
297 
298  const std::array<double, VolumeDim> x_1ahead = x + dx;
299  const std::array<double, VolumeDim> x_2ahead = x_1ahead + dx;
300  const std::array<double, VolumeDim> x_3ahead = x_2ahead + dx;
301  const std::array<double, VolumeDim> x_1behind = x - dx;
302  const std::array<double, VolumeDim> x_2behind = x_1behind - dx;
303  const std::array<double, VolumeDim> x_3behind = x_2behind - dx;
304  return (1.0 / (60.0 * delta)) * function(x_3ahead) +
305  (-3.0 / (20.0 * delta)) * function(x_2ahead) +
306  (0.75 / delta) * function(x_1ahead) +
307  (-0.75 / delta) * function(x_1behind) +
308  (3.0 / (20.0 * delta)) * function(x_2behind) +
309  (-1.0 / (60.0 * delta)) * function(x_3behind);
310 }
311 
312 struct NonCopyable {
313  constexpr NonCopyable() = default;
314  constexpr NonCopyable(const NonCopyable&) = delete;
315  constexpr NonCopyable& operator=(const NonCopyable&) = delete;
316  constexpr NonCopyable(NonCopyable&&) = default;
317  NonCopyable& operator=(NonCopyable&&) = default;
318  ~NonCopyable() = default;
319 };
320 inline bool operator==(const NonCopyable& /*a*/,
321  const NonCopyable& /*b*/) noexcept {
322  return true;
323 }
324 inline bool operator!=(const NonCopyable& a, const NonCopyable& b) noexcept {
325  return not(a == b);
326 }
327 inline std::ostream& operator<<(std::ostream& os,
328  const NonCopyable& /*v*/) noexcept {
329  return os << "NC";
330 }
331 
333  public:
334  DoesNotThrow() noexcept = default;
335  DoesNotThrow(const DoesNotThrow&) noexcept = default;
336  DoesNotThrow& operator=(const DoesNotThrow&) noexcept = default;
337  DoesNotThrow(DoesNotThrow&&) noexcept = default;
338  DoesNotThrow& operator=(DoesNotThrow&&) noexcept = default;
339  ~DoesNotThrow() = default;
340 };
341 class DoesThrow {
342  public:
343  DoesThrow() noexcept(false);
344  DoesThrow(const DoesThrow&) noexcept(false);
345  DoesThrow& operator=(const DoesThrow&) noexcept(false);
346  DoesThrow(DoesThrow&&) noexcept(false);
347  DoesThrow& operator=(DoesThrow&&) noexcept(false);
348  ~DoesThrow() = default;
349 };
350 
351 /*!
352  * \ingroup TestingFrameworkGroup
353  * \brief Execute `func` and check that it throws an exception `expected`.
354  *
355  * \note The `.what()` strings of the thrown and `expected` exceptions are
356  * compared for a partial match only: the `expected.what()` string must be
357  * contained in (or equal to) the `.what()` string of the thrown exception.
358  */
359 template <typename Exception, typename ThrowingFunctor>
360 void test_throw_exception(const ThrowingFunctor& func,
361  const Exception& expected) {
362  try {
363  func();
364  INFO("Failed to throw any exception");
365  CHECK(false);
366  } catch (Exception& e) {
367  CAPTURE(e.what());
368  CAPTURE(expected.what());
369  CHECK(boost::contains(std::string(e.what()), std::string(expected.what())));
370  } catch (...) {
371  INFO("Failed to throw exception of type " +
372  pretty_type::get_name<Exception>());
373  CHECK(false);
374  }
375 }
376 
377 /// \cond
378 #define MAKE_GENERATOR_IMPL_FIRST_ARG(NAME, ...) NAME
379 #define MAKE_GENERATOR_IMPL_SECOND_ARG(NAME, SEED, ...) SEED
380 /// \endcond
381 
382 /// \ingroup TestingFrameworkGroup
383 /// \brief `MAKE_GENERATOR(NAME [, SEED])` declares a variable of name `NAME`
384 /// containing a generator of type `std::mt19937`.
385 ///
386 /// \details As the generator is made, `INFO` is called to make sure failed
387 /// tests provide seed information. `SEED` is chosen randomly if not supplied,
388 /// otherwise it must be a constant expression.
389 // What is going on here:
390 //
391 // If this is called as MAKE_GENERATOR(NAME):
392 // MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN)
393 // -> MAKE_GENERATOR_IMPL_FIRST_ARG(NAME, DUMMY_TOKEN)
394 // -> NAME
395 // MAKE_GENERATOR_IMPL_SECOND_ARG(
396 // __VA_ARGS__, std::random_device{}(), DUMMY_TOKEN)
397 // -> MAKE_GENERATOR_IMPL_SECOND_ARG(
398 // NAME, std::random_device{}(), DUMMY_TOKEN)
399 // -> std::random_device{}()
400 // So we create NAME with a random seed.
401 //
402 // In this case DUMMY_TOKEN is needed because the "..." in the IMPL
403 // macros has to match at least one thing.
404 //
405 // If this is called as MAKE_GENERATOR(NAME, SEED):
406 // MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN)
407 // -> MAKE_GENERATOR_IMPL_FIRST_ARG(NAME, SEED, DUMMY_TOKEN)
408 // -> NAME
409 // MAKE_GENERATOR_IMPL_SECOND_ARG(
410 // __VA_ARGS__, std::random_device{}(), DUMMY_TOKEN)
411 // -> MAKE_GENERATOR_IMPL_SECOND_ARG(
412 // NAME, SEED, std::random_device{}(), DUMMY_TOKEN)
413 // -> SEED
414 // So we create NAME with seed SEED.
415 //
416 // In this case the DUMMY_TOKEN is not necessary.
417 #define MAKE_GENERATOR(...) \
418  std::mt19937 MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN); \
419  /* Capture everything because we don't know what passed seed uses */ \
420  INFO("Seed is: " << [&]() noexcept { \
421  const auto MAKE_GENERATOR_seed = (MAKE_GENERATOR_IMPL_SECOND_ARG( \
422  __VA_ARGS__, std::random_device{}(), DUMMY_TOKEN)); \
423  MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN) \
424  .seed(MAKE_GENERATOR_seed); \
425  return MakeString{} << MAKE_GENERATOR_seed << " from " __FILE__ ":" \
426  << __LINE__; \
427  }())
428 
429 /*!
430  * \ingroup TestingFrameworkGroup
431  * \brief A wrapper around Catch's CHECK macro that checks approximate equality
432  * of each entry in each tag within a variables.
433  */
434 #define CHECK_VARIABLES_APPROX(a, b) \
435  do { \
436  INFO(__FILE__ ":" + std::to_string(__LINE__) + ": " #a " == " #b); \
437  check_variables_approx<std::common_type_t< \
438  std::decay_t<decltype(a)>, std::decay_t<decltype(b)>>>::apply(a, b); \
439  } while (false)
440 
441 /*!
442  * \ingroup TestingFrameworkGroup
443  * \brief Same as `CHECK_VARIABLES_APPROX`, but with a user-defined Approx.
444  * The third argument should be of type `Approx`.
445  */
446 #define CHECK_VARIABLES_CUSTOM_APPROX(a, b, appx) \
447  do { \
448  INFO(__FILE__ ":" + std::to_string(__LINE__) + ": " #a " == " #b); \
449  check_variables_approx<std::common_type_t< \
450  std::decay_t<decltype(a)>, std::decay_t<decltype(b)>>>::apply(a, b, \
451  appx); \
452  } while (false)
453 
454 template <typename Tag, typename TagList>
455 const auto& extract_value_for_variables_comparison(
456  const Variables<TagList>& input_vars, std::true_type /*meta*/) noexcept {
457  // only Scalars of spin-weighted quantities are currently allowed. If that
458  // changes, then this solution will no longer be workable.
459  return get(get<Tag>(input_vars)).data();
460 }
461 
462 template <typename Tag, typename TagList>
463 const auto& extract_value_for_variables_comparison(
464  const Variables<TagList>& input_vars, std::false_type /*meta*/) noexcept {
465  return get<Tag>(input_vars);
466 }
467 
468 template <typename T>
470 
471 template <typename TagList>
472 struct check_variables_approx<Variables<TagList>> {
473  // clang-tidy: non-const reference
474  static void apply(const Variables<TagList>& a, const Variables<TagList>& b,
475  Approx& appx = approx) { // NOLINT
476  tmpl::for_each<TagList>([&a, &b, &appx](auto x) {
477  using Tag = typename decltype(x)::type;
478  INFO(db::tag_name<Tag>());
479  const auto& a_val = extract_value_for_variables_comparison<Tag>(
481  const auto& b_val = extract_value_for_variables_comparison<Tag>(
483  CHECK_ITERABLE_CUSTOM_APPROX(a_val, b_val, appx);
484  });
485  }
486 };
487 
488 // an additional overload for SpinWeighted quantities that just forwards to
489 // checking on the wrapped `data()` for the SpinWeighted
490 template <typename T>
491 struct check_iterable_approx<T, Requires<is_any_spin_weighted_v<T>>> {
492  // clang-tidy: non-const reference
493  static void apply(const T& a, const T& b, Approx& appx = approx) { // NOLINT
495  appx);
496  }
497 };
498 
499 /*!
500  * \ingroup TestingFrameworkGroup
501  * \brief A test utility for verifying that an element-wise function, `function`
502  * acts identically to the same operation applied to each element of a container
503  * separately. This macro invokes `test_element_wise_function()` (which gives a
504  * more complete documentation of the element-wise checking operations and
505  * arguments).
506  */
507 #define CHECK_ELEMENT_WISE_FUNCTION_APPROX(function, arguments) \
508  do { \
509  INFO(__FILE__ ":" + std::to_string(__LINE__) + \
510  ": " #function ", " #arguments); \
511  test_element_wise_function(function, arguments); \
512  } while (false)
513 
514 /*!
515  * \ingroup TestingFrameworkGroup
516  * \brief Same as `CHECK_ELEMENT_WISE_FUNCTION_APPROX`, but with a user-defined
517  * function `at_operator` and `size_of_operator`, each of which correspond to
518  * arguments of `test_element_wise_function()` (which gives a more complete
519  * documentation of the element-wise checking operations and arguments).
520  */
521 #define CHECK_CUSTOM_ELEMENT_WISE_FUNCTION_APPROX( \
522  function, arguments, at_operator, size_of_operator) \
523  do { \
524  INFO(__FILE__ ":" + std::to_string(__LINE__) + \
525  ": " #function ", " #arguments); \
526  test_element_wise_function(function, arguments, at_operator, \
527  size_of_operator); \
528  } while (false)
529 
530 namespace TestHelpers_detail {
531 // CHECK forwarding for parameter pack expansion. Return value also required for
532 // easy parameter pack use.
533 template <typename Approx>
534 SPECTRE_ALWAYS_INLINE int call_check_approx(double a, double b,
535  Approx custom_approx) noexcept {
536  CHECK(custom_approx(a) == b);
537  return 0;
538 }
539 
540 template <typename Approx>
541 SPECTRE_ALWAYS_INLINE int call_check_approx(std::complex<double> a,
543  Approx custom_approx) noexcept {
544  CHECK(custom_approx(real(a)) == real(b));
545  CHECK(custom_approx(imag(a)) == imag(b));
546  return 0;
547 }
548 
549 // internal implementation for checking that an element-wise function acts
550 // appropriately on a container object by first trying the operation on the
551 // containers, then checking that the operation has been performed as it would
552 // by looping over the elements and performing the same operation on each. This
553 // also supports testing functions between containers and single elements by
554 // making use of `get_size` and `get_element`.
555 template <typename Function, typename IndexingFunction, typename SizeFunction,
556  typename... Arguments, size_t... Is>
557 void test_element_wise_function_impl(
558  Function element_wise_function,
559  const gsl::not_null<std::tuple<Arguments...>*> arguments,
560  IndexingFunction at, SizeFunction size, std::index_sequence<Is...> /*meta*/,
561  Approx custom_approx) noexcept {
562  const size_t size_value =
563  std::max({get_size(std::get<Is>(*arguments), size)...});
564  tuple_fold(
565  *arguments, [&size, &size_value ](const auto x) noexcept {
566  if (not(get_size(x, size) == size_value or get_size(x, size) == 1)) {
567  ERROR(
568  "inconsistent sized arguments passed "
569  "to test_element_wise_function");
570  }
571  });
572  // Some operators might modify the values of the arguments. We take care to
573  // verify that the modifications (or lack thereof) are also as expected.
574  auto original_arguments = std::make_tuple(std::get<Is>(*arguments)...);
575  const auto result = element_wise_function(std::get<Is>(*arguments)...);
576  for (size_t i = 0; i < size_value; ++i) {
577  // the arguments which modify their arguments must be passed lvalues
578  auto element_of_arguments = std::make_tuple(
579  get_element(std::get<Is>(original_arguments), i, at)...);
580  call_check_approx(
581  get_element(result, i, at),
582  element_wise_function(std::get<Is>(element_of_arguments)...),
583  custom_approx);
584  // ensure that the final state of the arguments matches
585  expand_pack(call_check_approx(get_element(std::get<Is>(*arguments), i, at),
586  std::get<Is>(element_of_arguments),
587  custom_approx)...);
588  }
589 }
590 } // namespace TestHelpers_detail
591 
592 /*!
593  * \ingroup TestingFrameworkGroup
594  * \brief Utility function for verifying the action of an element-wise function
595  * on containers, or on some combination of containers and compatible
596  * non-containers (e.g. `DataVectors` with `doubles`).
597  *
598  * \details The ability to specify custom functions for `at` and `size` is
599  * useful for more intricate containers. For instance, multidimensional types
600  * can be used with this function with a size function that returns the full
601  * number of elements, and an `at` function which indexes the multidimensional
602  * type in a flattened fashion.
603  *
604  * parameters:
605  * \param element_wise_function A callable which is expected to act in an
606  * element-wise fashion, must be compatible both with the container and its
607  * individual elements.
608  * \param arguments A tuple of arguments to be tested
609  * \param at A function to override the container access function. Defaults to
610  * an object which simply calls `container.at(i)`. A custom callable must take
611  * as arguments the container(s) used in `arguments` and a `size_t` index (in
612  * that order), and return an element compatible with
613  * `element_wise_function`. This function signature follows the convention of
614  * `gsl::at`.
615  * \param size A function to override the container size function. Defaults to
616  * an object which simply calls `container.size()`. A custom callable must take
617  * as argument the container(s) used in `arguments`, and return a size_t. This
618  * function signature follows the convention of `std::size`.
619  * \param custom_approx An object of type `Approx` specifying an alternative
620  * precision with which to test the element-wise function
621  */
622 template <typename ElementWiseFunction,
623  typename AtFunction = GetContainerElement,
624  typename SizeFunction = GetContainerSize, typename... Arguments>
626  ElementWiseFunction element_wise_function,
627  const gsl::not_null<std::tuple<Arguments...>*> arguments,
628  AtFunction at = GetContainerElement{},
629  SizeFunction size = GetContainerSize{},
630  Approx custom_approx = approx) noexcept {
631  TestHelpers_detail::test_element_wise_function_impl(
632  element_wise_function, arguments, at, size,
633  std::make_index_sequence<sizeof...(Arguments)>{}, custom_approx);
634 }
expand_pack
constexpr void expand_pack(Ts &&...) noexcept
Allows zero-cost unordered expansion of a parameter.
Definition: TMPL.hpp:547
gsl::at
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid.
Definition: Gsl.hpp:125
RegisterDerivedClassesWithCharm.hpp
std::true_type
std::string
get
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:639
test_serialization
void test_serialization(const T &t)
Tests the serialization of comparable types.
Definition: TestHelpers.hpp:58
std::rel_ops::operator!=
T operator!=(T... args)
std::index_sequence
Error.hpp
MakeArray.hpp
std::size
T size(T... args)
TestingFramework.hpp
iterator
NonCopyable
Definition: TestHelpers.hpp:312
random
std::is_default_constructible
tuple
Serialize.hpp
tuple_fold
constexpr void tuple_fold(const std::tuple< Elements... > &tuple, N_aryOp &&op, Args &&... args) noexcept(noexcept(tuple_impl_detail::tuple_fold_impl< ReverseIteration >(tuple, std::forward< N_aryOp >(op), std::make_index_sequence< sizeof...(Elements)>{}, args...)))
Perform a fold over a std::tuple.
Definition: Tuple.hpp:112
CHECK_ITERABLE_CUSTOM_APPROX
#define CHECK_ITERABLE_CUSTOM_APPROX(a, b, appx)
Same as CHECK_ITERABLE_APPROX with user-defined Approx. The third argument should be of type Approx.
Definition: TestingFramework.hpp:151
DoesThrow
Definition: TestHelpers.hpp:341
is_any_spin_weighted
Definition: SpinWeighted.hpp:232
std::is_nothrow_move_constructible
test_move_semantics
void test_move_semantics(T &&a, const T &comparison, Args &&... args)
Test for move semantics assuming operator== is implemented correctly.
Definition: TestHelpers.hpp:115
algorithm
get_size
decltype(auto) get_size(const T &t, SizeFunction size=GetContainerSize{}) noexcept
Retrieve the size of t if t.size() is a valid expression, otherwise if T is fundamental or a std::com...
Definition: ContainerHelpers.hpp:145
std::is_rvalue_reference
test_copy_semantics
void test_copy_semantics(const T &a)
Test for copy semantics assuming operator== is implement correctly.
Definition: TestHelpers.hpp:86
numerical_derivative
std::result_of_t< const Invocable &(const std::array< double, VolumeDim > &)> numerical_derivative(const Invocable &function, const std::array< double, VolumeDim > &x, const size_t direction, const double delta) noexcept
Calculates the derivative of an Invocable at a point x - represented by an array of doubles - in the ...
Definition: TestHelpers.hpp:286
GetContainerElement
Callable struct for the subscript operator. Returns t[i]
Definition: ContainerHelpers.hpp:27
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:36
db::apply
constexpr auto apply(F &&f, const DataBox< BoxTags > &box, Args &&... args) noexcept
Apply the invokable f with argument Tags TagsList from DataBox box
Definition: DataBox.hpp:1424
check_cmp
void check_cmp(const T &less, const U &greater)
Function to test comparison operators. Pass values with less < greater.
Definition: TestHelpers.hpp:238
SPECTRE_ALWAYS_INLINE
#define SPECTRE_ALWAYS_INLINE
Definition: ForceInline.hpp:16
std::ostream
cstddef
Assert.hpp
array
Tuple.hpp
GetContainerSize
Callable struct which retrieves the t.size() for operand t. This will cause a compiler error if no su...
Definition: ContainerHelpers.hpp:18
test_element_wise_function
void test_element_wise_function(ElementWiseFunction element_wise_function, const gsl::not_null< std::tuple< Arguments... > * > arguments, AtFunction at=GetContainerElement{}, SizeFunction size=GetContainerSize{}, Approx custom_approx=approx) noexcept
Utility function for verifying the action of an element-wise function on containers,...
Definition: TestHelpers.hpp:625
memory
serialize_and_deserialize
T serialize_and_deserialize(const T &t)
Serializes and deserializes an object t of type T
Definition: TestHelpers.hpp:45
std::rend
T rend(T... args)
std::is_copy_constructible
ASSERT
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:51
test_throw_exception
void test_throw_exception(const ThrowingFunctor &func, const Exception &expected)
Execute func and check that it throws an exception expected.
Definition: TestHelpers.hpp:360
std::is_nothrow_move_assignable
Variables.hpp
DereferenceWrapper.hpp
Gsl.hpp
std::begin
T begin(T... args)
StdArrayHelpers.hpp
test_serialization_via_base
void test_serialization_via_base(Args &&... args)
Test the serialization of a derived class via a base class pointer.
Definition: TestHelpers.hpp:72
std::result_of_t
Requires.hpp
check_variables_approx
Definition: TestHelpers.hpp:469
std::complex< double >
DoesNotThrow
Definition: TestHelpers.hpp:332
std::end
T end(T... args)
std::is_copy_assignable
Requires
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t
Definition: Requires.hpp:67
ostream
std::unique_ptr
test_throwing_move_semantics
void test_throwing_move_semantics(T &&a, const T &comparison, Args &&... args) noexcept
Test for move semantics assuming operator== is implemented correctly.
Definition: TestHelpers.hpp:143
get_element
decltype(auto) get_element(T &t, const size_t i, SubscriptFunction at=GetContainerElement{}) noexcept
Returns the ith element if T has a subscript operator, otherwise if T is fundamental or a std::comple...
Definition: ContainerHelpers.hpp:117
TMPL.hpp
std::rbegin
T rbegin(T... args)
gsl::not_null
Require a pointer to not be a nullptr
Definition: Gsl.hpp:183
string