// Copyright Kevlin Henney, 2000, 2001. All rights reserved. // Copyright Antony Polukhin, 2013-2019. // Copyright Ruslan Arutyunyan, 2019-2021. // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_ANY_TEST_BASIC_TEST_HPP #define BOOST_ANY_TEST_BASIC_TEST_HPP // what: unit tests for variant type boost::any // who: contributed by Kevlin Henney // when: July 2001, 2013, 2014 // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.95 #include #include #include #include #include #include "test.hpp" namespace any_tests { struct huge_structure { char take_place[1024]; std::string text; }; template struct basic_tests // test definitions { struct copy_counter { public: copy_counter() {} copy_counter(const copy_counter&) { ++count; } copy_counter& operator=(const copy_counter&) { ++count; return *this; } static int get_count() { return count; } private: static int count; }; static void test_default_ctor() { const Any value; check_true(value.empty(), "empty"); check_null(boost::any_cast(&value), "any_cast"); check_equal(value.type(), boost::typeindex::type_id(), "type"); } static void test_converting_ctor() { std::string text = "test message"; Any value = text; check_false(value.empty(), "empty"); check_equal(value.type(), boost::typeindex::type_id(), "type"); check_null(boost::any_cast(&value), "any_cast"); check_non_null(boost::any_cast(&value), "any_cast"); check_equal( boost::any_cast(value), text, "comparing cast copy against original text"); check_unequal( boost::any_cast(&value), &text, "comparing address in copy against original text"); } static void test_copy_ctor() { std::string text = "test message"; Any original = text, copy = original; check_false(copy.empty(), "empty"); check_equal(boost::typeindex::type_index(original.type()), copy.type(), "type"); check_equal( boost::any_cast(original), boost::any_cast(copy), "comparing cast copy against original"); check_equal( text, boost::any_cast(copy), "comparing cast copy against original text"); check_unequal( boost::any_cast(&original), boost::any_cast(©), "comparing address in copy against original"); } static void test_copy_assign() { std::string text = "test message"; Any original = text, copy; Any * assign_result = &(copy = original); check_false(copy.empty(), "empty"); check_equal(boost::typeindex::type_index(original.type()), copy.type(), "type"); check_equal( boost::any_cast(original), boost::any_cast(copy), "comparing cast copy against cast original"); check_equal( text, boost::any_cast(copy), "comparing cast copy against original text"); check_unequal( boost::any_cast(&original), boost::any_cast(©), "comparing address in copy against original"); check_equal(assign_result, ©, "address of assignment result"); } static void test_converting_assign() { std::string text = "test message"; Any value; Any * assign_result = &(value = text); check_false(value.empty(), "type"); check_equal(value.type(), boost::typeindex::type_id(), "type"); check_null(boost::any_cast(&value), "any_cast"); check_non_null(boost::any_cast(&value), "any_cast"); check_equal( boost::any_cast(value), text, "comparing cast copy against original text"); check_unequal( boost::any_cast(&value), &text, "comparing address in copy against original text"); check_equal(assign_result, &value, "address of assignment result"); } static void test_bad_cast() { std::string text = "test message"; Any value = text; TEST_CHECK_THROW( boost::any_cast(value), boost::bad_any_cast, "any_cast to incorrect type"); } static void test_swap() { huge_structure stored; stored.text = "test message"; Any original = stored; Any swapped; huge_structure * original_ptr = boost::any_cast(&original); Any * swap_result = &original.swap(swapped); check_true(original.empty(), "empty on original"); check_false(swapped.empty(), "empty on swapped"); check_equal(swapped.type(), boost::typeindex::type_id(), "type"); check_equal( stored.text, boost::any_cast(swapped).text, "comparing swapped copy against original text"); check_non_null(original_ptr, "address in pre-swapped original"); check_equal( original_ptr, boost::any_cast(&swapped), "comparing address in swapped against original"); check_equal(swap_result, &original, "address of swap result"); swap(swapped, swapped); check_false(swapped.empty(), "empty on self swap"); check_equal( swapped.type(), boost::typeindex::type_id(), "type missmatch on self swap"); check_equal( stored.text, boost::any_cast(swapped).text, "comparing against original text on self swap"); Any copy1 = copy_counter(); Any copy2 = copy_counter(); int count = copy_counter::get_count(); swap(copy1, copy2); check_equal(count, copy_counter::get_count(), "checking that free swap doesn't make any copies."); Any any_char = '1'; swap(any_char, swapped); check_equal( stored.text, boost::any_cast(any_char).text, "comparing against original text on swap with small type"); check_equal( swapped.type(), boost::typeindex::type_id(), "comparing type on swap with small type"); check_equal( '1', boost::any_cast(swapped), "comparing small type swapped value"); } static void test_null_copying() { const Any null; Any copied = null, assigned; assigned = null; check_true(null.empty(), "empty on null"); check_true(copied.empty(), "empty on copied"); check_true(assigned.empty(), "empty on copied"); } static void test_cast_to_reference() { Any a(137); const Any b(a); int & ra = boost::any_cast(a); int const & ra_c = boost::any_cast(a); int volatile & ra_v = boost::any_cast(a); int const volatile & ra_cv = boost::any_cast(a); check_true( &ra == &ra_c && &ra == &ra_v && &ra == &ra_cv, "cv references to same obj"); int const & rb_c = boost::any_cast(b); int const volatile & rb_cv = boost::any_cast(b); check_true(&rb_c == &rb_cv, "cv references to copied const obj"); check_true(&ra != &rb_c, "copies hold different objects"); ++ra; int incremented = boost::any_cast(a); check_true(incremented == 138, "increment by reference changes value"); TEST_CHECK_THROW( boost::any_cast(a), boost::bad_any_cast, "any_cast to incorrect reference type"); TEST_CHECK_THROW( boost::any_cast(b), boost::bad_any_cast, "any_cast to incorrect const reference type"); } static void test_bad_any_cast() { check_true( boost::is_base_and_derived::value, "bad_any_cast base class check" ); check_true( std::string(boost::bad_any_cast().what()).find("any") != std::string::npos, "bad_any_cast notes any in excaption" ); } static void test_with_array() { Any value1("Char array"); Any value2; value2 = "Char array"; check_false(value1.empty(), "type"); check_false(value2.empty(), "type"); check_equal(value1.type(), boost::typeindex::type_id(), "type"); check_equal(value2.type(), boost::typeindex::type_id(), "type"); check_non_null(boost::any_cast(&value1), "any_cast"); check_non_null(boost::any_cast(&value2), "any_cast"); } static void test_clear() { std::string text = "test message"; Any value = text; check_false(value.empty(), "empty"); value.clear(); check_true(value.empty(), "non-empty after clear"); value.clear(); check_true(value.empty(), "non-empty after second clear"); value = text; check_false(value.empty(), "empty"); value.clear(); check_true(value.empty(), "non-empty after clear"); } // Following tests cover the case from #9462 // https://svn.boost.org/trac/boost/ticket/9462 static Any makeVec() { return std::vector(100 /*size*/, 7 /*value*/); } static void test_vectors() { const std::vector& vec = boost::any_cast >(makeVec()); check_equal(vec.size(), 100u, "size of vector extracted from boost::any"); check_equal(vec.back(), 7, "back value of vector extracted from boost::any"); check_equal(vec.front(), 7, "front value of vector extracted from boost::any"); std::vector vec1 = boost::any_cast >(makeVec()); check_equal(vec1.size(), 100u, "size of second vector extracted from boost::any"); check_equal(vec1.back(), 7, "back value of second vector extracted from boost::any"); check_equal(vec1.front(), 7, "front value of second vector extracted from boost::any"); } template class class_with_address_op { public: class_with_address_op(const T* p) : ptr(p) {} const T** operator &() { return &ptr; } const T* get() const { return ptr; } private: const T* ptr; }; static void test_addressof() { int val = 10; const int* ptr = &val; class_with_address_op obj(ptr); Any test_val(obj); class_with_address_op returned_obj = boost::any_cast >(test_val); check_equal(&val, returned_obj.get(), "any_cast incorrectly works with type that has operator&(): addresses differ"); check_true(!!boost::any_cast >(&test_val), "any_cast incorrectly works with type that has operator&()"); check_equal(boost::unsafe_any_cast >(&test_val)->get(), ptr, "unsafe_any_cast incorrectly works with type that has operator&()"); } static void test_multiple_assign() { Any test_val = 10; check_true(!!boost::any_cast(&test_val), "any_cast"); test_val = '0'; check_true(!!boost::any_cast(&test_val), "any_cast"); test_val = huge_structure(); check_true(!!boost::any_cast(&test_val), "any_cast"); test_val = '0'; check_true(!!boost::any_cast(&test_val), "any_cast"); test_val = Any(huge_structure()); check_true(!!boost::any_cast(&test_val), "any_cast"); } static int run_tests() { typedef test test_case; const test_case test_cases[] = { { "default construction", test_default_ctor }, { "single argument construction", test_converting_ctor }, { "copy construction", test_copy_ctor }, { "copy assignment operator", test_copy_assign }, { "converting assignment operator", test_converting_assign }, { "failed custom keyword cast", test_bad_cast }, { "swap member function", test_swap }, { "copying operations on a null", test_null_copying }, { "cast to reference types", test_cast_to_reference }, { "bad_any_cast exception", test_bad_any_cast }, { "storing an array inside", test_with_array }, { "clear() methods", test_clear }, { "testing with vectors", test_vectors }, { "class with operator&()", test_addressof }, { "multiple assignments", test_multiple_assign }, }; typedef const test_case * test_case_iterator; tester test_suite(test_cases, test_cases + sizeof(test_cases) / sizeof(test_cases[0])); return test_suite() ? EXIT_SUCCESS : EXIT_FAILURE; } }; template int basic_tests::copy_counter::count = 0; } #endif