blob: a6bce0ada231ac4fdda59d049b907180ebf9c76d [file] [log] [blame]
/**
For testing only.
Used with the dummy ranges for testing higher order ranges.
*/
module std.internal.test.dummyrange;
import std.meta;
import std.range.primitives;
import std.typecons;
enum RangeType
{
Input,
Forward,
Bidirectional,
Random
}
enum Length
{
Yes,
No
}
enum ReturnBy
{
Reference,
Value
}
import std.traits : isArray;
// Range that's useful for testing other higher order ranges,
// can be parametrized with attributes. It just dumbs down an array of
// numbers 1 .. 10.
struct DummyRange(ReturnBy _r, Length _l, RangeType _rt, T = uint[])
if (isArray!T)
{
private static immutable uinttestData =
[1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U];
// These enums are so that the template params are visible outside
// this instantiation.
enum r = _r;
enum l = _l;
enum rt = _rt;
static if (is(T == uint[]))
{
T arr = uinttestData;
}
else
{
T arr;
}
alias RetType = ElementType!(T);
alias RetTypeNoAutoDecoding = ElementEncodingType!(T);
void reinit()
{
// Workaround for DMD bug 4378
static if (is(T == uint[]))
{
arr = uinttestData.dup;
}
}
void popFront()
{
arr = arr[1..$];
}
@property bool empty() const
{
return arr.length == 0;
}
static if (r == ReturnBy.Reference)
{
@property ref inout(RetType) front() inout
{
return arr[0];
}
}
else
{
@property RetType front() const
{
return arr[0];
}
@property void front(RetTypeNoAutoDecoding val)
{
arr[0] = val;
}
}
static if (rt >= RangeType.Forward)
{
@property typeof(this) save()
{
return this;
}
}
static if (rt >= RangeType.Bidirectional)
{
void popBack()
{
arr = arr[0..$ - 1];
}
static if (r == ReturnBy.Reference)
{
@property ref inout(RetType) back() inout
{
return arr[$ - 1];
}
}
else
{
@property RetType back() const
{
return arr[$ - 1];
}
@property void back(RetTypeNoAutoDecoding val)
{
arr[$ - 1] = val;
}
}
}
static if (rt >= RangeType.Random)
{
static if (r == ReturnBy.Reference)
{
ref inout(RetType) opIndex(size_t index) inout
{
return arr[index];
}
}
else
{
RetType opIndex(size_t index) const
{
return arr[index];
}
RetType opIndexAssign(RetTypeNoAutoDecoding val, size_t index)
{
return arr[index] = val;
}
RetType opIndexOpAssign(string op)(RetTypeNoAutoDecoding value, size_t index)
{
mixin("return arr[index] " ~ op ~ "= value;");
}
RetType opIndexUnary(string op)(size_t index)
{
mixin("return " ~ op ~ "arr[index];");
}
}
typeof(this) opSlice(size_t lower, size_t upper)
{
auto ret = this;
ret.arr = arr[lower .. upper];
return ret;
}
typeof(this) opSlice()
{
return this;
}
}
static if (l == Length.Yes)
{
@property size_t length() const
{
return arr.length;
}
alias opDollar = length;
}
}
enum dummyLength = 10;
alias AllDummyRanges = AliasSeq!(
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward),
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional),
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random),
DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward),
DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random),
DummyRange!(ReturnBy.Value, Length.No, RangeType.Input),
DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward),
DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional)
);
template AllDummyRangesType(T)
{
alias AllDummyRangesType = AliasSeq!(
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward, T),
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional, T),
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T),
DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward, T),
DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional, T),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input, T),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward, T),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional, T),
DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T),
DummyRange!(ReturnBy.Value, Length.No, RangeType.Input, T),
DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward, T),
DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional, T)
);
}
/**
Tests whether forward, bidirectional and random access properties are
propagated properly from the base range(s) R to the higher order range
H. Useful in combination with DummyRange for testing several higher
order ranges.
*/
template propagatesRangeType(H, R...)
{
static if (allSatisfy!(isRandomAccessRange, R))
enum bool propagatesRangeType = isRandomAccessRange!H;
else static if (allSatisfy!(isBidirectionalRange, R))
enum bool propagatesRangeType = isBidirectionalRange!H;
else static if (allSatisfy!(isForwardRange, R))
enum bool propagatesRangeType = isForwardRange!H;
else
enum bool propagatesRangeType = isInputRange!H;
}
template propagatesLength(H, R...)
{
static if (allSatisfy!(hasLength, R))
enum bool propagatesLength = hasLength!H;
else
enum bool propagatesLength = !hasLength!H;
}
/**
Reference type input range
*/
class ReferenceInputRange(T)
{
import std.array : array;
this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);}
final @property ref T front(){return _payload.front;}
final void popFront(){_payload.popFront();}
final @property bool empty(){return _payload.empty;}
protected T[] _payload;
}
/**
Infinite input range
*/
class ReferenceInfiniteInputRange(T)
{
this(T first = T.init) {_val = first;}
final @property T front(){return _val;}
final void popFront(){++_val;}
enum bool empty = false;
protected T _val;
}
/**
Reference forward range
*/
class ReferenceForwardRange(T) : ReferenceInputRange!T
{
this(Range)(Range r) if (isInputRange!Range) {super(r);}
final @property auto save(this This)() {return new This( _payload);}
}
/**
Infinite forward range
*/
class ReferenceInfiniteForwardRange(T) : ReferenceInfiniteInputRange!T
{
this(T first = T.init) {super(first);}
final @property ReferenceInfiniteForwardRange save()
{return new ReferenceInfiniteForwardRange!T(_val);}
}
/**
Reference bidirectional range
*/
class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T
{
this(Range)(Range r) if (isInputRange!Range) {super(r);}
final @property ref T back(){return _payload.back;}
final void popBack(){_payload.popBack();}
}
@safe unittest
{
static assert(isInputRange!(ReferenceInputRange!int));
static assert(isInputRange!(ReferenceInfiniteInputRange!int));
static assert(isForwardRange!(ReferenceForwardRange!int));
static assert(isForwardRange!(ReferenceInfiniteForwardRange!int));
static assert(isBidirectionalRange!(ReferenceBidirectionalRange!int));
}
private:
pure struct Cmp(T)
if (is(T == uint))
{
static auto iota(size_t low = 1, size_t high = 11)
{
import std.range : iota;
return iota(cast(uint) low, cast(uint) high);
}
static void initialize(ref uint[] arr)
{
import std.array : array;
arr = iota().array;
}
static bool function(uint,uint) cmp = function(uint a, uint b) { return a == b; };
enum dummyValue = 1337U;
enum dummyValueRslt = 1337U * 2;
}
pure struct Cmp(T)
if (is(T == double))
{
import std.math : approxEqual;
static auto iota(size_t low = 1, size_t high = 11)
{
import std.range : iota;
return iota(cast(double) low, cast(double) high, 1.0);
}
static void initialize(ref double[] arr)
{
import std.array : array;
arr = iota().array;
}
alias cmp = approxEqual!(double,double);
enum dummyValue = 1337.0;
enum dummyValueRslt = 1337.0 * 2.0;
}
struct TestFoo
{
int a;
bool opEquals(const ref TestFoo other) const
{
return this.a == other.a;
}
TestFoo opBinary(string op)(TestFoo other)
{
TestFoo ret = this;
mixin("ret.a " ~ op ~ "= other.a;");
return ret;
}
TestFoo opOpAssign(string op)(TestFoo other)
{
mixin("this.a " ~ op ~ "= other.a;");
return this;
}
}
pure struct Cmp(T)
if (is(T == TestFoo))
{
import std.math : approxEqual;
static auto iota(size_t low = 1, size_t high = 11)
{
import std.algorithm.iteration : map;
import std.range : iota;
return iota(cast(int) low, cast(int) high).map!(a => TestFoo(a));
}
static void initialize(ref TestFoo[] arr)
{
import std.array : array;
arr = iota().array;
}
static bool function(TestFoo,TestFoo) cmp = function(TestFoo a, TestFoo b)
{
return a.a == b.a;
};
@property static TestFoo dummyValue()
{
return TestFoo(1337);
}
@property static TestFoo dummyValueRslt()
{
return TestFoo(1337 * 2);
}
}
@system unittest
{
import std.algorithm.comparison : equal;
import std.range : iota, retro, repeat;
import std.traits : Unqual;
static void testInputRange(T,Cmp)()
{
T it;
Cmp.initialize(it.arr);
for (size_t numRuns = 0; numRuns < 2; ++numRuns)
{
if (numRuns == 1)
{
static if (is(Unqual!(ElementType!(T)) == uint))
{
it.reinit();
}
Cmp.initialize(it.arr);
}
assert(equal!(Cmp.cmp)(it, Cmp.iota(1, 11)));
static if (hasLength!T)
{
assert(it.length == 10);
}
assert(!Cmp.cmp(it.front, Cmp.dummyValue));
auto s = it.front;
it.front = Cmp.dummyValue;
assert(Cmp.cmp(it.front, Cmp.dummyValue));
it.front = s;
auto cmp = Cmp.iota(1,11);
size_t jdx = 0;
while (!it.empty && !cmp.empty)
{
static if (hasLength!T)
{
assert(it.length == 10 - jdx);
}
assert(Cmp.cmp(it.front, cmp.front));
it.popFront();
cmp.popFront();
++jdx;
}
assert(it.empty);
assert(cmp.empty);
}
}
static void testForwardRange(T,Cmp)()
{
T it;
Cmp.initialize(it.arr);
auto s = it.save();
s.popFront();
assert(!Cmp.cmp(s.front, it.front));
}
static void testBidirectionalRange(T,Cmp)()
{
T it;
Cmp.initialize(it.arr);
assert(equal!(Cmp.cmp)(it.retro, Cmp.iota().retro));
auto s = it.back;
assert(!Cmp.cmp(s, Cmp.dummyValue));
it.back = Cmp.dummyValue;
assert( Cmp.cmp(it.back, Cmp.dummyValue));
it.back = s;
}
static void testRandomAccessRange(T,Cmp)()
{
T it;
Cmp.initialize(it.arr);
size_t idx = 0;
foreach (jt; it)
{
assert(it[idx] == jt);
T copy = it[idx .. $];
auto cmp = Cmp.iota(idx + 1, it.length + 1);
assert(equal!(Cmp.cmp)(copy, cmp));
++idx;
}
{
auto copy = it;
copy.arr = it.arr.dup;
for (size_t i = 0; i < copy.length; ++i)
{
copy[i] = Cmp.dummyValue;
copy[i] += Cmp.dummyValue;
}
assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length)));
}
static if (it.r == ReturnBy.Reference)
{
T copy;
copy.arr = it.arr.dup;
for (size_t i = 0; i < copy.length; ++i)
{
copy[i] = Cmp.dummyValue;
copy[i] += Cmp.dummyValue;
}
assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length)));
}
}
import std.meta : AliasSeq;
foreach (S; AliasSeq!(uint, double, TestFoo))
{
foreach (T; AllDummyRangesType!(S[]))
{
testInputRange!(T,Cmp!S)();
static if (isForwardRange!T)
{
testForwardRange!(T,Cmp!S)();
}
static if (isBidirectionalRange!T)
{
testBidirectionalRange!(T,Cmp!S)();
}
static if (isRandomAccessRange!T)
{
testRandomAccessRange!(T,Cmp!S)();
}
}
}
}