// Copyright 2020 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include #include #include #include #include #include "gtest/gtest.h" #include "pw_kvs/internal/span_traits.h" namespace pw::kvs { namespace { using internal::make_span; using std::byte; using std::dynamic_extent; using std::span; // Test that the ConvertsToSpan trait correctly idenitifies types that convert // to std::span. // Basic types should not convert to span. struct Foo {}; static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); // Arrays without an extent are just pointers -- these should not convert. static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); static_assert(!ConvertsToSpan()); // C arrays convert to span. static_assert(ConvertsToSpan>()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); // Container types convert to span. struct FakeContainer { const char* data() const { return nullptr; } size_t size() const { return 0; } }; static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); static_assert(ConvertsToSpan()); // Spans should also convert to span. static_assert(ConvertsToSpan>()); static_assert(ConvertsToSpan>()); static_assert(ConvertsToSpan>()); static_assert(ConvertsToSpan&&>()); static_assert(ConvertsToSpan&>()); static_assert(ConvertsToSpan&&>()); // These tests for the make_span function were copied from Chromium: // https://chromium.googlesource.com/chromium/src/+/master/base/containers/span_unittest.cc TEST(SpanTest, MakeSpanFromDataAndSize) { int* nullint = nullptr; auto empty_span = make_span(nullint, 0); EXPECT_TRUE(empty_span.empty()); EXPECT_EQ(nullptr, empty_span.data()); std::vector vector = {1, 1, 2, 3, 5, 8}; span expected_span(vector.data(), vector.size()); auto made_span = make_span(vector.data(), vector.size()); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == dynamic_extent, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } TEST(SpanTest, MakeSpanFromPointerPair) { int* nullint = nullptr; auto empty_span = make_span(nullint, nullint); EXPECT_TRUE(empty_span.empty()); EXPECT_EQ(nullptr, empty_span.data()); std::vector vector = {1, 1, 2, 3, 5, 8}; span expected_span(vector.data(), vector.size()); auto made_span = make_span(vector.data(), vector.data() + vector.size()); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == dynamic_extent, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } TEST(SpanTest, MakeSpanFromConstexprArray) { static constexpr int kArray[] = {1, 2, 3, 4, 5}; constexpr span expected_span(kArray); constexpr auto made_span = make_span(kArray); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == 5, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } TEST(SpanTest, MakeSpanFromStdArray) { const std::array kArray = {{1, 2, 3, 4, 5}}; span expected_span(kArray); auto made_span = make_span(kArray); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == 5, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } TEST(SpanTest, MakeSpanFromConstContainer) { const std::vector vector = {-1, -2, -3, -4, -5}; span expected_span(vector); auto made_span = make_span(vector); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == dynamic_extent, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } #if 0 // Not currently working with fixed extent spans. TEST(SpanTest, MakeStaticSpanFromConstContainer) { const std::vector vector = {-1, -2, -3, -4, -5}; span expected_span(vector.data(), vector.size()); auto made_span = make_span<5>(vector); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == 5, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } #endif // 0 TEST(SpanTest, MakeSpanFromContainer) { std::vector vector = {-1, -2, -3, -4, -5}; span expected_span(vector); auto made_span = make_span(vector); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == dynamic_extent, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } #if 0 // Not currently working with fixed extent spans. TEST(SpanTest, MakeStaticSpanFromContainer) { std::vector vector = {-1, -2, -3, -4, -5}; span expected_span(vector.data(), vector.size()); auto made_span = make_span<5>(vector); EXPECT_EQ(expected_span.data(), make_span<5>(vector).data()); EXPECT_EQ(expected_span.size(), make_span<5>(vector).size()); static_assert(decltype(make_span<5>(vector))::extent == 5, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } TEST(SpanTest, MakeStaticSpanFromConstexprContainer) { constexpr StringPiece str = "Hello, World"; constexpr auto made_span = make_span<12>(str); static_assert(str.data() == made_span.data(), "Error: data() does not match"); static_assert(str.size() == made_span.size(), "Error: size() does not match"); static_assert(std::is_same::value, "Error: value_type does not match"); static_assert(str.size() == decltype(made_span)::extent, "Error: extent does not match"); } #endif // 0 TEST(SpanTest, MakeSpanFromRValueContainer) { std::vector vector = {-1, -2, -3, -4, -5}; span expected_span(vector); // Note: While static_cast(foo) is effectively just a fancy spelling of // std::move(foo), make_span does not actually take ownership of the passed in // container. Writing it this way makes it more obvious that we simply care // about the right behavour when passing rvalues. auto made_span = make_span(static_cast&&>(vector)); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == dynamic_extent, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } #if 0 // Not currently working with fixed extent spans. TEST(SpanTest, MakeStaticSpanFromRValueContainer) { std::vector vector = {-1, -2, -3, -4, -5}; span expected_span(vector.data(), vector.size()); // Note: While static_cast(foo) is effectively just a fancy spelling of // std::move(foo), make_span does not actually take ownership of the passed in // container. Writing it this way makes it more obvious that we simply care // about the right behavour when passing rvalues. auto made_span = make_span<5>(static_cast&&>(vector)); EXPECT_EQ(expected_span.data(), made_span.data()); EXPECT_EQ(expected_span.size(), made_span.size()); static_assert(decltype(made_span)::extent == 5, ""); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } #endif // 0 TEST(SpanTest, MakeSpanFromDynamicSpan) { static constexpr int kArray[] = {1, 2, 3, 4, 5}; constexpr span expected_span(kArray); constexpr auto made_span = make_span(expected_span); static_assert(std::is_same::value, "make_span(span) should have the same element_type as span"); static_assert(expected_span.data() == made_span.data(), "make_span(span) should have the same data() as span"); static_assert(expected_span.size() == made_span.size(), "make_span(span) should have the same size() as span"); static_assert(decltype(made_span)::extent == decltype(expected_span)::extent, "make_span(span) should have the same extent as span"); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } TEST(SpanTest, MakeSpanFromStaticSpan) { static constexpr int kArray[] = {1, 2, 3, 4, 5}; constexpr span expected_span(kArray); constexpr auto made_span = make_span(expected_span); static_assert(std::is_same::value, "make_span(span) should have the same element_type as span"); static_assert(expected_span.data() == made_span.data(), "make_span(span) should have the same data() as span"); static_assert(expected_span.size() == made_span.size(), "make_span(span) should have the same size() as span"); static_assert(decltype(made_span)::extent == decltype(expected_span)::extent, "make_span(span) should have the same extent as span"); static_assert( std::is_same::value, "the type of made_span differs from expected_span!"); } } // namespace } // namespace pw::kvs