diff options
Diffstat (limited to 'libphobos/src/std/internal/test/dummyrange.d')
-rw-r--r-- | libphobos/src/std/internal/test/dummyrange.d | 565 |
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)(); + } + } + } +} |