aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/internal/test/dummyrange.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/internal/test/dummyrange.d')
-rw-r--r--libphobos/src/std/internal/test/dummyrange.d565
1 files changed, 565 insertions, 0 deletions
diff --git a/libphobos/src/std/internal/test/dummyrange.d b/libphobos/src/std/internal/test/dummyrange.d
new file mode 100644
index 0000000..a6bce0a
--- /dev/null
+++ b/libphobos/src/std/internal/test/dummyrange.d
@@ -0,0 +1,565 @@
+/**
+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)();
+ }
+ }
+ }
+}