aboutsummaryrefslogtreecommitdiff
path: root/tests/unit_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit_tests')
-rw-r--r--tests/unit_tests/CMakeLists.txt2
-rw-r--r--tests/unit_tests/crypto.cpp24
-rw-r--r--tests/unit_tests/epee_utils.cpp16
-rw-r--r--tests/unit_tests/util.cpp50
-rw-r--r--tests/unit_tests/variant.cpp436
5 files changed, 528 insertions, 0 deletions
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index d57c9bd7b..57bd568fc 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -90,6 +90,8 @@ set(unit_tests_sources
hardfork.cpp
unbound.cpp
uri.cpp
+ variant.cpp
+ util.cpp
varint.cpp
ver_rct_non_semantics_simple_cached.cpp
ringct.cpp
diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp
index 4cac0b89f..e41f3955f 100644
--- a/tests/unit_tests/crypto.cpp
+++ b/tests/unit_tests/crypto.cpp
@@ -32,8 +32,15 @@
#include <sstream>
#include <string>
+extern "C"
+{
+#include "crypto/crypto-ops.h"
+}
+#include "crypto/generators.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/merge_mining.h"
+#include "ringct/rctOps.h"
+#include "ringct/rctTypes.h"
namespace
{
@@ -312,3 +319,20 @@ TEST(Crypto, tree_branch)
}
}
}
+
+TEST(Crypto, generator_consistency)
+{
+ // crypto/generators.h
+ const crypto::public_key G{crypto::get_G()};
+ const crypto::public_key H{crypto::get_H()};
+ const ge_p3 H_p3 = crypto::get_H_p3();
+
+ // crypto/crypto-ops.h
+ ASSERT_TRUE(memcmp(&H_p3, &ge_p3_H, sizeof(ge_p3)) == 0);
+
+ // ringct/rctOps.h
+ ASSERT_TRUE(memcmp(G.data, rct::G.bytes, 32) == 0);
+
+ // ringct/rctTypes.h
+ ASSERT_TRUE(memcmp(H.data, rct::H.bytes, 32) == 0);
+}
diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp
index 185cb73b1..c776fbcc9 100644
--- a/tests/unit_tests/epee_utils.cpp
+++ b/tests/unit_tests/epee_utils.cpp
@@ -1241,6 +1241,22 @@ TEST(ToHex, ArrayFromPod)
);
}
+TEST(ToHex, Buffer)
+{
+ static constexpr const std::uint8_t source[] = {0xFF, 0x00, 0xAB, 0x01};
+ const std::vector<char> expected{'f', 'f', '0', '0', 'a', 'b', '0', '1'};
+
+ std::vector<char> buffer;
+ buffer.resize(expected.size());
+ EXPECT_TRUE(epee::to_hex::buffer(epee::to_mut_span(buffer), source));
+ EXPECT_EQ(expected, buffer);
+
+ buffer.pop_back();
+ EXPECT_FALSE(epee::to_hex::buffer(epee::to_mut_span(buffer), source));
+ buffer.pop_back();
+ EXPECT_FALSE(epee::to_hex::buffer(epee::to_mut_span(buffer), source));
+}
+
TEST(ToHex, Ostream)
{
std::stringstream out;
diff --git a/tests/unit_tests/util.cpp b/tests/unit_tests/util.cpp
new file mode 100644
index 000000000..9285d2000
--- /dev/null
+++ b/tests/unit_tests/util.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2023-2023, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "gtest/gtest.h"
+
+#include "common/util.h"
+
+TEST(LocalAddress, localhost) { ASSERT_TRUE(tools::is_local_address("localhost")); }
+TEST(LocalAddress, localhost_port) { ASSERT_TRUE(tools::is_local_address("localhost:18081")); }
+TEST(LocalAddress, localhost_suffix) { ASSERT_TRUE(tools::is_local_address("test.localhost")); }
+TEST(LocalAddress, loopback) { ASSERT_TRUE(tools::is_local_address("127.0.0.1")); }
+TEST(LocalAddress, loopback_port) { ASSERT_TRUE(tools::is_local_address("127.0.0.1:18081")); }
+TEST(LocalAddress, loopback_protocol) { ASSERT_TRUE(tools::is_local_address("http://127.0.0.1")); }
+TEST(LocalAddress, loopback_hi) { ASSERT_TRUE(tools::is_local_address("127.255.255.255")); }
+TEST(LocalAddress, loopback_lo) { ASSERT_TRUE(tools::is_local_address("127.0.0.0")); }
+TEST(LocalAddress, loopback_ipv6) { ASSERT_TRUE(tools::is_local_address("[0:0:0:0:0:0:0:1]")); }
+
+TEST(LocalAddress, onion) { ASSERT_FALSE(tools::is_local_address("vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion")); }
+TEST(LocalAddress, i2p) { ASSERT_FALSE(tools::is_local_address("xmrto2bturnore26xmrto2bturnore26xmrto2bturnore26xmr2.b32.i2p")); }
+TEST(LocalAddress, valid_ip) { ASSERT_FALSE(tools::is_local_address("1.2.3.4")); }
+TEST(LocalAddress, valid_ipv6) { ASSERT_FALSE(tools::is_local_address("[0:0:0:0:0:0:0:2]")); }
+TEST(LocalAddress, valid_domain) { ASSERT_FALSE(tools::is_local_address("getmonero.org")); }
+TEST(LocalAddress, local_prefix) { ASSERT_FALSE(tools::is_local_address("localhost.com")); }
+TEST(LocalAddress, invalid) { ASSERT_FALSE(tools::is_local_address("test")); }
+TEST(LocalAddress, empty) { ASSERT_FALSE(tools::is_local_address("")); }
diff --git a/tests/unit_tests/variant.cpp b/tests/unit_tests/variant.cpp
new file mode 100644
index 000000000..d7ded8e4b
--- /dev/null
+++ b/tests/unit_tests/variant.cpp
@@ -0,0 +1,436 @@
+// Copyright (c) 2023, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "common/variant.h"
+
+#include <boost/mpl/deref.hpp>
+#include <boost/variant/recursive_wrapper.hpp>
+#include <boost/variant/recursive_variant.hpp>
+
+#include "gtest/gtest.h"
+
+#include <sstream>
+#include <type_traits>
+#include <vector>
+
+using tools::variant;
+using tools::variant_static_visitor;
+
+namespace
+{
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template <typename T>
+using strip_all_t = std::remove_reference_t<std::remove_cv_t<T>>;
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template <typename T, typename U>
+using strip_same = std::is_same<strip_all_t<T>, strip_all_t<U>>;
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template
+<
+ typename PositiveType,
+ typename TestType,
+ typename... VariantTypes,
+ class VecTypes = boost::mpl::vector<VariantTypes...>,
+ class VecBegin = typename boost::mpl::begin<VecTypes>::type,
+ class VecIndexT = typename boost::mpl::find<VecTypes, TestType>::type,
+ size_t TYPE_INDEX = boost::mpl::distance<VecBegin, VecIndexT>::value,
+ bool LAST_VARIANT_TYPE = TYPE_INDEX == sizeof...(VariantTypes) - 1
+>
+static std::enable_if_t<LAST_VARIANT_TYPE>
+test_is_type_match(const variant<VariantTypes...>& v)
+{
+ constexpr bool expected = strip_same<PositiveType, TestType>();
+ const bool actual = v.template is_type<TestType>();
+ EXPECT_EQ(expected, actual);
+
+ EXPECT_FALSE(v.template is_type<boost::blank>());
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template
+<
+ typename PositiveType,
+ typename TestType,
+ typename... VariantTypes,
+ class VecTypes = boost::mpl::vector<VariantTypes...>,
+ class VecBegin = typename boost::mpl::begin<VecTypes>::type,
+ class VecIndexT = typename boost::mpl::find<VecTypes, TestType>::type,
+ size_t TYPE_INDEX = boost::mpl::distance<VecBegin, VecIndexT>::value,
+ bool LAST_VARIANT_TYPE = TYPE_INDEX == sizeof...(VariantTypes) - 1
+>
+static std::enable_if_t<!LAST_VARIANT_TYPE>
+test_is_type_match(const variant<VariantTypes...>& v)
+{
+ constexpr bool expected = strip_same<PositiveType, TestType>();
+ const bool actual = v.template is_type<TestType>();
+ EXPECT_EQ(expected, actual);
+
+ using NextTypeIt = typename boost::mpl::advance<VecIndexT, boost::mpl::int_<1>>::type;
+ using NextTestType = typename boost::mpl::deref<NextTypeIt>::type;
+ test_is_type_match<PositiveType, NextTestType>(v);
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template
+<
+ typename VariantType0,
+ typename... VariantTypesRest,
+ typename AssignType
+>
+static void test_is_type_ref
+(
+ variant<VariantType0, VariantTypesRest...>& v,
+ AssignType&& val
+)
+{
+ v = val;
+ test_is_type_match<AssignType, VariantType0>(v);
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template
+<
+ typename VariantType0,
+ typename... VariantTypesRest,
+ typename AssignType0,
+ typename... AssignTypesRest
+>
+static void test_is_type_ref
+(
+ variant<VariantType0, VariantTypesRest...>& v,
+ AssignType0&& val_0,
+ AssignTypesRest&&... val_rest
+)
+{
+ v = val_0;
+ test_is_type_match<AssignType0, VariantType0>(v);
+ test_is_type_ref(v, val_rest...);
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template <typename... VariantTypes>
+static void test_is_type_full(VariantTypes&&... test_vals)
+{
+ variant<VariantTypes...> v;
+ test_is_type_ref(v, test_vals...);
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template
+<
+ size_t IJ = 0,
+ typename... VariantTypes,
+ bool END = IJ == sizeof...(VariantTypes) * sizeof...(VariantTypes)
+>
+static std::enable_if_t<END>
+test_same_type_ref
+(
+ variant<VariantTypes...>& v1,
+ variant<VariantTypes...>& v2,
+ const std::tuple<VariantTypes...>& tup_i,
+ const std::tuple<VariantTypes...>& tup_j
+)
+{ /* trivial end case */ }
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template
+<
+ size_t IJ = 0,
+ typename... VariantTypes,
+ bool END = IJ == sizeof...(VariantTypes) * sizeof...(VariantTypes)
+>
+static std::enable_if_t<!END>
+test_same_type_ref
+(
+ variant<VariantTypes...>& v1,
+ variant<VariantTypes...>& v2,
+ const std::tuple<VariantTypes...>& tup_i,
+ const std::tuple<VariantTypes...>& tup_j
+)
+{
+ constexpr size_t I = IJ / sizeof...(VariantTypes);
+ constexpr size_t J = IJ % sizeof...(VariantTypes);
+ constexpr bool expected = I == J;
+
+ v1 = std::get<I>(tup_i);
+ v2 = std::get<J>(tup_j);
+ const bool actual = variant<VariantTypes...>::same_type(v1, v2);
+
+ EXPECT_EQ(expected, actual);
+
+ test_same_type_ref<IJ + 1>(v1, v2, tup_i, tup_j);
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+template <typename... VariantTypes>
+static void test_same_type_full
+(
+ const std::tuple<VariantTypes...>& vals_i,
+ const std::tuple<VariantTypes...>& vals_j
+)
+{
+ using Variant = variant<VariantTypes...>;
+ Variant v_i;
+ Variant v_j;
+ test_same_type_ref(v_i, v_j, vals_i, vals_j);
+}
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+struct test_stringify_visitor: public variant_static_visitor<std::string>
+{
+ template <typename T>
+ static std::string stringify(const T& t)
+ {
+ std::stringstream ss;
+ ss << typeid(T).name();
+ ss << "::";
+ ss << t;
+ return ss.str();
+ }
+
+ template <class Variant, typename T>
+ static void test_visitation(const Variant& v, const T& t)
+ {
+ EXPECT_EQ(test_stringify_visitor::stringify(t), v.visit(test_stringify_visitor()));
+ }
+
+ // Make sure boost::blank errors
+ using variant_static_visitor::operator();
+
+ // Visitation implementation
+ template <typename T>
+ std::string operator()(const T& t) const
+ {
+ return test_stringify_visitor::stringify(t);
+ }
+};
+//-------------------------------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------------------------------
+} // anonymous namespace
+
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, operatorbool)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_FALSE(v);
+ v = (int16_t) 2023;
+ EXPECT_TRUE(v);
+ v = (int16_t) 0;
+ EXPECT_TRUE(v);
+ v = boost::blank{};
+ EXPECT_FALSE(v);
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, is_empty)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_TRUE(v.is_empty());
+ v = (int16_t) 2023;
+ EXPECT_FALSE(v.is_empty());
+ v = (int16_t) 0;
+ EXPECT_FALSE(v.is_empty());
+ v = boost::blank{};
+ EXPECT_TRUE(v.is_empty());
+
+ variant<> v2;
+ EXPECT_TRUE(v2.is_empty());
+ v2 = boost::blank{};
+ EXPECT_TRUE(v2.is_empty());
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, is_type)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_TRUE(v.is_type<boost::blank>());
+ v = (int16_t) 2023;
+ EXPECT_TRUE(v.is_type<int16_t>());
+
+ test_is_type_full((uint32_t) 2023, (char) '\n', std::string("HOWDY"));
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, try_unwrap)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_FALSE(v.try_unwrap<int8_t>());
+ v = (int16_t) 5252;
+ ASSERT_TRUE(v.try_unwrap<int16_t>());
+ EXPECT_EQ(5252, *v.try_unwrap<int16_t>());
+ EXPECT_FALSE(v.try_unwrap<uint16_t>());
+ EXPECT_FALSE(v.try_unwrap<std::string>());
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, unwrap)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_THROW(v.unwrap<int8_t>(), std::runtime_error);
+ v = (int16_t) 5252;
+ EXPECT_EQ(5252, v.unwrap<int16_t>());
+ EXPECT_THROW(v.unwrap<uint16_t>(), std::runtime_error);
+ EXPECT_THROW(v.unwrap<std::string>(), std::runtime_error);
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, mutation)
+{
+ variant<uint8_t> v;
+ v = (uint8_t) 5;
+ EXPECT_EQ(5, v.unwrap<uint8_t>());
+ uint8_t &intref{v.unwrap<uint8_t>()};
+ intref = 10;
+ EXPECT_EQ(10, v.unwrap<uint8_t>());
+ EXPECT_TRUE(v.try_unwrap<uint8_t>());
+ uint8_t *intptr{v.try_unwrap<uint8_t>()};
+ *intptr = 15;
+ EXPECT_EQ(15, v.unwrap<uint8_t>());
+
+ const variant<uint8_t> &v_ref{v};
+ EXPECT_EQ(15, v_ref.unwrap<uint8_t>());
+ EXPECT_TRUE(v_ref.try_unwrap<uint8_t>());
+ EXPECT_EQ(15, *(v_ref.try_unwrap<uint8_t>()));
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, index)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_EQ(0, v.index());
+ v = (int8_t) 7;
+ EXPECT_EQ(1, v.index());
+ v = (uint8_t) 7;
+ EXPECT_EQ(2, v.index());
+ v = (int16_t) 7;
+ EXPECT_EQ(3, v.index());
+ v = (uint16_t) 7;
+ EXPECT_EQ(4, v.index());
+ v = "verifiable variant vying for vengence versus visa";
+ EXPECT_EQ(5, v.index());
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, type_index_of)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_EQ(0, decltype(v)::type_index_of<boost::blank>());
+ EXPECT_EQ(1, decltype(v)::type_index_of<int8_t>());
+ EXPECT_EQ(2, decltype(v)::type_index_of<uint8_t>());
+ EXPECT_EQ(3, decltype(v)::type_index_of<int16_t>());
+ EXPECT_EQ(4, decltype(v)::type_index_of<uint16_t>());
+ EXPECT_EQ(5, decltype(v)::type_index_of<std::string>());
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, constexpr_type_index_of)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ constexpr int TINDEX0 = decltype(v)::type_index_of<boost::blank>();
+ EXPECT_EQ(0, TINDEX0);
+ constexpr int TINDEX5 = decltype(v)::type_index_of<std::string>();
+ EXPECT_EQ(5, TINDEX5);
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, same_type)
+{
+ const std::tuple<int, std::string, char> vals_i(77840, "Hullubaloo", '\0');
+ const std::tuple<int, std::string, char> vals_j(1876, "Canneck", '\t');
+ test_same_type_full(vals_i, vals_j);
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, visit)
+{
+ variant<int8_t, uint8_t, int16_t, uint16_t, std::string> v;
+ EXPECT_THROW(v.visit(test_stringify_visitor()), std::runtime_error);
+
+ v = "Rev";
+ test_stringify_visitor::test_visitation(v, std::string("Rev"));
+
+ v = (int16_t) 2001;
+ test_stringify_visitor::test_visitation(v, (int16_t) 2001);
+ EXPECT_NE(test_stringify_visitor::stringify((uint16_t) 2001), v.visit(test_stringify_visitor()));
+}
+//-------------------------------------------------------------------------------------------------------------------
+TEST(variant, ad_hoc_recursion)
+{
+ struct left_t;
+ struct right_t;
+
+ using twisty = variant<boost::recursive_wrapper<left_t>, boost::recursive_wrapper<right_t>>;
+
+ struct left_t
+ {
+ twisty l;
+ };
+
+ struct right_t
+ {
+ twisty r;
+ };
+
+ auto right = [](twisty&& t = {}) -> twisty
+ {
+ right_t r;
+ r.r = t;
+ return r;
+ };
+
+ auto left = [](twisty&& t = {}) -> twisty
+ {
+ left_t l;
+ l.l = t;
+ return l;
+ };
+
+ struct twisty_counter: variant_static_visitor<std::pair<int, int>>
+ {
+ std::pair<int, int> operator()(boost::blank) const
+ {
+ return {0, 0};
+ }
+
+ std::pair<int, int> operator()(const left_t& l) const
+ {
+ auto count = l.l.visit(twisty_counter());
+ count.first += 1;
+ return count;
+ }
+
+ std::pair<int, int> operator()(const right_t& r) const
+ {
+ auto count = r.r.visit(twisty_counter());
+ count.second += 1;
+ return count;
+ }
+ };
+
+ const twisty tw = left(left(right(right(left(right(left(right(left()))))))));
+
+ int left_count, right_count;
+ std::tie(left_count, right_count) = tw.visit(twisty_counter());
+
+ EXPECT_EQ(5, left_count);
+ EXPECT_EQ(4, right_count);
+}
+//-------------------------------------------------------------------------------------------------------------------