Colin Snover 58c83dcd14 COMMON: Make SpanOwner copy assignment make a copy of the owned Span
To move data from one SpanOwner to another, use `moveFrom`.
Thanks @waltervn for pointing out the problem.
2017-06-08 10:45:55 -05:00

809 lines
24 KiB
C++

#include <cxxtest/TestSuite.h>
class SpanTestSuite;
#include "common/span.h"
#include "common/str.h"
class SpanTestSuite : public CxxTest::TestSuite {
struct Foo {
int a;
};
template <typename ValueType, template <typename> class Derived>
class SiblingSpanImpl : public Common::SpanImpl<ValueType, Derived> {
typedef Common::SpanImpl<ValueType, Derived> super_type;
public:
COMMON_SPAN_TYPEDEFS
SiblingSpanImpl() : super_type() {}
SiblingSpanImpl(pointer data_, size_type size_) : super_type(data_, size_) {}
};
template <typename ValueType>
class SiblingSpan : public SiblingSpanImpl<ValueType, SiblingSpan> {
typedef SiblingSpanImpl<ValueType, ::SpanTestSuite::SiblingSpan> super_type;
public:
COMMON_SPAN_TYPEDEFS
SiblingSpan() : super_type() {}
SiblingSpan(pointer data_, size_type size_) : super_type(data_, size_) {}
};
template <typename ValueType, template <typename> class Derived>
class SubSpanImpl : public Common::NamedSpanImpl<ValueType, Derived> {
typedef Common::NamedSpanImpl<ValueType, Derived> super_type;
public:
COMMON_SPAN_TYPEDEFS
SubSpanImpl() : super_type() {}
SubSpanImpl(pointer data_,
size_type size_,
const Common::String &name_ = Common::String(),
const size_type sourceByteOffset_ = 0) :
super_type(data_, size_, name_, sourceByteOffset_) {}
template <typename Other>
SubSpanImpl(const Other &other) : super_type(other) {}
};
template <typename ValueType>
class SubSpan : public SubSpanImpl<ValueType, SubSpan> {
typedef SubSpanImpl<ValueType, ::SpanTestSuite::SubSpan> super_type;
public:
COMMON_SPAN_TYPEDEFS
SubSpan() : super_type() {}
SubSpan(pointer data_,
size_type size_,
const Common::String &name_ = Common::String(),
const size_type sourceByteOffset_ = 0) :
super_type(data_, size_, name_, sourceByteOffset_) {}
template <typename Other>
SubSpan(const Other &other) : super_type(other) {}
};
public:
void test_sibling_span() {
byte data[] = { 'h', 'e', 'l', 'l', 'o' };
SiblingSpan<byte> ss(data, sizeof(data));
Common::Span<byte> superInstance = ss;
TS_ASSERT_EQUALS(ss.data(), data);
TS_ASSERT_EQUALS(superInstance.data(), data);
}
void test_sub_span() {
byte data[] = { 'h', 'e', 'l', 'l', 'o' };
SubSpan<byte> ss(data, sizeof(data), "custom subspan");
Common::NamedSpan<byte> namedSuper = ss;
Common::Span<byte> unnamedSuper = ss;
TS_ASSERT(ss.name() == "custom subspan");
TS_ASSERT(namedSuper.name() == ss.name());
TS_ASSERT(unnamedSuper.name() == Common::String::format("%p", (void *)data));
}
void test_span_iterator_const() {
byte data[] = { 'h', 'e', 'l', 'l', 'o' };
const Common::Span<byte> span(data, sizeof(data));
Common::Span<byte>::const_iterator it = span.cbegin();
Common::Span<byte>::const_iterator sameIt(it);
TS_ASSERT_EQUALS(sameIt, it);
uint i;
for (i = 0; it != span.cend(); ++i, ++it) {
TS_ASSERT_EQUALS(*it, data[i]);
TS_ASSERT_LESS_THAN(i, sizeof(data));
}
TS_ASSERT_EQUALS(i, sizeof(data));
it = span.cend() - 1;
for (i = sizeof(data) - 1; it != span.cbegin(); --i, --it) {
TS_ASSERT_EQUALS(data[i], *it);
}
TS_ASSERT_EQUALS(i, 0U);
it = span.cbegin();
it += 4;
TS_ASSERT_EQUALS(data[4], *it);
it -= 4;
TS_ASSERT_EQUALS(data[0], *it);
TS_ASSERT_EQUALS(data[0], *it++);
TS_ASSERT_EQUALS(data[1], *it--);
TS_ASSERT_EQUALS(span.cend() - span.cbegin(), 5);
TS_ASSERT_EQUALS(*(span.cbegin() + 4), data[4]);
TS_ASSERT_EQUALS(*(span.cend() - 4), data[1]);
TS_ASSERT(span.cbegin() < span.cend());
TS_ASSERT(span.cbegin() <= span.cend());
TS_ASSERT(span.cbegin() <= span.cbegin());
TS_ASSERT(span.cend() > span.cbegin());
TS_ASSERT(span.cend() >= span.cbegin());
TS_ASSERT(span.cend() >= span.cend());
}
void test_span_iterator() {
byte data[] = { 'h', 'e', 'l', 'l', 'o' };
Common::Span<byte> span(data, sizeof(data));
// empty iterator should default construct OK
Common::Span<byte>::iterator defaultIt;
Common::Span<byte>::iterator it = span.begin();
Common::Span<byte>::iterator sameIt(it);
TS_ASSERT_EQUALS(sameIt, it);
uint i;
for (i = 0; it != span.end(); ++i, ++it) {
TS_ASSERT_EQUALS(*it, data[i]);
TS_ASSERT_LESS_THAN(i, sizeof(data));
}
TS_ASSERT_EQUALS(i, sizeof(data));
it = span.end() - 1;
for (i = sizeof(data) - 1; it != span.begin(); --i, --it) {
TS_ASSERT_EQUALS(data[i], *it);
}
TS_ASSERT_EQUALS(i, 0U);
it = span.begin();
it += 4;
TS_ASSERT_EQUALS(data[4], *it);
it -= 4;
TS_ASSERT_EQUALS(data[0], *it);
TS_ASSERT_EQUALS(data[0], *it++);
TS_ASSERT_EQUALS(data[1], *it--);
TS_ASSERT_EQUALS(span.end() - span.begin(), 5);
TS_ASSERT_EQUALS(*(span.begin() + 4), data[4]);
TS_ASSERT_EQUALS(*(span.end() - 4), data[1]);
TS_ASSERT(span.begin() < span.end());
TS_ASSERT(span.begin() <= span.end());
TS_ASSERT(span.begin() <= span.begin());
TS_ASSERT(span.end() > span.begin());
TS_ASSERT(span.end() >= span.begin());
TS_ASSERT(span.end() >= span.end());
it = span.begin();
for (i = 0; it != span.end(); ++i, ++it) {
*it = 'a' + i;
}
it = span.begin();
for (i = 0; it != span.end(); ++i, ++it) {
TS_ASSERT_EQUALS(*it, 'a' + i);
TS_ASSERT_EQUALS(data[i], 'a' + i);
}
}
void test_span_iterator_integers() {
const byte data[] = { 0xFF, 1, 2, 3, 2, 1, 0xFF };
Common::Span<const byte> span(data, sizeof(data));
Common::Span<const byte>::const_iterator it = span.cbegin();
TS_ASSERT_EQUALS(it.getInt8(), -1);
TS_ASSERT_EQUALS(it.getUint8(), 255);
TS_ASSERT_EQUALS(it.getInt16BE(), -255);
TS_ASSERT_EQUALS(it.getUint16BE(), 65281U);
TS_ASSERT_EQUALS((it + 5).getInt16LE(), -255);
TS_ASSERT_EQUALS((it + 5).getUint16LE(), 65281U);
TS_ASSERT_EQUALS(it.getUint24LE(), 131583U);
#if defined(SCUMM_LITTLE_ENDIAN)
TS_ASSERT_EQUALS((it + 3).getUint32(), 4278256131U);
#elif defined(SCUMM_BIG_ENDIAN)
TS_ASSERT_EQUALS(it.getUint32(), 4278256131U);
#else
#error No endianness detected
#endif
TS_ASSERT_EQUALS(it.getInt32BE(), -16711165);
TS_ASSERT_EQUALS(it.getUint32BE(), 4278256131U);
TS_ASSERT_EQUALS((it + 3).getInt32LE(), -16711165);
TS_ASSERT_EQUALS((it + 3).getUint32LE(), 4278256131U);
}
void test_span_iterator_ptr() {
Foo foo[2];
foo[0].a = 1;
foo[1].a = 2;
const Common::Span<Foo> span(foo, 2);
Common::Span<Foo>::const_iterator it = span.cbegin();
TS_ASSERT_EQUALS(it->a, 1);
++it;
TS_ASSERT_EQUALS(it->a, 2);
TS_ASSERT_EQUALS(it[0].a, 2);
TS_ASSERT_EQUALS(it[-1].a, 1);
--it;
TS_ASSERT_EQUALS(it[1].a, 2);
}
void test_span_owner() {
Common::SpanOwner<Common::Span<byte> > owner;
owner->allocate(3);
owner[0] = 'a';
owner[1] = 'b';
owner[2] = 'c';
for (int i = 0; i < 3; ++i) {
TS_ASSERT_EQUALS(owner->getUint8At(i), 'a' + i);
TS_ASSERT_EQUALS((*owner)[i], 'a' + i);
}
{
Common::SpanOwner<Common::NamedSpan<byte> > owner2;
TS_ASSERT(owner2->data() == nullptr);
owner2->allocateFromSpan(*owner);
TS_ASSERT(owner2->data() != nullptr);
TS_ASSERT_DIFFERS(owner->data(), owner2->data());
for (int i = 0; i < 3; ++i) {
TS_ASSERT_EQUALS(owner2->getUint8At(i), 'a' + i);
TS_ASSERT_EQUALS((*owner2)[i], 'a' + i);
}
TS_ASSERT_EQUALS((bool)owner2, true);
owner2.release();
TS_ASSERT_EQUALS((bool)owner2, false);
}
{
Common::SpanOwner<Common::Span<byte> > owner2;
TS_ASSERT(owner2->data() == nullptr);
owner2 = owner;
TS_ASSERT(owner2->data() != nullptr);
TS_ASSERT_DIFFERS(owner->data(), owner2->data());
for (int i = 0; i < 3; ++i) {
TS_ASSERT_EQUALS(owner2->getUint8At(i), 'a' + i);
TS_ASSERT_EQUALS((*owner2)[i], 'a' + i);
}
TS_ASSERT_EQUALS((bool)owner2, true);
owner2.release();
TS_ASSERT_EQUALS((bool)owner2, false);
}
{
Common::SpanOwner<Common::Span<byte> > owner2;
TS_ASSERT_EQUALS((bool)owner, true);
void *dataPtr = owner->data();
owner2.moveFrom(owner);
TS_ASSERT_EQUALS((bool)owner, false);
TS_ASSERT(owner->data() == nullptr);
TS_ASSERT_EQUALS(owner2->data(), dataPtr);
// tests destruction of held pointer by reassignment
owner2 = owner;
// tests nullipotence of assignment to self
dataPtr = owner2->data();
owner2 = owner2;
TS_ASSERT(owner2->data() == dataPtr);
}
{
char *data = new char[6];
Common::strlcpy(data, "hello", 6);
const Common::SpanOwner<Common::Span<const char> > constOwner(Common::Span<const char>(data, 6));
TS_ASSERT_EQUALS((*constOwner)[0], 'h');
TS_ASSERT_EQUALS(constOwner->getUint8At(1), 'e');
TS_ASSERT_EQUALS(constOwner[2], 'l');
}
{
TS_ASSERT_EQUALS((bool)owner, false);
Common::SpanOwner<Common::Span<byte> > owner2(owner);
TS_ASSERT_EQUALS((bool)owner2, false);
}
{
owner->allocate(1);
TS_ASSERT_EQUALS((bool)owner, true);
Common::SpanOwner<Common::Span<byte> > owner2(owner);
TS_ASSERT_EQUALS((bool)owner2, true);
TS_ASSERT_DIFFERS(owner->data(), owner2->data());
}
{
TS_ASSERT_EQUALS((bool)owner, true);
void *dataPtr = owner->data();
TS_ASSERT_EQUALS(owner.release(), dataPtr);
TS_ASSERT_EQUALS((bool)owner, false);
}
}
void test_span_owner_named_span() {
Common::SpanOwner<Common::NamedSpan<byte> > owner;
owner->allocate(3, "foo");
owner[0] = 'a';
owner[1] = 'b';
owner[2] = 'c';
for (int i = 0; i < 3; ++i) {
TS_ASSERT_EQUALS(owner->getUint8At(i), 'a' + i);
TS_ASSERT_EQUALS((*owner)[i], 'a' + i);
}
TS_ASSERT(owner->name() == "foo");
{
Common::SpanOwner<Common::NamedSpan<byte> > owner2;
TS_ASSERT(owner2->data() == nullptr);
owner2->allocateFromSpan(*owner);
TS_ASSERT(owner2->data() != nullptr);
TS_ASSERT_DIFFERS(owner->data(), owner2->data());
TS_ASSERT(owner2->name() == "foo");
for (int i = 0; i < 3; ++i) {
TS_ASSERT_EQUALS(owner2->getUint8At(i), 'a' + i);
TS_ASSERT_EQUALS((*owner2)[i], 'a' + i);
}
TS_ASSERT_EQUALS((bool)owner2, true);
owner2.release();
TS_ASSERT_EQUALS((bool)owner2, false);
}
{
Common::SpanOwner<Common::NamedSpan<byte> > owner2;
TS_ASSERT_EQUALS((bool)owner, true);
void *dataPtr = owner->data();
owner2.moveFrom(owner);
TS_ASSERT_EQUALS((bool)owner, false);
TS_ASSERT(owner->data() == nullptr);
TS_ASSERT_EQUALS(owner2->data(), dataPtr);
// tests destruction of held pointer by reassignment
owner2 = owner;
}
{
char *data = new char[6];
Common::strlcpy(data, "hello", 6);
const Common::SpanOwner<Common::NamedSpan<const char> > constOwner(Common::NamedSpan<const char>(data, 6));
TS_ASSERT_EQUALS((*constOwner)[0], 'h');
TS_ASSERT_EQUALS(constOwner->getUint8At(1), 'e');
TS_ASSERT_EQUALS(constOwner[2], 'l');
}
{
TS_ASSERT_EQUALS((bool)owner, false);
Common::SpanOwner<Common::NamedSpan<byte> > owner2(owner);
TS_ASSERT_EQUALS((bool)owner2, false);
}
{
owner->allocate(1);
TS_ASSERT_EQUALS((bool)owner, true);
Common::SpanOwner<Common::NamedSpan<byte> > owner2(owner);
TS_ASSERT_EQUALS((bool)owner2, true);
TS_ASSERT_DIFFERS(owner->data(), owner2->data());
}
{
TS_ASSERT_EQUALS((bool)owner, true);
void *dataPtr = owner->data();
TS_ASSERT_EQUALS(owner.release(), dataPtr);
TS_ASSERT_EQUALS((bool)owner, false);
}
}
void test_span_allocate_from_stream() {
byte data[] = "hello";
Common::MemoryReadStream stream(data, sizeof(data));
Common::SpanOwner<Common::Span<byte> > owner;
owner->allocateFromStream(stream, 2);
TS_ASSERT(owner->data() != data);
TS_ASSERT_EQUALS(owner->size(), 2U);
TS_ASSERT_EQUALS(owner[0], 'h');
TS_ASSERT_EQUALS(owner[1], 'e');
owner.clear();
TS_ASSERT(owner->data() == nullptr);
stream.seek(0, SEEK_SET);
owner->allocateFromStream(stream);
TS_ASSERT(owner->data() != data);
TS_ASSERT_EQUALS(owner->size(), sizeof(data));
TS_ASSERT_EQUALS(owner[0], 'h');
TS_ASSERT_EQUALS(owner[1], 'e');
TS_ASSERT_EQUALS(owner[2], 'l');
TS_ASSERT_EQUALS(owner[3], 'l');
TS_ASSERT_EQUALS(owner[4], 'o');
Common::SpanOwner<Common::NamedSpan<const byte> > owner2;
stream.seek(0, SEEK_SET);
owner2->allocateFromStream(stream, Common::kSpanMaxSize, "streamname");
TS_ASSERT(owner2->data() != data);
TS_ASSERT_EQUALS(owner2->size(), sizeof(data));
TS_ASSERT_EQUALS(owner2[0], 'h');
TS_ASSERT_EQUALS(owner2[1], 'e');
TS_ASSERT_EQUALS(owner2[2], 'l');
TS_ASSERT_EQUALS(owner2[3], 'l');
TS_ASSERT_EQUALS(owner2[4], 'o');
TS_ASSERT_EQUALS(owner2->name(), "streamname");
}
void test_span_byte() {
{
byte data[] = { 'h', 'e', 'l', 'l', 'o' };
Common::Span<byte> span(data, sizeof(data));
TS_ASSERT_EQUALS(span.size(), sizeof(data));
TS_ASSERT_EQUALS(span.byteSize(), sizeof(data));
Common::Span<byte> other(span);
TS_ASSERT_EQUALS(span, other);
other.clear();
TS_ASSERT(span != other);
TS_ASSERT_EQUALS(span[0], 'h');
TS_ASSERT_EQUALS(span[1], 'e');
span[1] = 'o';
TS_ASSERT_EQUALS(span[1], 'o');
TS_ASSERT((bool)span);
span.clear();
TS_ASSERT(!(bool)span);
}
{
byte data[] = { 'h', 'e', 'l', 'l', 'o' };
const Common::Span<const byte> span(data, sizeof(data));
TS_ASSERT_EQUALS(span.size(), sizeof(data));
TS_ASSERT_EQUALS(span.byteSize(), sizeof(data));
const Common::Span<const byte> other(span);
TS_ASSERT_EQUALS(span, other);
TS_ASSERT_EQUALS(span[0], 'h');
TS_ASSERT_EQUALS(span[1], 'e');
}
}
void test_span_integers() {
const byte data[] = { 0xFF, 1, 2, 3, 2, 1, 0xFF };
Common::Span<const byte> span(data, sizeof(data));
TS_ASSERT_EQUALS(span[0], 255);
TS_ASSERT_EQUALS(span.getInt8At(0), -1);
TS_ASSERT_EQUALS(span.getUint8At(0), 255U);
TS_ASSERT_EQUALS(span.getInt16BEAt(0), -255);
TS_ASSERT_EQUALS(span.getUint16BEAt(0), 65281U);
TS_ASSERT_EQUALS(span.getInt16LEAt(5), -255);
TS_ASSERT_EQUALS(span.getUint16LEAt(5), 65281U);
TS_ASSERT_EQUALS(span.getUint24LEAt(0), 131583U);
TS_ASSERT_EQUALS(span.getInt32BEAt(0), -16711165);
TS_ASSERT_EQUALS(span.getUint32BEAt(0), 4278256131U);
TS_ASSERT_EQUALS(span.getInt32LEAt(3), -16711165);
TS_ASSERT_EQUALS(span.getUint32LEAt(3), 4278256131U);
#if defined(SCUMM_LITTLE_ENDIAN)
TS_ASSERT_EQUALS(span.getUint32At(3), 4278256131U);
#elif defined(SCUMM_BIG_ENDIAN)
TS_ASSERT_EQUALS(span.getUint32At(0), 4278256131U);
#else
#error No endianness detected
#endif
}
void test_span_string() {
char data[] = "hello";
Common::Span<char> span(data, sizeof(data));
TS_ASSERT_EQUALS(span[sizeof(data) - 1], '\0');
TS_ASSERT(span.getStringAt(0) == data);
TS_ASSERT(span.getStringAt(0, 2) == "he");
TS_ASSERT(span.getStringAt(2) == "llo");
TS_ASSERT(span.getStringAt(2, 3) == "llo");
span[3] = '\0';
TS_ASSERT(span.getStringAt(0) == "hel");
}
void test_span_unsafe_data() {
char data[] = "hello";
Common::Span<char> span(data, sizeof(data));
char *ptr = span.getUnsafeDataAt(0, 6);
TS_ASSERT_EQUALS(ptr, data);
ptr = span.getUnsafeDataAt(0);
TS_ASSERT_EQUALS(ptr, data);
const Common::Span<const char> span2(data, sizeof(data));
const char *ptr2 = span2.getUnsafeDataAt(0, 6);
TS_ASSERT_EQUALS(ptr2, data);
ptr2 = span2.getUnsafeDataAt(0);
TS_ASSERT_EQUALS(ptr2, data);
}
void test_span_subspan() {
{
byte data[] = { 1, 2, 3, 4, 5, 6 };
Common::Span<byte> span(data, sizeof(data));
TS_ASSERT_EQUALS(span.subspan(0).size(), sizeof(data) - 0);
TS_ASSERT_EQUALS(span.subspan(2).size(), sizeof(data) - 2);
TS_ASSERT_EQUALS(span.subspan(2, 2).size(), 2U);
TS_ASSERT_EQUALS(span.subspan<uint16>(0).size(), sizeof(data) / 2);
TS_ASSERT_EQUALS(span.subspan<uint16>(0).byteSize(), sizeof(data));
TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).size(), 1U);
TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).byteSize(), 2U);
#if defined(SCUMM_LITTLE_ENDIAN)
TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 4 << 8 | 3);
#elif defined(SCUMM_BIG_ENDIAN)
TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 3 << 8 | 4);
#else
#error No endianness detected
#endif
Common::Span<uint16> shortSpan = span.subspan<uint16>(0);
TS_ASSERT_EQUALS(shortSpan.byteSize(), span.byteSize());
TS_ASSERT(shortSpan.size() != span.size());
shortSpan[1] = 0xFFFF;
Common::Span<byte> byteSpan = shortSpan.subspan<byte>(1);
TS_ASSERT_EQUALS(byteSpan.size(), sizeof(data) - 1 * sizeof(uint16));
TS_ASSERT_EQUALS(byteSpan[0], 0xFF);
TS_ASSERT_EQUALS(byteSpan[1], 0xFF);
}
{
byte data[] = { 1, 2, 3, 4, 5, 6 };
const Common::Span<const byte> span(data, sizeof(data));
TS_ASSERT_EQUALS(span.subspan(0).size(), sizeof(data) - 0);
TS_ASSERT_EQUALS(span.subspan(2).size(), sizeof(data) - 2);
TS_ASSERT_EQUALS(span.subspan(2, 2).size(), 2U);
TS_ASSERT_EQUALS(span.subspan<uint16>(0).size(), sizeof(data) / 2);
TS_ASSERT_EQUALS(span.subspan<uint16>(0).byteSize(), sizeof(data));
TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).size(), 1U);
TS_ASSERT_EQUALS(span.subspan<uint16>(0, 2).byteSize(), 2U);
#if defined(SCUMM_LITTLE_ENDIAN)
TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 4 << 8 | 3);
#elif defined(SCUMM_BIG_ENDIAN)
TS_ASSERT_EQUALS(span.subspan<uint16>(0)[1], 3 << 8 | 4);
#else
#error No endianness detected
#endif
const Common::Span<uint16> shortSpan = span.subspan<uint16>(0);
TS_ASSERT_EQUALS(shortSpan.byteSize(), span.byteSize());
TS_ASSERT(shortSpan.size() != span.size());
Common::Span<byte> byteSpan = shortSpan.subspan<byte>(1);
TS_ASSERT_EQUALS(byteSpan.size(), sizeof(data) - 1 * sizeof(uint16));
TS_ASSERT_EQUALS(byteSpan[0], 3);
TS_ASSERT_EQUALS(byteSpan[1], 4);
}
}
void test_span_to_stream() {
const byte data[] = { 0, 1, 2, 3 };
Common::Span<const byte> span(data, sizeof(data));
{
Common::MemoryReadStream stream(span.toStream(1, 2));
byte out;
TS_ASSERT_EQUALS(stream.read(&out, 1), 1U);
TS_ASSERT_EQUALS(out, 1);
TS_ASSERT_EQUALS(stream.read(&out, 1), 1U);
TS_ASSERT_EQUALS(out, 2);
TS_ASSERT_EQUALS(stream.read(&out, 1), 0U);
}
{
Common::MemoryReadStream stream = span.toStream();
byte out;
TS_ASSERT_EQUALS(stream.read(&out, 1), 1U);
TS_ASSERT_EQUALS(out, 0);
TS_ASSERT_EQUALS(stream.read(&out, 1), 1U);
TS_ASSERT_EQUALS(out, 1);
TS_ASSERT_EQUALS(stream.read(&out, 1), 1U);
TS_ASSERT_EQUALS(out, 2);
TS_ASSERT_EQUALS(stream.read(&out, 1), 1U);
TS_ASSERT_EQUALS(out, 3);
TS_ASSERT_EQUALS(stream.read(&out, 1), 0U);
}
}
void test_span_copying() {
const byte data[] = { 0, 1, 2, 3, 4, 5 };
Common::Span<const byte> span(data, sizeof(data));
byte targetData[sizeof(data)] = {};
Common::Span<byte> target(targetData, sizeof(targetData));
span.copyDataTo(target);
for (uint i = 0; i < sizeof(data); ++i) {
TS_ASSERT_EQUALS(target[i], i);
}
byte out[sizeof(data)];
span.unsafeCopyDataTo(out);
for (uint i = 0; i < sizeof(data); ++i) {
TS_ASSERT_EQUALS(out[i], i);
}
}
void test_span_validation() {
byte data[6];
Common::Span<byte> span(data, sizeof(data));
TS_ASSERT(!span.checkInvalidBounds(0, 0));
TS_ASSERT(!span.checkInvalidBounds(0, 6));
TS_ASSERT(!span.checkInvalidBounds(2, 4));
TS_ASSERT(!span.checkInvalidBounds(4, 2));
TS_ASSERT(!span.checkInvalidBounds(6, 0));
TS_ASSERT(!span.checkInvalidBounds(2, -2));
TS_ASSERT(span.checkInvalidBounds(-2, 2)); // negative index disallowed
TS_ASSERT(span.checkInvalidBounds(6, 1)); // combined positive overflow (+7)
TS_ASSERT(span.checkInvalidBounds(2, -4)); // negative overflow (-2)
TS_ASSERT(span.checkInvalidBounds(0, 10)); // delta positive overflow
const Common::Span<byte>::difference_type big = 1L << (8 * sizeof(Common::Span<byte>::difference_type) - 1);
TS_ASSERT(span.checkInvalidBounds(big, 0));
TS_ASSERT(span.checkInvalidBounds(0, big));
TS_ASSERT(span.checkInvalidBounds(big, big));
}
void test_span_validation_message() {
byte data[1];
Common::Span<byte> span(data, sizeof(data));
Common::String source = span.name();
Common::String actual;
Common::String expected;
actual = span.getValidationMessage(12, 34, Common::kValidateRead);
expected = Common::String::format("Access violation reading %s: 12 + 34 > 1", source.c_str());
TS_ASSERT_EQUALS(actual, expected);
actual = span.getValidationMessage(23, 45, Common::kValidateWrite);
expected = Common::String::format("Access violation writing %s: 23 + 45 > 1", source.c_str());
TS_ASSERT_EQUALS(actual, expected);
actual = span.getValidationMessage(0, -56, Common::kValidateSeek);
expected = Common::String::format("Access violation seeking %s: 0 + -56 > 1", source.c_str());
TS_ASSERT_EQUALS(actual, expected);
}
void test_span_comparators() {
byte data[2];
Common::Span<const byte> span0(data, sizeof(data));
Common::Span<const byte> span1(data, sizeof(data));
Common::Span<const byte> span2(data, sizeof(data) - 1);
Common::Span<const byte> span3(data + 1, sizeof(data) - 1);
Common::Span<const byte> span4(data + 2, sizeof(data) - 2);
TS_ASSERT(span0 == span1);
TS_ASSERT(span0 != span2);
TS_ASSERT(span0 <= span1);
TS_ASSERT(span0 <= span3);
TS_ASSERT(span0 < span3);
TS_ASSERT(span3 < span4);
TS_ASSERT(span4 > span3);
TS_ASSERT(span3 > span0);
TS_ASSERT(span4 >= span4);
TS_ASSERT(span0 >= span1);
TS_ASSERT_EQUALS(span1 - span0, 0);
TS_ASSERT_EQUALS(span3 - span0, 1);
TS_ASSERT_EQUALS(span4 - span0, 2);
TS_ASSERT_EQUALS(span0 - span1, 0);
TS_ASSERT_EQUALS(span0 - span3, -1);
TS_ASSERT_EQUALS(span0 - span4, -2);
}
void test_named_span() {
byte data[6] = { 0, 1, 2, 3, 4, 5 };
Common::NamedSpan<byte> span(data, sizeof(data), "foo.data");
TS_ASSERT_EQUALS(span.name(), "foo.data");
Common::String actual;
Common::String expected;
actual = span.getValidationMessage(12, 34, Common::kValidateRead);
expected = "Access violation reading foo.data: 12 + 34 > 6 (abs: 12 + 34 > 6)";
TS_ASSERT_EQUALS(actual, expected);
{
Common::NamedSpan<byte> subspan = span.subspan(2);
expected = "Access violation reading foo.data: 23 + 45 > 4 (abs: 25 + 45 > 6)";
actual = subspan.getValidationMessage(23, 45, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
}
{
Common::NamedSpan<byte> subspan = span.subspan(2, Common::kSpanMaxSize, "new.data");
expected = "Access violation reading new.data: 0 + -56 > 4 (abs: 2 + -56 > 6)";
actual = subspan.getValidationMessage(0, -56, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
}
{
Common::NamedSpan<byte> subspan = span.subspan(2, Common::kSpanMaxSize, "new.data", 0);
expected = "Access violation reading new.data: 0 + -56 > 4 (abs: 0 + -56 > 4)";
actual = subspan.getValidationMessage(0, -56, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
}
Common::NamedSpan<byte> span2;
span = span2 = span;
TS_ASSERT_EQUALS(span2, span);
TS_ASSERT(span2.name() == span.name());
TS_ASSERT(span2.sourceByteOffset() == span.sourceByteOffset());
Common::Span<byte> superclassInstance;
superclassInstance = span;
TS_ASSERT_EQUALS(span, superclassInstance);
Common::Span<byte> subclassInstance(superclassInstance);
TS_ASSERT_EQUALS(subclassInstance, superclassInstance);
const Common::NamedSpan<const byte> constSpan(span);
{
Common::NamedSpan<const byte> subspan = constSpan.subspan(2);
expected = "Access violation reading foo.data: 23 + 45 > 4 (abs: 25 + 45 > 6)";
actual = subspan.getValidationMessage(23, 45, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
TS_ASSERT_EQUALS(subspan.sourceByteOffset(), 2U);
}
{
Common::NamedSpan<const byte> subspan = constSpan.subspan(2, Common::kSpanMaxSize, "new.data");
expected = "Access violation reading new.data: 0 + -56 > 4 (abs: 2 + -56 > 6)";
actual = subspan.getValidationMessage(0, -56, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
}
{
Common::NamedSpan<const byte> subspan = constSpan.subspan(2, Common::kSpanMaxSize, "new.data", 0);
expected = "Access violation reading new.data: 0 + -56 > 4 (abs: 0 + -56 > 4)";
actual = subspan.getValidationMessage(0, -56, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
}
{
Common::NamedSpan<const byte> subspan = constSpan.subspan(2, Common::kSpanMaxSize, "new.data", 0);
subspan.sourceByteOffset() = 2;
expected = "Access violation reading new.data: 0 + -56 > 4 (abs: 2 + -56 > 6)";
actual = subspan.getValidationMessage(0, -56, Common::kValidateRead);
TS_ASSERT_EQUALS(actual, expected);
}
{
Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, sizeof(data));
Common::File file;
file.open(stream, "test.txt");
Common::SpanOwner<Common::NamedSpan<const byte> > fileOwner;
fileOwner->allocateFromStream(file);
TS_ASSERT_EQUALS(fileOwner->size(), (uint)file.size());
file.close();
TS_ASSERT(fileOwner->name() == "test.txt");
for (uint i = 0; i < fileOwner->size(); ++i) {
TS_ASSERT_EQUALS(fileOwner->getInt8At(i), data[i]);
}
}
}
};