OpenKalman
tests.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2021-2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
17 #ifndef OPENKALMAN_COLLECTIONS_TESTS_HPP
18 #define OPENKALMAN_COLLECTIONS_TESTS_HPP
19 
20 #include <string>
21 #include "values/values.hpp"
26 #include "values/tests/tests.hpp"
27 
28 namespace OpenKalman::test
29 {
37 #ifdef __cpp_concepts
38  template<collections::collection Arg1, collections::collection Arg2, typename Err> requires
39  (collections::uniformly_gettable<Arg1> and collections::uniformly_gettable<Arg2> and
40  (collections::uniformly_gettable<Err> or values::value<Err>)) or
41  (stdex::ranges::range<Arg1> and stdex::ranges::range<Arg2> and
42  (stdex::ranges::range<Err> or values::value<Err>))
43  struct TestComparison<Arg1, Arg2, Err>
44 #else
45  template<typename Arg1, typename Arg2, typename Err>
46  struct TestComparison<Arg1, Arg2, Err, std::enable_if_t<
47  collections::collection<Arg1> and collections::collection<Arg2> and
48  ((collections::uniformly_gettable<Arg1> and collections::uniformly_gettable<Arg2> and
49  (collections::uniformly_gettable<Err> or values::value<Err>)) or
50  (stdex::ranges::range<Arg1> and stdex::ranges::range<Arg2> and
51  (stdex::ranges::range<Err> or values::value<Err>)))>>
52 #endif
53  : ::testing::AssertionResult
54  {
55  private:
56 
57  template<std::size_t...Ix>
58  static auto
59  compare_tuple_like(const Arg1& arg1, const Arg2& arg2, const Err& err, std::index_sequence<Ix...>)
60  {
61  if constexpr (collections::uniformly_gettable<Err>)
62  {
63  if ((... and OpenKalman::test::TestComparison {collections::get<Ix>(arg1), collections::get<Ix>(arg2), collections::get<Ix>(err)}))
64  {
65  return ::testing::AssertionSuccess();
66  }
67  else
68  {
69  return (::testing::AssertionFailure() << ... << (std::string(Ix == 0 ? "" : ", ") +
70  std::string(OpenKalman::test::TestComparison {collections::get<Ix>(arg1), collections::get<Ix>(arg2), collections::get<Ix>(err)} ?
71  std::to_string(collections::get<Ix>(arg1)) + "==" + std::to_string(collections::get<Ix>(arg2)) :
72  std::to_string(collections::get<Ix>(arg1)) + "!=" + std::to_string(collections::get<Ix>(arg2)) )));
73  }
74  }
75  else
76  {
77  if ((... and OpenKalman::test::TestComparison {collections::get<Ix>(arg1), collections::get<Ix>(arg2), err}))
78  {
79  return ::testing::AssertionSuccess();
80  }
81  else
82  {
83  return (::testing::AssertionFailure() << ... << (std::string(Ix == 0 ? "" : ", ") +
84  std::string(OpenKalman::test::TestComparison {collections::get<Ix>(arg1), collections::get<Ix>(arg2), err} ?
85  std::to_string(collections::get<Ix>(arg1)) + " == " + std::to_string(collections::get<Ix>(arg2)) :
86  std::to_string(collections::get<Ix>(arg1)) + " != " + std::to_string(collections::get<Ix>(arg2)) )));
87  }
88  }
89  }
90 
91 
92  template<typename A1, typename A2, typename E, typename...Ix>
93  static bool
94  compare_mdarray(const A1& a1, const A2& a2, const E& e, std::string& message, Ix...ix)
95  {
96  static_assert(std::rank_v<A1> == std::rank_v<A2>);
97  if constexpr (std::rank_v<A1> > 0)
98  {
99  static_assert(std::extent_v<A1> == std::extent_v<A2>, "extents of arguments must match");
100  static_assert(not std::is_array_v<Err> or std::extent_v<E> == std::extent_v<A1>, "extents of error argument must match other arguments");
101  bool success = true;
102  for (std::size_t i = 0; i < std::extent_v<A1>; ++i)
103  {
104  if constexpr (std::is_array_v<Err>)
105  {
106  if (not compare_mdarray(a1[i], a2[i], e[i], message, ix..., i)) success = false;
107  }
108  else
109  {
110  if (not compare_mdarray(a1[i], a2[i], e, message, ix..., i)) success = false;
111  }
112  }
113  return success;
114  }
115  else
116  {
117  if (OpenKalman::test::TestComparison {a1, a2, e}) return true;
118  message += ("" + ... + (" " + std::to_string(ix))) + ": " + std::to_string(a1) +
119  "!=" + std::to_string(a2) + "(±" + std::to_string(e) + "). ";
120  return false;
121  }
122  }
123 
124 
125  template<std::size_t...Ix>
126  static auto
127  compare(const Arg1& arg1, const Arg2& arg2, const Err& err)
128  {
129  static_assert(
131  "size of arguments must match");
132 
133  static_assert(values::value<Err> or
135  "size of error margins must match that of arguments");
136 
137  if constexpr (std::rank_v<Arg1> > 1 and std::rank_v<Arg1> == std::rank_v<Arg2> and (std::rank_v<Err> == std::rank_v<Arg1> or values::value<Err>))
138  {
139  std::string message;
140  if (compare_mdarray(arg1, arg2, err, message)) return ::testing::AssertionSuccess();
141  else return ::testing::AssertionFailure() << message;
142  }
143  else if constexpr (collections::uniformly_gettable<Arg1> and collections::uniformly_gettable<Arg2>)
144  {
145  return compare_tuple_like(arg1, arg2, err, std::make_index_sequence<collections::size_of_v<Arg1>>{});
146  }
147  else // if constexpr (stdex::ranges::range<Arg1> and stdex::ranges::range<Arg2>)
148  {
149  std::string message;
150  bool success = true;
151  auto it1 = stdex::ranges::begin(arg1);
152  auto it2 = stdex::ranges::begin(arg2);
153  std::size_t count = 0;
154  if constexpr (stdex::ranges::range<Err>)
155  {
156  for (auto ite = stdex::ranges::begin(err);
157  it1 != stdex::ranges::end(arg1) and it2 != stdex::ranges::end(arg2) and ite != stdex::ranges::end(err);
158  ++it1, ++it2, ++ite, ++count)
159  {
160  if (not OpenKalman::test::TestComparison {*it1, *it2, *ite})
161  {
162  success = false;
163  message += std::to_string(count) + ": " + std::to_string(*it1) + "!=" + std::to_string(*it2) +
164  "(±" + std::to_string(*ite) + "). ";
165  }
166  }
167  }
168  else
169  {
170  for (; it1 != stdex::ranges::end(arg1) and it2 != stdex::ranges::end(arg2); ++it1, ++it2, ++count)
171  {
172  if (not OpenKalman::test::TestComparison {*it1, *it2, err})
173  {
174  success = false;
175  message += std::to_string(count) + ": " + std::to_string(*it1) + "!=" + std::to_string(*it2) +
176  "(±" + std::to_string(err) + "). ";
177  }
178  }
179  }
180  if (success) return ::testing::AssertionSuccess();
181  else return ::testing::AssertionFailure() << message;
182  }
183  }
184 
185  public:
186 
187  TestComparison(const Arg1& arg1, const Arg2& arg2, const Err& err)
188  : ::testing::AssertionResult {compare(arg1, arg2, err)} {}
189 
190  };
191 
192 }
193 
194 
195 #endif
Definition for collections::get.
Definition for collections::collection.
Header file for code relating to values (e.g., scalars and indices)
constexpr auto compare(const A &a, const B &b)
Compare two coordinates::pattern objects lexicographically.
Definition: compare.hpp:40
Definition for collections::size_of.
The size of a sized object (including a collection).
Definition: size_of.hpp:33
Definition: tests.hpp:36
Definition: tests.hpp:22
constexpr bool size_compares_with
T and U are sizes that compare in a particular way based on parameter comp.
Definition: size_compares_with.hpp:98
Basic utilities for OpenKalman testing.
Definition: trait_backports.hpp:64
Definition for collections::uniformly_gettable.