| /** | |
| 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)(); | |
| } | |
| } | |
| } | |
| } |