1 /**
2 For testing only.
3 Used with the dummy ranges for testing higher order ranges.
4 */
5 module std.internal.test.dummyrange;
6 
7 import std.meta;
8 import std.range.primitives;
9 import std.typecons;
10 
11 enum RangeType
12 {
13     Input,
14     Forward,
15     Bidirectional,
16     Random
17 }
18 
19 enum Length
20 {
21     Yes,
22     No
23 }
24 
25 enum ReturnBy
26 {
27     Reference,
28     Value
29 }
30 
31 import std.traits : isArray;
32 
33 // Range that's useful for testing other higher order ranges,
34 // can be parametrized with attributes.  It just dumbs down an array of
35 // numbers 1 .. 10.
36 struct DummyRange(ReturnBy _r, Length _l, RangeType _rt, T = uint[])
37 if (isArray!T)
38 {
39     private static immutable uinttestData =
40         [1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U];
41     // These enums are so that the template params are visible outside
42     // this instantiation.
43     enum r = _r;
44     enum l = _l;
45     enum rt = _rt;
46 
47     static if (is(T == uint[]))
48     {
49         T arr = uinttestData;
50     }
51     else
52     {
53         T arr;
54     }
55 
56     alias RetType = ElementType!(T);
57     alias RetTypeNoAutoDecoding = ElementEncodingType!(T);
58 
reinitDummyRange59     void reinit()
60     {
61         // Workaround for DMD bug 4378
62         static if (is(T == uint[]))
63         {
64             arr = uinttestData.dup;
65         }
66     }
67 
popFrontDummyRange68     void popFront()
69     {
70         arr = arr[1..$];
71     }
72 
emptyDummyRange73     @property bool empty() const
74     {
75         return arr.length == 0;
76     }
77 
78     static if (r == ReturnBy.Reference)
79     {
inoutDummyRange80         @property ref inout(RetType) front() inout
81         {
82             return arr[0];
83         }
84     }
85     else
86     {
frontDummyRange87         @property RetType front() const
88         {
89             return arr[0];
90         }
91 
frontDummyRange92         @property void front(RetTypeNoAutoDecoding val)
93         {
94             arr[0] = val;
95         }
96     }
97 
98     static if (rt >= RangeType.Forward)
99     {
typeofDummyRange100         @property typeof(this) save()
101         {
102             return this;
103         }
104     }
105 
106     static if (rt >= RangeType.Bidirectional)
107     {
popBackDummyRange108         void popBack()
109         {
110             arr = arr[0..$ - 1];
111         }
112 
113         static if (r == ReturnBy.Reference)
114         {
inoutDummyRange115             @property ref inout(RetType) back() inout
116             {
117                 return arr[$ - 1];
118             }
119         }
120         else
121         {
backDummyRange122             @property RetType back() const
123             {
124                 return arr[$ - 1];
125             }
126 
backDummyRange127             @property void back(RetTypeNoAutoDecoding val)
128             {
129                 arr[$ - 1] = val;
130             }
131         }
132     }
133 
134     static if (rt >= RangeType.Random)
135     {
136         static if (r == ReturnBy.Reference)
137         {
inoutDummyRange138             ref inout(RetType) opIndex(size_t index) inout
139             {
140                 return arr[index];
141             }
142         }
143         else
144         {
opIndexDummyRange145             RetType opIndex(size_t index) const
146             {
147                 return arr[index];
148             }
149 
opIndexAssignDummyRange150             RetType opIndexAssign(RetTypeNoAutoDecoding val, size_t index)
151             {
152                 return arr[index] = val;
153             }
154 
opIndexOpAssignDummyRange155             RetType opIndexOpAssign(string op)(RetTypeNoAutoDecoding value, size_t index)
156             {
157                 mixin("return arr[index] " ~ op ~ "= value;");
158             }
159 
opIndexUnaryDummyRange160             RetType opIndexUnary(string op)(size_t index)
161             {
162                 mixin("return " ~ op ~ "arr[index];");
163             }
164         }
165 
opSliceDummyRange166         typeof(this) opSlice(size_t lower, size_t upper)
167         {
168             auto ret = this;
169             ret.arr = arr[lower .. upper];
170             return ret;
171         }
172 
opSliceDummyRange173         typeof(this) opSlice()
174         {
175             return this;
176         }
177     }
178 
179     static if (l == Length.Yes)
180     {
lengthDummyRange181         @property size_t length() const
182         {
183             return arr.length;
184         }
185 
186         alias opDollar = length;
187     }
188 }
189 
190 enum dummyLength = 10;
191 
192 alias AllDummyRanges = AliasSeq!(
193     DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward),
194     DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional),
195     DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random),
196     DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward),
197     DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional),
198     DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input),
199     DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward),
200     DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional),
201     DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random),
202     DummyRange!(ReturnBy.Value, Length.No, RangeType.Input),
203     DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward),
204     DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional)
205 );
206 
AllDummyRangesType(T)207 template AllDummyRangesType(T)
208 {
209     alias AllDummyRangesType = AliasSeq!(
210         DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward, T),
211         DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional, T),
212         DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T),
213         DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward, T),
214         DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional, T),
215         DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input, T),
216         DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward, T),
217         DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional, T),
218         DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T),
219         DummyRange!(ReturnBy.Value, Length.No, RangeType.Input, T),
220         DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward, T),
221         DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional, T)
222     );
223 }
224 
225 /**
226 Tests whether forward, bidirectional and random access properties are
227 propagated properly from the base range(s) R to the higher order range
228 H.  Useful in combination with DummyRange for testing several higher
229 order ranges.
230 */
propagatesRangeType(H,R...)231 template propagatesRangeType(H, R...)
232 {
233     static if (allSatisfy!(isRandomAccessRange, R))
234         enum bool propagatesRangeType = isRandomAccessRange!H;
235     else static if (allSatisfy!(isBidirectionalRange, R))
236         enum bool propagatesRangeType = isBidirectionalRange!H;
237     else static if (allSatisfy!(isForwardRange, R))
238         enum bool propagatesRangeType = isForwardRange!H;
239     else
240         enum bool propagatesRangeType = isInputRange!H;
241 }
242 
propagatesLength(H,R...)243 template propagatesLength(H, R...)
244 {
245     static if (allSatisfy!(hasLength, R))
246         enum bool propagatesLength = hasLength!H;
247     else
248         enum bool propagatesLength = !hasLength!H;
249 }
250 
251 /**
252 Reference type input range
253 */
ReferenceInputRange(T)254 class ReferenceInputRange(T)
255 {
256     import std.array : array;
257 
258     this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);}
259     final @property ref T front(){return _payload.front;}
260     final void popFront(){_payload.popFront();}
261     final @property bool empty(){return _payload.empty;}
262     protected T[] _payload;
263 }
264 
265 /**
266 Infinite input range
267 */
ReferenceInfiniteInputRange(T)268 class ReferenceInfiniteInputRange(T)
269 {
270     this(T first = T.init) {_val = first;}
271     final @property T front(){return _val;}
272     final void popFront(){++_val;}
273     enum bool empty = false;
274     protected T _val;
275 }
276 
277 /**
278 Reference forward range
279 */
280 class ReferenceForwardRange(T) : ReferenceInputRange!T
281 {
282     this(Range)(Range r) if (isInputRange!Range) {super(r);}
save(this This)283     final @property auto save(this This)() {return new This( _payload);}
284 }
285 
286 /**
287 Infinite forward range
288 */
289 class ReferenceInfiniteForwardRange(T) : ReferenceInfiniteInputRange!T
290 {
291     this(T first = T.init) {super(first);}
save()292     final @property ReferenceInfiniteForwardRange save()
293     {return new ReferenceInfiniteForwardRange!T(_val);}
294 }
295 
296 /**
297 Reference bidirectional range
298 */
299 class ReferenceBidirectionalRange(T) : ReferenceForwardRange!T
300 {
301     this(Range)(Range r) if (isInputRange!Range) {super(r);}
back()302     final @property ref T back(){return _payload.back;}
popBack()303     final void popBack(){_payload.popBack();}
304 }
305 
306 @safe unittest
307 {
308     static assert(isInputRange!(ReferenceInputRange!int));
309     static assert(isInputRange!(ReferenceInfiniteInputRange!int));
310 
311     static assert(isForwardRange!(ReferenceForwardRange!int));
312     static assert(isForwardRange!(ReferenceInfiniteForwardRange!int));
313 
314     static assert(isBidirectionalRange!(ReferenceBidirectionalRange!int));
315 }
316 
317 private:
318 
319 pure struct Cmp(T)
320 if (is(T == uint))
321 {
322     static auto iota(size_t low = 1, size_t high = 11)
323     {
324         import std.range : iota;
325         return iota(cast(uint) low, cast(uint) high);
326     }
327 
initialize(ref uint[]arr)328     static void initialize(ref uint[] arr)
329     {
330         import std.array : array;
331         arr = iota().array;
332     }
333 
function(uint,uint)334     static bool function(uint,uint) cmp = function(uint a, uint b) { return a == b; };
335 
336     enum dummyValue = 1337U;
337     enum dummyValueRslt = 1337U * 2;
338 }
339 
340 pure struct Cmp(T)
341 if (is(T == double))
342 {
343     import std.math.operations : isClose;
344 
345     static auto iota(size_t low = 1, size_t high = 11)
346     {
347         import std.range : iota;
348         return iota(cast(double) low, cast(double) high, 1.0);
349     }
350 
initialize(ref double[]arr)351     static void initialize(ref double[] arr)
352     {
353         import std.array : array;
354         arr = iota().array;
355     }
356 
357     alias cmp = isClose!(double,double,double);
358 
359     enum dummyValue = 1337.0;
360     enum dummyValueRslt = 1337.0 * 2.0;
361 }
362 
363 struct TestFoo
364 {
365     int a;
366 
opEqualsTestFoo367     bool opEquals(const ref TestFoo other) const
368     {
369         return this.a == other.a;
370     }
371 
opBinaryTestFoo372     TestFoo opBinary(string op)(TestFoo other)
373     {
374         TestFoo ret = this;
375         mixin("ret.a " ~ op ~ "= other.a;");
376         return ret;
377     }
378 
opOpAssignTestFoo379     TestFoo opOpAssign(string op)(TestFoo other)
380     {
381         mixin("this.a " ~ op ~ "= other.a;");
382         return this;
383     }
384 }
385 
Cmp(T)386 pure struct Cmp(T)
387 if (is(T == TestFoo))
388 {
389     static auto iota(size_t low = 1, size_t high = 11)
390     {
391         import std.algorithm.iteration : map;
392         import std.range : iota;
393         return iota(cast(int) low, cast(int) high).map!(a => TestFoo(a));
394     }
395 
396     static void initialize(ref TestFoo[] arr)
397     {
398         import std.array : array;
399         arr = iota().array;
400     }
401 
402     static bool function(TestFoo,TestFoo) cmp = function(TestFoo a, TestFoo b)
403     {
404         return a.a == b.a;
405     };
406 
407     @property static TestFoo dummyValue()
408     {
409         return TestFoo(1337);
410     }
411 
412     @property static TestFoo dummyValueRslt()
413     {
414         return TestFoo(1337 * 2);
415     }
416 }
417 
418 @system unittest
419 {
420     import std.algorithm.comparison : equal;
421     import std.range : iota, retro, repeat;
422 
testInputRange(T,Cmp)423     static void testInputRange(T,Cmp)()
424     {
425         T it;
426         Cmp.initialize(it.arr);
427         for (size_t numRuns = 0; numRuns < 2; ++numRuns)
428         {
429             if (numRuns == 1)
430             {
431                 static if (is(immutable ElementType!(T) == immutable uint))
432                 {
433                     it.reinit();
434                 }
435 
436                 Cmp.initialize(it.arr);
437             }
438 
439             assert(equal!(Cmp.cmp)(it, Cmp.iota(1, 11)));
440 
441             static if (hasLength!T)
442             {
443                 assert(it.length == 10);
444             }
445 
446             assert(!Cmp.cmp(it.front, Cmp.dummyValue));
447             auto s = it.front;
448             it.front = Cmp.dummyValue;
449             assert(Cmp.cmp(it.front, Cmp.dummyValue));
450             it.front = s;
451 
452             auto cmp = Cmp.iota(1,11);
453 
454             size_t jdx = 0;
455             while (!it.empty && !cmp.empty)
456             {
457                 static if (hasLength!T)
458                 {
459                     assert(it.length == 10 - jdx);
460                 }
461 
462                 assert(Cmp.cmp(it.front, cmp.front));
463                 it.popFront();
464                 cmp.popFront();
465 
466                 ++jdx;
467             }
468 
469             assert(it.empty);
470             assert(cmp.empty);
471         }
472 
473     }
474 
testForwardRange(T,Cmp)475     static void testForwardRange(T,Cmp)()
476     {
477         T it;
478         Cmp.initialize(it.arr);
479         auto s = it.save();
480         s.popFront();
481         assert(!Cmp.cmp(s.front, it.front));
482     }
483 
testBidirectionalRange(T,Cmp)484     static void testBidirectionalRange(T,Cmp)()
485     {
486         T it;
487         Cmp.initialize(it.arr);
488         assert(equal!(Cmp.cmp)(it.retro, Cmp.iota().retro));
489 
490         auto s = it.back;
491         assert(!Cmp.cmp(s, Cmp.dummyValue));
492         it.back = Cmp.dummyValue;
493         assert( Cmp.cmp(it.back, Cmp.dummyValue));
494         it.back = s;
495     }
496 
testRandomAccessRange(T,Cmp)497     static void testRandomAccessRange(T,Cmp)()
498     {
499         T it;
500         Cmp.initialize(it.arr);
501         size_t idx = 0;
502         foreach (jt; it)
503         {
504             assert(it[idx] == jt);
505 
506             T copy = it[idx .. $];
507             auto cmp = Cmp.iota(idx + 1, it.length + 1);
508             assert(equal!(Cmp.cmp)(copy, cmp));
509 
510             ++idx;
511         }
512 
513         {
514             auto copy = it;
515             copy.arr = it.arr.dup;
516             for (size_t i = 0; i < copy.length; ++i)
517             {
518                 copy[i] = Cmp.dummyValue;
519                 copy[i] += Cmp.dummyValue;
520             }
521             assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length)));
522         }
523 
524         static if (it.r == ReturnBy.Reference)
525         {
526             T copy;
527             copy.arr = it.arr.dup;
528             for (size_t i = 0; i < copy.length; ++i)
529             {
530                 copy[i] = Cmp.dummyValue;
531                 copy[i] += Cmp.dummyValue;
532             }
533 
534             assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length)));
535         }
536     }
537 
538     import std.meta : AliasSeq;
539 
540     static foreach (S; AliasSeq!(uint, double, TestFoo))
541     {
542         foreach (T; AllDummyRangesType!(S[]))
543         {
544             testInputRange!(T,Cmp!S)();
545 
546             static if (isForwardRange!T)
547             {
548                 testForwardRange!(T,Cmp!S)();
549             }
550 
551             static if (isBidirectionalRange!T)
552             {
553                 testBidirectionalRange!(T,Cmp!S)();
554             }
555 
556             static if (isRandomAccessRange!T)
557             {
558                 testRandomAccessRange!(T,Cmp!S)();
559             }
560         }
561     }
562 }
563