mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 05:30:33 +03:00

* Fix UndocumentedDeclarationCheck linting issue * Fix IfConstraintsIndentCheck linting issue * Address feedback * Fix publictests CI * Fix old (libdparse) D-Scanner linting warn
565 lines
14 KiB
D
565 lines
14 KiB
D
/**
|
|
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)();
|
|
}
|
|
}
|
|
}
|
|
}
|