blob: 9d0d44f14faa58491ebb57386e89689f325d0462 [file] [log] [blame]
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// span_unittests.cpp: Unit tests for the angle::Span class.
//
#ifdef UNSAFE_BUFFERS_BUILD
# pragma allow_unsafe_buffers
#endif
#include "common/span.h"
#include <gtest/gtest.h>
#include <array>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
namespace angle
{
namespace
{
constexpr size_t kSpanDataSize = 16;
constexpr unsigned int kSpanData[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15};
constexpr std::array<const unsigned int, kSpanDataSize> kSpanArr = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}};
class FakeRange
{
public:
size_t size() const { return kSpanDataSize; }
const unsigned int *data() { return kSpanData; }
};
// Test that comparing spans work
TEST(SpanTest, Comparison)
{
// Duplicate data to make sure comparison is being done on values (and not addresses).
static constexpr unsigned int kSpanDataDup[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15};
// Don't use ASSERT_EQ at first because the == is more hidden
ASSERT_TRUE(Span<const unsigned int>() == Span(kSpanData, 0));
ASSERT_TRUE(Span(kSpanData + 3, 4) != Span(kSpanDataDup + 5, 4));
// Check ASSERT_EQ and ASSERT_NE work correctly
ASSERT_EQ(Span(kSpanData, kSpanDataSize), Span(kSpanDataDup, kSpanDataSize));
ASSERT_NE(Span(kSpanData, kSpanDataSize - 1), Span(kSpanDataDup + 1, kSpanDataSize - 1));
ASSERT_NE(Span(kSpanData, kSpanDataSize), Span(kSpanDataDup, kSpanDataSize - 1));
ASSERT_NE(Span(kSpanData, kSpanDataSize - 1), Span(kSpanDataDup, kSpanDataSize));
ASSERT_NE(Span(kSpanData, 0), Span(kSpanDataDup, 1));
ASSERT_NE(Span(kSpanData, 1), Span(kSpanDataDup, 0));
}
// Test indexing
TEST(SpanTest, Indexing)
{
constexpr Span sp(kSpanData, kSpanDataSize);
for (size_t i = 0; i < kSpanDataSize; ++i)
{
ASSERT_EQ(sp[i], i);
}
unsigned int storage[kSpanDataSize] = {};
angle::Span<unsigned int> writableSpan(storage, kSpanDataSize);
for (size_t i = 0; i < kSpanDataSize; ++i)
{
writableSpan[i] = i;
}
for (size_t i = 0; i < kSpanDataSize; ++i)
{
ASSERT_EQ(writableSpan[i], i);
}
for (size_t i = 0; i < kSpanDataSize; ++i)
{
ASSERT_EQ(storage[i], i);
}
}
// Test for the various constructors
TEST(SpanTest, Constructors)
{
// Default constructor
{
Span<const unsigned int> sp;
static_assert(std::is_same_v<decltype(sp), Span<const unsigned int, dynamic_extent>>);
ASSERT_TRUE(sp.size() == 0);
ASSERT_TRUE(sp.empty());
}
// Constexpr construct from pointer and size
{
constexpr Span sp(kSpanData, kSpanDataSize);
static_assert(std::is_same_v<decltype(sp), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(sp.data(), kSpanData);
ASSERT_EQ(sp.size(), kSpanDataSize);
ASSERT_FALSE(sp.empty());
}
// Constexpr construct from fixed C-style array
{
constexpr Span sp(kSpanData);
static_assert(std::is_same_v<decltype(sp), const Span<const unsigned int, kSpanDataSize>>);
ASSERT_EQ(sp.data(), kSpanData);
ASSERT_EQ(sp.size(), kSpanDataSize);
ASSERT_FALSE(sp.empty());
}
// Constexpr construct from constexpr std::array<const>
{
constexpr Span sp(kSpanArr);
static_assert(std::is_same_v<decltype(sp), const Span<const unsigned int, kSpanDataSize>>);
ASSERT_EQ(sp.data(), kSpanArr.data());
ASSERT_EQ(sp.size(), kSpanArr.size());
ASSERT_FALSE(sp.empty());
}
// Construct from const std::array<non-const>.
{
const std::array<int, 2> kArr = {1, 2};
Span sp(kArr);
static_assert(std::is_same_v<decltype(sp), Span<const int, 2u>>);
ASSERT_EQ(sp.data(), kArr.data());
ASSERT_EQ(sp.size(), kArr.size());
ASSERT_FALSE(sp.empty());
}
// Construct from std::array<const>.
{
std::array<const int, 2> arr = {1, 2};
Span sp(arr);
static_assert(std::is_same_v<decltype(sp), Span<const int, 2u>>);
ASSERT_EQ(sp.data(), arr.data());
ASSERT_EQ(sp.size(), arr.size());
ASSERT_FALSE(sp.empty());
}
// Construct from std::array<non-const>.
{
std::array<int, 2> arr = {1, 2};
Span sp(arr);
static_assert(std::is_same_v<decltype(sp), Span<int, 2u>>);
ASSERT_EQ(sp.data(), arr.data());
ASSERT_EQ(sp.size(), arr.size());
ASSERT_FALSE(sp.empty());
}
// Construct from std::vector
{
std::vector<unsigned int> vec({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
Span sp(vec);
static_assert(std::is_same_v<decltype(sp), Span<unsigned int, dynamic_extent>>);
ASSERT_EQ(sp.data(), vec.data());
ASSERT_EQ(sp.size(), vec.size());
ASSERT_FALSE(sp.empty());
}
// Construct from const std::vector
{
const std::vector<unsigned int> vec({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
Span sp(vec);
static_assert(std::is_same_v<decltype(sp), Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(sp.data(), vec.data());
ASSERT_EQ(sp.size(), vec.size());
ASSERT_FALSE(sp.empty());
}
// Construct from std::string
{
std::string str = "hooray";
Span sp(str);
static_assert(std::is_same_v<decltype(sp), Span<char, dynamic_extent>>);
ASSERT_EQ(sp.data(), str.data());
ASSERT_EQ(sp.size(), str.size());
ASSERT_FALSE(sp.empty());
}
// Construct from const std::string
{
const std::string str = "hooray";
Span sp(str);
static_assert(std::is_same_v<decltype(sp), Span<const char, dynamic_extent>>);
ASSERT_EQ(sp.data(), str.data());
ASSERT_EQ(sp.size(), str.size());
ASSERT_FALSE(sp.empty());
}
// Construct from std::string_view
{
std::string_view view = "hooray";
Span sp(view);
static_assert(std::is_same_v<decltype(sp), Span<const char, dynamic_extent>>);
ASSERT_EQ(sp.data(), view.data());
ASSERT_EQ(sp.size(), view.size());
ASSERT_FALSE(sp.empty());
}
// Construction from any class that provides data() and size().
{
FakeRange range;
Span sp(range);
ASSERT_EQ(sp.data(), kSpanData);
ASSERT_EQ(sp.size(), kSpanDataSize);
}
// Copy constructor and copy assignment
{
Span sp(kSpanData);
Span sp2(sp);
Span<const unsigned int> sp3;
ASSERT_EQ(sp, sp2);
ASSERT_EQ(sp2.data(), kSpanData);
ASSERT_EQ(sp2.size(), kSpanDataSize);
ASSERT_FALSE(sp2.empty());
sp3 = sp;
ASSERT_EQ(sp3, sp);
ASSERT_EQ(sp3.data(), kSpanData);
ASSERT_EQ(sp3.size(), kSpanDataSize);
ASSERT_FALSE(sp3.empty());
}
}
// Test accessing the data directly
TEST(SpanTest, DataAccess)
{
constexpr Span sp(kSpanData, kSpanDataSize);
const unsigned int *data = sp.data();
for (size_t i = 0; i < kSpanDataSize; ++i)
{
ASSERT_EQ(data[i], i);
}
}
// Test front and back
TEST(SpanTest, FrontAndBack)
{
constexpr Span sp(kSpanData, kSpanDataSize);
ASSERT_TRUE(sp.front() == 0);
ASSERT_EQ(sp.back(), kSpanDataSize - 1);
}
// Test begin and end
TEST(SpanTest, BeginAndEnd)
{
constexpr Span sp(kSpanData, kSpanDataSize);
size_t currentIndex = 0;
for (unsigned int value : sp)
{
ASSERT_EQ(value, currentIndex);
++currentIndex;
}
}
// Test reverse begin and end
TEST(SpanTest, RbeginAndRend)
{
constexpr Span sp(kSpanData, kSpanDataSize);
size_t currentIndex = 0;
for (auto iter = sp.rbegin(); iter != sp.rend(); ++iter)
{
ASSERT_EQ(*iter, kSpanDataSize - 1 - currentIndex);
++currentIndex;
}
}
// Test first and last
TEST(SpanTest, FirstAndLast)
{
constexpr Span sp(kSpanData, kSpanDataSize);
constexpr size_t kSplitSize = kSpanDataSize / 4;
{
constexpr Span first = sp.first(kSplitSize);
constexpr Span last = sp.last(kSplitSize);
static_assert(
std::is_same_v<decltype(first), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(first, Span(kSpanData, kSplitSize));
ASSERT_EQ(first.data(), kSpanData);
ASSERT_EQ(first.size(), kSplitSize);
static_assert(
std::is_same_v<decltype(last), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(last, Span(kSpanData + kSpanDataSize - kSplitSize, kSplitSize));
ASSERT_EQ(last.data(), kSpanData + kSpanDataSize - kSplitSize);
ASSERT_EQ(last.size(), kSplitSize);
}
{
constexpr Span first = sp.first<kSplitSize>();
constexpr Span last = sp.last<kSplitSize>();
static_assert(std::is_same_v<decltype(first), const Span<const unsigned int, kSplitSize>>);
ASSERT_EQ(first, Span(kSpanData, kSplitSize));
ASSERT_EQ(first.data(), kSpanData);
ASSERT_EQ(first.size(), kSplitSize);
static_assert(std::is_same_v<decltype(last), const Span<const unsigned int, kSplitSize>>);
ASSERT_EQ(last, Span(kSpanData + kSpanDataSize - kSplitSize, kSplitSize));
ASSERT_EQ(last.data(), kSpanData + kSpanDataSize - kSplitSize);
ASSERT_EQ(last.size(), kSplitSize);
}
}
// Test subspan
TEST(SpanTest, Subspan)
{
constexpr Span sp(kSpanData, kSpanDataSize);
constexpr size_t kSplitOffset = kSpanDataSize / 4;
constexpr size_t kSplitSize = kSpanDataSize / 2;
// Test one-arg subspan
{
constexpr Span subspan = sp.subspan(kSplitOffset);
static_assert(
std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSpanDataSize - kSplitOffset));
ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset);
ASSERT_EQ(subspan.size(), kSpanDataSize - kSplitOffset);
}
// Test two-arg subspan
{
constexpr Span subspan = sp.subspan(kSplitOffset, kSplitSize);
static_assert(
std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSplitSize));
ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset);
ASSERT_EQ(subspan.size(), kSplitSize);
}
// Templated one-arg subspan just sugar until C++20 fixed spans.
{
constexpr Span subspan = sp.subspan<kSplitOffset>();
static_assert(
std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSpanDataSize - kSplitOffset));
ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset);
ASSERT_EQ(subspan.size(), kSpanDataSize - kSplitOffset);
}
// Templated two-arg subspan just sugar until C++20 fixed spans.
{
constexpr Span subspan = sp.subspan<kSplitOffset, kSplitSize>();
static_assert(
std::is_same_v<decltype(subspan), const Span<const unsigned int, dynamic_extent>>);
ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSplitSize));
ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset);
ASSERT_EQ(subspan.size(), kSplitSize);
}
}
// Test conversions from non-const to const spans.
TEST(SpanTest, ConstConversions)
{
const unsigned int kStorage[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15};
unsigned int storage[kSpanDataSize] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
angle::Span readable_span(kStorage, kSpanDataSize);
angle::Span writable_span(storage, kSpanDataSize);
static_assert(
std::is_same_v<decltype(readable_span), Span<const unsigned int, dynamic_extent>>);
static_assert(std::is_same_v<decltype(writable_span), Span<unsigned int, dynamic_extent>>);
// Direct comparisons allowed.
EXPECT_TRUE(readable_span == writable_span);
EXPECT_FALSE(readable_span != writable_span);
writable_span[0] = 1234;
EXPECT_FALSE(readable_span == writable_span);
// Assignment allowed from non-const to const.
readable_span = writable_span;
EXPECT_TRUE(readable_span == writable_span);
}
// Test conversions from fixed to dynamic spans.
TEST(SpanTest, FixedConversions)
{
unsigned int storage[kSpanDataSize] = {};
std::vector<unsigned int> vec(kSpanDataSize, 42);
Span<unsigned int> dynamic_span(vec);
Span<unsigned int, kSpanDataSize> static_span(storage);
// Direct comparisons allowed.
EXPECT_NE(static_span, dynamic_span);
// Assignment allowed from fixed to dynamic.
dynamic_span = static_span;
EXPECT_EQ(static_span, dynamic_span);
// Other way around prohibited without fixed conversion.
dynamic_span[0] = 1234;
static_span = dynamic_span.first<kSpanDataSize>();
EXPECT_EQ(static_span, dynamic_span);
}
// Test non-member functions.
TEST(SpanTest, Helpers)
{
// Test as_bytes.
{
constexpr int kArray[] = {2, 3, 5, 7, 11, 13};
auto bytes_span = as_bytes(Span(kArray));
EXPECT_EQ(reinterpret_cast<const uint8_t *>(kArray), bytes_span.data());
EXPECT_EQ(sizeof(kArray), bytes_span.size());
EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes());
}
// Test as_writble_bytes.
{
std::vector<int> vec = {1, 1, 2, 3, 5, 8};
Span<int> mutable_span(vec);
auto writable_bytes_span = as_writable_bytes(mutable_span);
static_assert(std::is_same_v<decltype(writable_bytes_span), Span<uint8_t>>);
EXPECT_EQ(reinterpret_cast<uint8_t *>(vec.data()), writable_bytes_span.data());
EXPECT_EQ(sizeof(int) * vec.size(), writable_bytes_span.size());
EXPECT_EQ(writable_bytes_span.size(), writable_bytes_span.size_bytes());
}
// Test as_chars.
{
constexpr int kArray[] = {2, 3, 5, 7, 11, 13};
auto chars_span = as_chars(Span(kArray));
EXPECT_EQ(reinterpret_cast<const char *>(kArray), chars_span.data());
EXPECT_EQ(sizeof(kArray), chars_span.size());
EXPECT_EQ(chars_span.size(), chars_span.size_bytes());
}
// Test as writable chars.
{
std::vector<int> vec = {1, 1, 2, 3, 5, 8};
Span<int> mutable_span(vec);
auto writable_chars_span = as_writable_chars(mutable_span);
static_assert(std::is_same_v<decltype(writable_chars_span), Span<char>>);
EXPECT_EQ(reinterpret_cast<char *>(vec.data()), writable_chars_span.data());
EXPECT_EQ(sizeof(int) * vec.size(), writable_chars_span.size());
EXPECT_EQ(writable_chars_span.size(), writable_chars_span.size_bytes());
}
// Test span_from_ref.
{
int x = 123;
auto s = span_from_ref(x);
EXPECT_EQ(&x, s.data());
EXPECT_EQ(1u, s.size());
EXPECT_EQ(sizeof(int), s.size_bytes());
EXPECT_EQ(123, s[0]);
}
// Test byte_span_from_ref.
{
int x = 123;
auto b = byte_span_from_ref(x);
EXPECT_EQ(reinterpret_cast<uint8_t *>(&x), b.data());
EXPECT_EQ(sizeof(int), b.size());
}
// Test as_byte_span.
{
const std::vector<int> kVec({2, 3, 5, 7, 11, 13});
auto byte_span = as_byte_span(kVec);
static_assert(std::is_same_v<decltype(byte_span), Span<const uint8_t>>);
EXPECT_EQ(byte_span.data(), reinterpret_cast<const uint8_t *>(kVec.data()));
EXPECT_EQ(byte_span.size(), kVec.size() * sizeof(int));
}
// Test as_writable_byte_span
{
int kMutArray[] = {2, 3, 5, 7};
auto byte_span = as_writable_byte_span(kMutArray);
EXPECT_EQ(byte_span.data(), reinterpret_cast<uint8_t *>(kMutArray));
EXPECT_EQ(byte_span.size(), sizeof(kMutArray));
}
}
} // anonymous namespace
} // namespace angle