/** 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.operations : isClose; 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 = isClose!(double,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)) { 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; 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(immutable ElementType!(T) == immutable 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; static 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)(); } } } }