From d7273ec070786b2eadbde50dad77a2b86be5c890 Mon Sep 17 00:00:00 2001 From: SlowRiot Date: Wed, 7 Dec 2022 11:10:11 +0000 Subject: [PATCH] Tests for C++20 work-alike Span Adds a test suite for the Span class, testing the following features: - Creation - Random access - Popping front and back - Assert failure on attempting to pop an empty span - Equality - Comparison operators - Subspan - Iteration - Corner cases - empty span - very large span Additionally fixes order of files in test CMakeLists. Resolves #397. Test plan --------- Quick version: - `ninja check-bitcoin-span_tests` Or with detailed output: - `ninja test_bitcoin` - `src/test/test_bitcoin --log_level=all --run_test=span_tests -- DEBUG_LOG_OUT | fgrep -v 'is skipped because disabled'` --- src/test/CMakeLists.txt | 3 +- src/test/span_tests.cpp | 191 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 src/test/span_tests.cpp diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index a22d34de32..2c53ea778c 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -188,8 +188,8 @@ add_boost_unit_tests_to_suite(bitcoin test_bitcoin flatfile_tests.cpp gbtlight_tests.cpp getarg_tests.cpp - heapoptional_tests.cpp hash_tests.cpp + heapoptional_tests.cpp inv_tests.cpp key_io_tests.cpp key_tests.cpp @@ -232,6 +232,7 @@ add_boost_unit_tests_to_suite(bitcoin test_bitcoin sighash_tests.cpp sighashtype_tests.cpp skiplist_tests.cpp + span_tests.cpp streams_tests.cpp sync_tests.cpp testlib_tests.cpp diff --git a/src/test/span_tests.cpp b/src/test/span_tests.cpp new file mode 100644 index 0000000000..4552b6c97e --- /dev/null +++ b/src/test/span_tests.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2022 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(span_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(span_from_vector) { + const std::vector vec{2, 3, 1}; + + auto span = Span(vec); + BOOST_CHECK_EQUAL(span.size(), 3); + BOOST_CHECK_EQUAL(span.front(), 2); + BOOST_CHECK_EQUAL(span.back(), 1); + BOOST_CHECK_EQUAL(span[1], 3); + + BOOST_CHECK_EQUAL(&vec[1], &span[1]); +} + +BOOST_AUTO_TEST_CASE(span_from_c_array) { + uint8_t array[]{5, 6, 4}; + + auto span = Span(array); + BOOST_CHECK_EQUAL(span.size(), 3); + BOOST_CHECK_EQUAL(span.front(), 5); + BOOST_CHECK_EQUAL(span.back(), 4); + BOOST_CHECK_EQUAL(span[1], 6); + + BOOST_CHECK_EQUAL(&array[1], &span[1]); +} + +BOOST_AUTO_TEST_CASE(span_pop) { + const std::vector vec{7, 6, 5, 4, 3, 2, 1}; + + auto span = Span(vec); + + BOOST_CHECK_EQUAL(span.pop_front(), 7); + BOOST_CHECK_EQUAL(span.size(), 6); + BOOST_CHECK_EQUAL(span.front(), 6); + BOOST_CHECK_EQUAL(span.back(), 1); + + BOOST_CHECK_EQUAL(span.pop_back(), 1); + BOOST_CHECK_EQUAL(span.size(), 5); + BOOST_CHECK_EQUAL(span.front(), 6); + BOOST_CHECK_EQUAL(span.back(), 2); + + BOOST_CHECK_EQUAL(span.pop_back(), 2); + BOOST_CHECK_EQUAL(span.size(), 4); + + BOOST_CHECK_EQUAL(span.pop_back(), 3); + BOOST_CHECK_EQUAL(span.size(), 3); + + BOOST_CHECK_EQUAL(span.pop_front(), 6); + BOOST_CHECK_EQUAL(span.size(), 2); + + BOOST_CHECK_EQUAL(span.pop_back(), 4); + BOOST_CHECK_EQUAL(span.size(), 1); + BOOST_CHECK_EQUAL(span.front(), 5); + BOOST_CHECK_EQUAL(span.front(), span.back()); + + BOOST_CHECK_EQUAL(span.pop_back(), 5); + BOOST_CHECK_EQUAL(span.size(), 0); + BOOST_CHECK(span.empty()); + + // popping an empty span should fail an assert + BOOST_CHECK(CheckAssert([&]{span.pop_back();}) == CheckAssertResult::AssertEncountered); + BOOST_CHECK(CheckAssert([&]{span.pop_front();}) == CheckAssertResult::AssertEncountered); + BOOST_CHECK_EQUAL(span.size(), 0); +} + +BOOST_AUTO_TEST_CASE(span_compare) { + { + // test equality operators + std::vector vec{3, 2, 1}; + uint8_t array[]{3, 2, 1}; + + auto span1 = Span(vec); + auto span2 = Span(array); + + BOOST_CHECK(span1 == span2); + BOOST_CHECK_EQUAL(span1 != span2, false); + + span1[1] = 123; + BOOST_CHECK(span1 != span2); + BOOST_CHECK_EQUAL(span1 == span2, false); + + span2[1] = 123; + BOOST_CHECK(span1 == span2); + BOOST_CHECK_EQUAL(span1 != span2, false); + } + { + // test comparison operators + std::vector vec1{1, 2, 3, 4, 5, 6}; + std::vector vec2{5, 5, 5, 5, 5, 5}; + + auto span1 = Span(vec1); + auto span2 = Span(vec2); + + BOOST_CHECK(span1 < span2); + BOOST_CHECK(!(span1 > span2)); + BOOST_CHECK(span1 <= span2); + BOOST_CHECK(!(span1 >= span2)); + + vec2 = {0, 0, 0, 0, 0, 1}; + span2 = Span(vec2); + + BOOST_CHECK(!(span1 < span2)); + BOOST_CHECK(span1 > span2); + BOOST_CHECK(!(span1 <= span2)); + BOOST_CHECK(span1 >= span2); + + vec2 = {6, 5, 4, 3, 2, 1}; + span2 = Span(vec2); + + BOOST_CHECK(span1 < span2); + BOOST_CHECK(!(span1 > span2)); + BOOST_CHECK(span1 <= span2); + BOOST_CHECK(!(span1 >= span2)); + } +} + +BOOST_AUTO_TEST_CASE(span_subspan) { + const std::vector vec1{7, 6, 5, 4, 3, 2, 1}; + const std::vector vec2{ 6, 5, 4, 3 }; + + auto span1 = Span(vec1); + auto span2 = Span(vec2); + auto span3 = span1.subspan(1, 4); + + BOOST_CHECK_EQUAL(span1.size(), 7); + BOOST_CHECK_EQUAL(span2.size(), 4); + BOOST_CHECK_EQUAL(span3.size(), 4); + + BOOST_CHECK(span1 != span2); + BOOST_CHECK(span1 != span3); + BOOST_CHECK(span3 == span1.subspan(1, 4)); + BOOST_CHECK(span3 == span2); + + BOOST_CHECK(span1.subspan(0, 0).empty()); + + BOOST_CHECK_EQUAL(span1.pop_front(), 7); + BOOST_CHECK(span1.first(4) == span2); + BOOST_CHECK(span1.first(4).last(3) == span2.last(3)); + BOOST_CHECK_EQUAL(span1.pop_back(), 1); + BOOST_CHECK_EQUAL(span1.pop_back(), 2); + BOOST_CHECK(span1 == span2); + BOOST_CHECK(span1 == span3); + + // subspan out of bounds is UB as per std::span, so we don't test for that +} + +BOOST_AUTO_TEST_CASE(span_iteration) { + const std::vector vec{7, 6, 5, 4, 3, 2, 1}; + auto span = Span(vec); + + uint8_t val = 7; + for (auto const &it : span) { + BOOST_CHECK_EQUAL(it, val--); + } +} + +BOOST_AUTO_TEST_CASE(span_corner_cases) { + { + // test empty span + const std::vector vec; + + auto span = Span(vec); + BOOST_CHECK_EQUAL(span.size(), 0); + BOOST_CHECK(span.empty()); + } + + { + // test large span + const std::vector vec(1024 * 1024, 123); + + auto span = Span(vec); + BOOST_CHECK(!span.empty()); + BOOST_CHECK_EQUAL(span.size(), 1024 * 1024); + BOOST_CHECK_EQUAL(span[1023], 123); + } +} + +BOOST_AUTO_TEST_SUITE_END()