Wyatt Hepler | 2debeb6 | 2020-12-03 08:19:57 -0800 | [diff] [blame] | 1 | // Copyright 2020 The Pigweed Authors |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 4 | // use this file except in compliance with the License. You may obtain a copy of |
| 5 | // the License at |
| 6 | // |
| 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 12 | // License for the specific language governing permissions and limitations under |
| 13 | // the License. |
| 14 | |
| 15 | #include <array> |
| 16 | #include <cstddef> |
| 17 | #include <span> |
| 18 | #include <string_view> |
| 19 | #include <vector> |
| 20 | |
| 21 | #include "gtest/gtest.h" |
| 22 | #include "pw_kvs/internal/span_traits.h" |
| 23 | |
| 24 | namespace pw::kvs { |
| 25 | namespace { |
| 26 | |
| 27 | using internal::make_span; |
| 28 | using std::byte; |
| 29 | using std::dynamic_extent; |
| 30 | using std::span; |
| 31 | |
| 32 | // Test that the ConvertsToSpan trait correctly idenitifies types that convert |
| 33 | // to std::span. |
| 34 | |
| 35 | // Basic types should not convert to span. |
| 36 | struct Foo {}; |
| 37 | |
| 38 | static_assert(!ConvertsToSpan<Foo>()); |
| 39 | static_assert(!ConvertsToSpan<int>()); |
| 40 | static_assert(!ConvertsToSpan<void>()); |
| 41 | static_assert(!ConvertsToSpan<byte>()); |
| 42 | static_assert(!ConvertsToSpan<byte*>()); |
| 43 | |
| 44 | // Arrays without an extent are just pointers -- these should not convert. |
| 45 | static_assert(!ConvertsToSpan<bool[]>()); |
| 46 | static_assert(!ConvertsToSpan<const int[]>()); |
| 47 | static_assert(!ConvertsToSpan<bool (&)[]>()); |
| 48 | static_assert(!ConvertsToSpan<const int (&)[]>()); |
| 49 | static_assert(!ConvertsToSpan<bool(&&)[]>()); |
| 50 | static_assert(!ConvertsToSpan<const int(&&)[]>()); |
| 51 | |
| 52 | // C arrays convert to span. |
| 53 | static_assert(ConvertsToSpan<std::array<int, 5>>()); |
| 54 | static_assert(ConvertsToSpan<decltype("Hello!")>()); |
| 55 | |
| 56 | static_assert(ConvertsToSpan<bool[1]>()); |
| 57 | static_assert(ConvertsToSpan<char[35]>()); |
| 58 | static_assert(ConvertsToSpan<const int[35]>()); |
| 59 | |
| 60 | static_assert(ConvertsToSpan<bool (&)[1]>()); |
| 61 | static_assert(ConvertsToSpan<char (&)[35]>()); |
| 62 | static_assert(ConvertsToSpan<const int (&)[35]>()); |
| 63 | |
| 64 | static_assert(ConvertsToSpan<bool(&&)[1]>()); |
| 65 | static_assert(ConvertsToSpan<bool(&&)[1]>()); |
| 66 | static_assert(ConvertsToSpan<char(&&)[35]>()); |
| 67 | static_assert(ConvertsToSpan<const int(&&)[35]>()); |
| 68 | |
| 69 | // Container types convert to span. |
| 70 | struct FakeContainer { |
| 71 | const char* data() const { return nullptr; } |
| 72 | size_t size() const { return 0; } |
| 73 | }; |
| 74 | |
| 75 | static_assert(ConvertsToSpan<FakeContainer>()); |
| 76 | static_assert(ConvertsToSpan<FakeContainer&>()); |
| 77 | static_assert(ConvertsToSpan<FakeContainer&&>()); |
| 78 | static_assert(ConvertsToSpan<const FakeContainer>()); |
| 79 | static_assert(ConvertsToSpan<const FakeContainer&>()); |
| 80 | static_assert(ConvertsToSpan<const FakeContainer&&>()); |
| 81 | |
| 82 | static_assert(ConvertsToSpan<std::string_view>()); |
| 83 | static_assert(ConvertsToSpan<std::string_view&>()); |
| 84 | static_assert(ConvertsToSpan<std::string_view&&>()); |
| 85 | |
| 86 | static_assert(ConvertsToSpan<const std::string_view>()); |
| 87 | static_assert(ConvertsToSpan<const std::string_view&>()); |
| 88 | static_assert(ConvertsToSpan<const std::string_view&&>()); |
| 89 | |
| 90 | // Spans should also convert to span. |
| 91 | static_assert(ConvertsToSpan<std::span<int>>()); |
| 92 | static_assert(ConvertsToSpan<std::span<byte>>()); |
| 93 | static_assert(ConvertsToSpan<std::span<const int*>>()); |
| 94 | static_assert(ConvertsToSpan<std::span<bool>&&>()); |
| 95 | static_assert(ConvertsToSpan<const std::span<bool>&>()); |
| 96 | static_assert(ConvertsToSpan<std::span<bool>&&>()); |
| 97 | |
| 98 | // These tests for the make_span function were copied from Chromium: |
| 99 | // https://chromium.googlesource.com/chromium/src/+/master/base/containers/span_unittest.cc |
| 100 | |
| 101 | TEST(SpanTest, MakeSpanFromDataAndSize) { |
| 102 | int* nullint = nullptr; |
| 103 | auto empty_span = make_span(nullint, 0); |
| 104 | EXPECT_TRUE(empty_span.empty()); |
| 105 | EXPECT_EQ(nullptr, empty_span.data()); |
| 106 | std::vector<int> vector = {1, 1, 2, 3, 5, 8}; |
| 107 | span<int> expected_span(vector.data(), vector.size()); |
| 108 | auto made_span = make_span(vector.data(), vector.size()); |
| 109 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 110 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 111 | static_assert(decltype(made_span)::extent == dynamic_extent, ""); |
| 112 | static_assert( |
| 113 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 114 | "the type of made_span differs from expected_span!"); |
| 115 | } |
| 116 | |
| 117 | TEST(SpanTest, MakeSpanFromPointerPair) { |
| 118 | int* nullint = nullptr; |
| 119 | auto empty_span = make_span(nullint, nullint); |
| 120 | EXPECT_TRUE(empty_span.empty()); |
| 121 | EXPECT_EQ(nullptr, empty_span.data()); |
| 122 | std::vector<int> vector = {1, 1, 2, 3, 5, 8}; |
| 123 | span<int> expected_span(vector.data(), vector.size()); |
| 124 | auto made_span = make_span(vector.data(), vector.data() + vector.size()); |
| 125 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 126 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 127 | static_assert(decltype(made_span)::extent == dynamic_extent, ""); |
| 128 | static_assert( |
| 129 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 130 | "the type of made_span differs from expected_span!"); |
| 131 | } |
| 132 | |
| 133 | TEST(SpanTest, MakeSpanFromConstexprArray) { |
| 134 | static constexpr int kArray[] = {1, 2, 3, 4, 5}; |
| 135 | constexpr span<const int, 5> expected_span(kArray); |
| 136 | constexpr auto made_span = make_span(kArray); |
| 137 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 138 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 139 | static_assert(decltype(made_span)::extent == 5, ""); |
| 140 | static_assert( |
| 141 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 142 | "the type of made_span differs from expected_span!"); |
| 143 | } |
| 144 | |
| 145 | TEST(SpanTest, MakeSpanFromStdArray) { |
| 146 | const std::array<int, 5> kArray = {{1, 2, 3, 4, 5}}; |
| 147 | span<const int, 5> expected_span(kArray); |
| 148 | auto made_span = make_span(kArray); |
| 149 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 150 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 151 | static_assert(decltype(made_span)::extent == 5, ""); |
| 152 | static_assert( |
| 153 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 154 | "the type of made_span differs from expected_span!"); |
| 155 | } |
| 156 | |
| 157 | TEST(SpanTest, MakeSpanFromConstContainer) { |
| 158 | const std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| 159 | span<const int> expected_span(vector); |
| 160 | auto made_span = make_span(vector); |
| 161 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 162 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 163 | static_assert(decltype(made_span)::extent == dynamic_extent, ""); |
| 164 | static_assert( |
| 165 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 166 | "the type of made_span differs from expected_span!"); |
| 167 | } |
| 168 | |
| 169 | #if 0 // Not currently working with fixed extent spans. |
| 170 | |
| 171 | TEST(SpanTest, MakeStaticSpanFromConstContainer) { |
| 172 | const std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| 173 | span<const int, 5> expected_span(vector.data(), vector.size()); |
| 174 | auto made_span = make_span<5>(vector); |
| 175 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 176 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 177 | static_assert(decltype(made_span)::extent == 5, ""); |
| 178 | static_assert( |
| 179 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 180 | "the type of made_span differs from expected_span!"); |
| 181 | } |
| 182 | |
| 183 | #endif // 0 |
| 184 | |
| 185 | TEST(SpanTest, MakeSpanFromContainer) { |
| 186 | std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| 187 | span<int> expected_span(vector); |
| 188 | auto made_span = make_span(vector); |
| 189 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 190 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 191 | static_assert(decltype(made_span)::extent == dynamic_extent, ""); |
| 192 | static_assert( |
| 193 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 194 | "the type of made_span differs from expected_span!"); |
| 195 | } |
| 196 | |
| 197 | #if 0 // Not currently working with fixed extent spans. |
| 198 | |
| 199 | TEST(SpanTest, MakeStaticSpanFromContainer) { |
| 200 | std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| 201 | span<int, 5> expected_span(vector.data(), vector.size()); |
| 202 | auto made_span = make_span<5>(vector); |
| 203 | EXPECT_EQ(expected_span.data(), make_span<5>(vector).data()); |
| 204 | EXPECT_EQ(expected_span.size(), make_span<5>(vector).size()); |
| 205 | static_assert(decltype(make_span<5>(vector))::extent == 5, ""); |
| 206 | static_assert( |
| 207 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 208 | "the type of made_span differs from expected_span!"); |
| 209 | } |
| 210 | |
| 211 | TEST(SpanTest, MakeStaticSpanFromConstexprContainer) { |
| 212 | constexpr StringPiece str = "Hello, World"; |
| 213 | constexpr auto made_span = make_span<12>(str); |
| 214 | static_assert(str.data() == made_span.data(), "Error: data() does not match"); |
| 215 | static_assert(str.size() == made_span.size(), "Error: size() does not match"); |
| 216 | static_assert(std::is_same<decltype(str)::value_type, |
| 217 | decltype(made_span)::value_type>::value, |
| 218 | "Error: value_type does not match"); |
| 219 | static_assert(str.size() == decltype(made_span)::extent, |
| 220 | "Error: extent does not match"); |
| 221 | } |
| 222 | |
| 223 | #endif // 0 |
| 224 | |
| 225 | TEST(SpanTest, MakeSpanFromRValueContainer) { |
| 226 | std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| 227 | span<const int> expected_span(vector); |
| 228 | // Note: While static_cast<T&&>(foo) is effectively just a fancy spelling of |
| 229 | // std::move(foo), make_span does not actually take ownership of the passed in |
| 230 | // container. Writing it this way makes it more obvious that we simply care |
| 231 | // about the right behavour when passing rvalues. |
| 232 | auto made_span = make_span(static_cast<std::vector<int>&&>(vector)); |
| 233 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 234 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 235 | static_assert(decltype(made_span)::extent == dynamic_extent, ""); |
| 236 | static_assert( |
| 237 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 238 | "the type of made_span differs from expected_span!"); |
| 239 | } |
| 240 | |
| 241 | #if 0 // Not currently working with fixed extent spans. |
| 242 | |
| 243 | TEST(SpanTest, MakeStaticSpanFromRValueContainer) { |
| 244 | std::vector<int> vector = {-1, -2, -3, -4, -5}; |
| 245 | span<const int, 5> expected_span(vector.data(), vector.size()); |
| 246 | // Note: While static_cast<T&&>(foo) is effectively just a fancy spelling of |
| 247 | // std::move(foo), make_span does not actually take ownership of the passed in |
| 248 | // container. Writing it this way makes it more obvious that we simply care |
| 249 | // about the right behavour when passing rvalues. |
| 250 | auto made_span = make_span<5>(static_cast<std::vector<int>&&>(vector)); |
| 251 | EXPECT_EQ(expected_span.data(), made_span.data()); |
| 252 | EXPECT_EQ(expected_span.size(), made_span.size()); |
| 253 | static_assert(decltype(made_span)::extent == 5, ""); |
| 254 | static_assert( |
| 255 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 256 | "the type of made_span differs from expected_span!"); |
| 257 | } |
| 258 | |
| 259 | #endif // 0 |
| 260 | |
| 261 | TEST(SpanTest, MakeSpanFromDynamicSpan) { |
| 262 | static constexpr int kArray[] = {1, 2, 3, 4, 5}; |
| 263 | constexpr span<const int> expected_span(kArray); |
| 264 | constexpr auto made_span = make_span(expected_span); |
| 265 | static_assert(std::is_same<decltype(expected_span)::element_type, |
| 266 | decltype(made_span)::element_type>::value, |
| 267 | "make_span(span) should have the same element_type as span"); |
| 268 | static_assert(expected_span.data() == made_span.data(), |
| 269 | "make_span(span) should have the same data() as span"); |
| 270 | static_assert(expected_span.size() == made_span.size(), |
| 271 | "make_span(span) should have the same size() as span"); |
| 272 | static_assert(decltype(made_span)::extent == decltype(expected_span)::extent, |
| 273 | "make_span(span) should have the same extent as span"); |
| 274 | static_assert( |
| 275 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 276 | "the type of made_span differs from expected_span!"); |
| 277 | } |
| 278 | |
| 279 | TEST(SpanTest, MakeSpanFromStaticSpan) { |
| 280 | static constexpr int kArray[] = {1, 2, 3, 4, 5}; |
| 281 | constexpr span<const int, 5> expected_span(kArray); |
| 282 | constexpr auto made_span = make_span(expected_span); |
| 283 | static_assert(std::is_same<decltype(expected_span)::element_type, |
| 284 | decltype(made_span)::element_type>::value, |
| 285 | "make_span(span) should have the same element_type as span"); |
| 286 | static_assert(expected_span.data() == made_span.data(), |
| 287 | "make_span(span) should have the same data() as span"); |
| 288 | static_assert(expected_span.size() == made_span.size(), |
| 289 | "make_span(span) should have the same size() as span"); |
| 290 | static_assert(decltype(made_span)::extent == decltype(expected_span)::extent, |
| 291 | "make_span(span) should have the same extent as span"); |
| 292 | static_assert( |
| 293 | std::is_same<decltype(expected_span), decltype(made_span)>::value, |
| 294 | "the type of made_span differs from expected_span!"); |
| 295 | } |
| 296 | |
| 297 | } // namespace |
| 298 | } // namespace pw::kvs |