rippled
MultivarJson_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/XRPLF/rippled/
4  Copyright (c) 2023 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/json/MultivarJson.h>
21 #include <ripple/rpc/impl/RPCHelpers.h>
22 
23 #include <ripple/beast/unit_test.h>
24 #include "ripple/beast/unit_test/suite.hpp"
25 #include "ripple/json/json_value.h"
26 #include <cstdint>
27 #include <limits>
28 #include <optional>
29 #include <type_traits>
30 
31 namespace ripple {
32 namespace test {
33 
34 struct MultivarJson_test : beast::unit_test::suite
35 {
36  static auto
37  makeJson(const char* key, int val)
38  {
40  obj1[key] = val;
41  return obj1;
42  };
43 
44  void
45  run() override
46  {
47  Json::Value const obj1 = makeJson("value", 1);
48  Json::Value const obj2 = makeJson("value", 2);
49  Json::Value const obj3 = makeJson("value", 3);
50  Json::Value const jsonNull{};
51 
52  MultivarJson<3> subject{};
53  static_assert(sizeof(subject) == sizeof(subject.val));
54  static_assert(subject.size == subject.val.size());
55  static_assert(
56  std::is_same_v<decltype(subject.val), std::array<Json::Value, 3>>);
57 
58  BEAST_EXPECT(subject.val.size() == 3);
59  BEAST_EXPECT(
60  (subject.val ==
61  std::array<Json::Value, 3>{jsonNull, jsonNull, jsonNull}));
62 
63  subject.val[0] = obj1;
64  subject.val[1] = obj2;
65 
66  {
67  testcase("default copy construction / assignment");
68 
69  MultivarJson<3> x{subject};
70 
71  BEAST_EXPECT(x.val.size() == subject.val.size());
72  BEAST_EXPECT(x.val[0] == subject.val[0]);
73  BEAST_EXPECT(x.val[1] == subject.val[1]);
74  BEAST_EXPECT(x.val[2] == subject.val[2]);
75  BEAST_EXPECT(x.val == subject.val);
76  BEAST_EXPECT(&x.val[0] != &subject.val[0]);
77  BEAST_EXPECT(&x.val[1] != &subject.val[1]);
78  BEAST_EXPECT(&x.val[2] != &subject.val[2]);
79 
81  BEAST_EXPECT((y.val == std::array<Json::Value, 3>{}));
82  y = subject;
83  BEAST_EXPECT(y.val == subject.val);
84  BEAST_EXPECT(&y.val[0] != &subject.val[0]);
85  BEAST_EXPECT(&y.val[1] != &subject.val[1]);
86  BEAST_EXPECT(&y.val[2] != &subject.val[2]);
87 
88  y = std::move(x);
89  BEAST_EXPECT(y.val == subject.val);
90  BEAST_EXPECT(&y.val[0] != &subject.val[0]);
91  BEAST_EXPECT(&y.val[1] != &subject.val[1]);
92  BEAST_EXPECT(&y.val[2] != &subject.val[2]);
93  }
94 
95  {
96  testcase("select");
97 
98  BEAST_EXPECT(
99  subject.select([]() -> std::size_t { return 0; }) == obj1);
100  BEAST_EXPECT(
101  subject.select([]() -> std::size_t { return 1; }) == obj2);
102  BEAST_EXPECT(
103  subject.select([]() -> std::size_t { return 2; }) == jsonNull);
104 
105  // Tests of requires clause - these are expected to match
106  static_assert([](auto&& v) {
107  return requires
108  {
109  v.select([]() -> std::size_t { return 0; });
110  };
111  }(subject));
112  static_assert([](auto&& v) {
113  return requires
114  {
115  v.select([]() constexpr->std::size_t { return 0; });
116  };
117  }(subject));
118  static_assert([](auto&& v) {
119  return requires
120  {
121  v.select([]() mutable -> std::size_t { return 0; });
122  };
123  }(subject));
124 
125  // Tests of requires clause - these are expected NOT to match
126  static_assert([](auto&& a) {
127  return !requires
128  {
129  subject.select([]() -> int { return 0; });
130  };
131  }(subject));
132  static_assert([](auto&& v) {
133  return !requires
134  {
135  v.select([]() -> void {});
136  };
137  }(subject));
138  static_assert([](auto&& v) {
139  return !requires
140  {
141  v.select([]() -> bool { return false; });
142  };
143  }(subject));
144  }
145 
146  {
147  testcase("set");
148 
150  x.set("name1", 42);
151  BEAST_EXPECT(x.val[0].isMember("name1"));
152  BEAST_EXPECT(x.val[1].isMember("name1"));
153  BEAST_EXPECT(x.val[0]["name1"].isInt());
154  BEAST_EXPECT(x.val[1]["name1"].isInt());
155  BEAST_EXPECT(x.val[0]["name1"].asInt() == 42);
156  BEAST_EXPECT(x.val[1]["name1"].asInt() == 42);
157 
158  x.set("name2", "bar");
159  BEAST_EXPECT(x.val[0].isMember("name2"));
160  BEAST_EXPECT(x.val[1].isMember("name2"));
161  BEAST_EXPECT(x.val[0]["name2"].isString());
162  BEAST_EXPECT(x.val[1]["name2"].isString());
163  BEAST_EXPECT(x.val[0]["name2"].asString() == "bar");
164  BEAST_EXPECT(x.val[1]["name2"].asString() == "bar");
165 
166  // Tests of requires clause - these are expected to match
167  static_assert([](auto&& v) {
168  return requires
169  {
170  v.set("name", Json::nullValue);
171  };
172  }(x));
173  static_assert([](auto&& v) {
174  return requires
175  {
176  v.set("name", "value");
177  };
178  }(x));
179  static_assert([](auto&& v) {
180  return requires
181  {
182  v.set("name", true);
183  };
184  }(x));
185  static_assert([](auto&& v) {
186  return requires
187  {
188  v.set("name", 42);
189  };
190  }(x));
191 
192  // Tests of requires clause - these are expected NOT to match
193  struct foo_t final
194  {
195  };
196  static_assert([](auto&& v) {
197  return !requires
198  {
199  v.set("name", foo_t{});
200  };
201  }(x));
202  static_assert([](auto&& v) {
203  return !requires
204  {
205  v.set("name", std::nullopt);
206  };
207  }(x));
208  }
209 
210  {
211  testcase("isMember");
212 
213  // Well defined behaviour even if we have different types of members
214  BEAST_EXPECT(subject.isMember("foo") == decltype(subject)::none);
215 
216  {
217  // All variants have element "One", none have element "Two"
218  MultivarJson<2> s1{};
219  s1.val[0] = makeJson("One", 12);
220  s1.val[1] = makeJson("One", 42);
221  BEAST_EXPECT(s1.isMember("One") == decltype(s1)::all);
222  BEAST_EXPECT(s1.isMember("Two") == decltype(s1)::none);
223  }
224 
225  {
226  // Some variants have element "One" and some have "Two"
227  MultivarJson<2> s2{};
228  s2.val[0] = makeJson("One", 12);
229  s2.val[1] = makeJson("Two", 42);
230  BEAST_EXPECT(s2.isMember("One") == decltype(s2)::some);
231  BEAST_EXPECT(s2.isMember("Two") == decltype(s2)::some);
232  }
233 
234  {
235  // Not all variants have element "One", because last one is null
236  MultivarJson<3> s3{};
237  s3.val[0] = makeJson("One", 12);
238  s3.val[1] = makeJson("One", 42);
239  BEAST_EXPECT(s3.isMember("One") == decltype(s3)::some);
240  BEAST_EXPECT(s3.isMember("Two") == decltype(s3)::none);
241  }
242  }
243 
244  {
245  // NOTE It's fine to change this test when we change API versions
246  testcase("apiVersionSelector");
247 
248  static_assert(MultiApiJson::size == 3);
249  static MultiApiJson x{obj1};
250  x.val[1] = obj2;
251  x.val[2] = obj3;
252 
253  static_assert(
255  static_assert([](auto&& v) {
256  return requires
257  {
258  v.select(apiVersionSelector(1));
259  };
260  }(x));
261 
262  BEAST_EXPECT(x.select(apiVersionSelector(0)) == obj1);
263  BEAST_EXPECT(x.select(apiVersionSelector(2)) == obj2);
264 
265  static_assert(apiVersionSelector(0)() == 0);
266  static_assert(apiVersionSelector(1)() == 0);
267  static_assert(apiVersionSelector(2)() == 1);
268  static_assert(apiVersionSelector(3)() == 2);
269  static_assert(apiVersionSelector(4)() == 2);
270  static_assert(
273  }
274 
275  {
276  // There should be no reson to change this test
277  testcase("apiVersionSelector invariants");
278 
279  static_assert(
281  static_assert(
283  == MultiApiJson::size);
284 
285  BEAST_EXPECT(MultiApiJson::size >= 1);
286  }
287 
288  {
289  testcase("visit");
290 
291  MultivarJson<3> s1{};
292  s1.val[0] = makeJson("value", 2);
293  s1.val[1] = makeJson("value", 3);
294  s1.val[2] = makeJson("value", 5);
295 
296  int result = 1;
297  ripple::visit<1, 3>(
298  s1, [&](Json::Value& json, unsigned int i) -> void {
299  if (BEAST_EXPECT(json.isObject() && json.isMember("value")))
300  {
301  auto const value = json["value"].asInt();
302  BEAST_EXPECT(
303  (value == 2 && i == 1) || //
304  (value == 3 && i == 2) || //
305  (value == 5 && i == 3));
306  result *= value;
307  }
308  });
309  BEAST_EXPECT(result == 30);
310 
311  // Can use fn with constexpr functor
312  static_assert([](auto&& v) {
313  return requires
314  {
315  ripple::visit<1, 3>(
316  v, [](Json::Value&, unsigned int) constexpr {});
317  };
318  }(s1));
319 
320  // Can use fn with deduction over all parameters
321  static_assert([](auto&& v) {
322  return requires
323  {
324  ripple::visit<1, 3>(v, [](auto&, auto) constexpr {});
325  };
326  }(s1));
327 
328  // Can use fn with conversion of version parameter
329  static_assert([](auto&& v) {
330  return requires
331  {
332  ripple::visit<1, 3>(v, [](auto&, std::size_t) constexpr {});
333  };
334  }(s1));
335 
336  // Cannot use fn with const parameter
337  static_assert([](auto&& v) {
338  return !requires
339  {
340  ripple::visit<1, 3>(
341  v, [](Json::Value const&, auto) constexpr {});
342  };
343  }(const_cast<MultivarJson<3> const&>(s1)));
344 
345  // Cannot call visit with size mismatch
346  static_assert([](auto&& v) {
347  return !requires
348  {
349  ripple::visit<1, 2>(
350  v, [](Json::Value&, unsigned int) constexpr {});
351  };
352  }(s1));
353 
354  // Cannot call visit with version offset
355  static_assert([](auto&& v) {
356  return !requires
357  {
358  ripple::visit<0, 2>(
359  v, [](Json::Value&, unsigned int) constexpr {});
360  };
361  }(s1));
362 
363  // Cannot call visit with size mismatch
364  static_assert([](auto&& v) {
365  return !requires
366  {
367  ripple::visit<1, 4>(
368  v, [](Json::Value&, unsigned int) constexpr {});
369  };
370  }(s1));
371 
372  // Cannot call visit with wrong order of versions
373  static_assert([](auto&& v) {
374  return !requires
375  {
376  ripple::visit<3, 1>(
377  v, [](Json::Value&, unsigned int) constexpr {});
378  };
379  }(s1));
380  }
381  }
382 };
383 
385 
386 } // namespace test
387 } // namespace ripple
ripple::test::jtx::json
Inject raw JSON.
Definition: jtx_json.h:31
std::is_same_v
T is_same_v
ripple::MultivarJson::size
constexpr static std::size_t size
Definition: MultivarJson.h:37
ripple::MultivarJson::val
std::array< Json::Value, Size > val
Definition: MultivarJson.h:36
ripple::TxSearched::all
@ all
ripple::RPC::apiBetaVersion
constexpr unsigned int apiBetaVersion
Definition: RPCHelpers.h:239
ripple::test::MultivarJson_test::run
void run() override
Definition: MultivarJson_test.cpp:45
ripple::requires
requires(T::ConsequencesFactory==Transactor::Normal) TxConsequences consequences_helper(PreflightContext const &ctx)
Definition: applySteps.cpp:181
ripple::test::MultivarJson_test
Definition: MultivarJson_test.cpp:34
Json::objectValue
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:43
std::array
STL class.
cstdint
ripple::apiVersionSelector
constexpr auto apiVersionSelector(unsigned int apiVersion) noexcept
Definition: MultivarJson.h:106
ripple::RPC::apiMinimumSupportedVersion
constexpr unsigned int apiMinimumSupportedVersion
Definition: RPCHelpers.h:236
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
limits
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
optional
std::size_t
ripple::MultivarJson
Definition: MultivarJson.h:34
std::numeric_limits
type_traits
ripple::test::MultivarJson_test::makeJson
static auto makeJson(const char *key, int val)
Definition: MultivarJson_test.cpp:37
ripple::TxSearched::some
@ some
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)