| // 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 <array> |
| #include <cstddef> |
| #include <span> |
| #include <string_view> |
| #include <vector> |
| |
| #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<Foo>()); |
| static_assert(!ConvertsToSpan<int>()); |
| static_assert(!ConvertsToSpan<void>()); |
| static_assert(!ConvertsToSpan<byte>()); |
| static_assert(!ConvertsToSpan<byte*>()); |
| |
| // Arrays without an extent are just pointers -- these should not convert. |
| static_assert(!ConvertsToSpan<bool[]>()); |
| static_assert(!ConvertsToSpan<const int[]>()); |
| static_assert(!ConvertsToSpan<bool (&)[]>()); |
| static_assert(!ConvertsToSpan<const int (&)[]>()); |
| static_assert(!ConvertsToSpan<bool(&&)[]>()); |
| static_assert(!ConvertsToSpan<const int(&&)[]>()); |
| |
| // C arrays convert to span. |
| static_assert(ConvertsToSpan<std::array<int, 5>>()); |
| static_assert(ConvertsToSpan<decltype("Hello!")>()); |
| |
| static_assert(ConvertsToSpan<bool[1]>()); |
| static_assert(ConvertsToSpan<char[35]>()); |
| static_assert(ConvertsToSpan<const int[35]>()); |
| |
| static_assert(ConvertsToSpan<bool (&)[1]>()); |
| static_assert(ConvertsToSpan<char (&)[35]>()); |
| static_assert(ConvertsToSpan<const int (&)[35]>()); |
| |
| static_assert(ConvertsToSpan<bool(&&)[1]>()); |
| static_assert(ConvertsToSpan<bool(&&)[1]>()); |
| static_assert(ConvertsToSpan<char(&&)[35]>()); |
| static_assert(ConvertsToSpan<const int(&&)[35]>()); |
| |
| // Container types convert to span. |
| struct FakeContainer { |
| const char* data() const { return nullptr; } |
| size_t size() const { return 0; } |
| }; |
| |
| static_assert(ConvertsToSpan<FakeContainer>()); |
| static_assert(ConvertsToSpan<FakeContainer&>()); |
| static_assert(ConvertsToSpan<FakeContainer&&>()); |
| static_assert(ConvertsToSpan<const FakeContainer>()); |
| static_assert(ConvertsToSpan<const FakeContainer&>()); |
| static_assert(ConvertsToSpan<const FakeContainer&&>()); |
| |
| static_assert(ConvertsToSpan<std::string_view>()); |
| static_assert(ConvertsToSpan<std::string_view&>()); |
| static_assert(ConvertsToSpan<std::string_view&&>()); |
| |
| static_assert(ConvertsToSpan<const std::string_view>()); |
| static_assert(ConvertsToSpan<const std::string_view&>()); |
| static_assert(ConvertsToSpan<const std::string_view&&>()); |
| |
| // Spans should also convert to span. |
| static_assert(ConvertsToSpan<std::span<int>>()); |
| static_assert(ConvertsToSpan<std::span<byte>>()); |
| static_assert(ConvertsToSpan<std::span<const int*>>()); |
| static_assert(ConvertsToSpan<std::span<bool>&&>()); |
| static_assert(ConvertsToSpan<const std::span<bool>&>()); |
| static_assert(ConvertsToSpan<std::span<bool>&&>()); |
| |
| // These tests for the make_span function were copied from Chromium: |
| // https://chromium.googlesource.com/chromium/src/+/main/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<int> vector = {1, 1, 2, 3, 5, 8}; |
| span<int> 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<decltype(expected_span), decltype(made_span)>::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<int> vector = {1, 1, 2, 3, 5, 8}; |
| span<int> 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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| TEST(SpanTest, MakeSpanFromConstexprArray) { |
| static constexpr int kArray[] = {1, 2, 3, 4, 5}; |
| constexpr span<const int, 5> 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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| TEST(SpanTest, MakeSpanFromStdArray) { |
| const std::array<int, 5> kArray = {{1, 2, 3, 4, 5}}; |
| span<const int, 5> 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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| TEST(SpanTest, MakeSpanFromConstContainer) { |
| const std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| span<const int> 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<decltype(expected_span), decltype(made_span)>::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<int> vector = {-1, -2, -3, -4, -5}; |
| span<const int, 5> 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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| #endif // 0 |
| |
| TEST(SpanTest, MakeSpanFromContainer) { |
| std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| span<int> 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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| #if 0 // Not currently working with fixed extent spans. |
| |
| TEST(SpanTest, MakeStaticSpanFromContainer) { |
| std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| span<int, 5> 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<decltype(expected_span), decltype(made_span)>::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<decltype(str)::value_type, |
| decltype(made_span)::value_type>::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<int> vector = {-1, -2, -3, -4, -5}; |
| span<const int> expected_span(vector); |
| // Note: While static_cast<T&&>(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<std::vector<int>&&>(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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| #if 0 // Not currently working with fixed extent spans. |
| |
| TEST(SpanTest, MakeStaticSpanFromRValueContainer) { |
| std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| span<const int, 5> expected_span(vector.data(), vector.size()); |
| // Note: While static_cast<T&&>(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<std::vector<int>&&>(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<decltype(expected_span), decltype(made_span)>::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<const int> expected_span(kArray); |
| constexpr auto made_span = make_span(expected_span); |
| static_assert(std::is_same<decltype(expected_span)::element_type, |
| decltype(made_span)::element_type>::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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| TEST(SpanTest, MakeSpanFromStaticSpan) { |
| static constexpr int kArray[] = {1, 2, 3, 4, 5}; |
| constexpr span<const int, 5> expected_span(kArray); |
| constexpr auto made_span = make_span(expected_span); |
| static_assert(std::is_same<decltype(expected_span)::element_type, |
| decltype(made_span)::element_type>::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<decltype(expected_span), decltype(made_span)>::value, |
| "the type of made_span differs from expected_span!"); |
| } |
| |
| } // namespace |
| } // namespace pw::kvs |