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 {
saveDummyRange100 @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 : approxEqual;
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 = approxEqual!(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 import std.math : approxEqual;
390
391 static auto iota(size_t low = 1, size_t high = 11)
392 {
393 import std.algorithm.iteration : map;
394 import std.range : iota;
395 return iota(cast(int) low, cast(int) high).map!(a => TestFoo(a));
396 }
397
398 static void initialize(ref TestFoo[] arr)
399 {
400 import std.array : array;
401 arr = iota().array;
402 }
403
404 static bool function(TestFoo,TestFoo) cmp = function(TestFoo a, TestFoo b)
405 {
406 return a.a == b.a;
407 };
408
409 @property static TestFoo dummyValue()
410 {
411 return TestFoo(1337);
412 }
413
414 @property static TestFoo dummyValueRslt()
415 {
416 return TestFoo(1337 * 2);
417 }
418 }
419
420 @system unittest
421 {
422 import std.algorithm.comparison : equal;
423 import std.range : iota, retro, repeat;
424 import std.traits : Unqual;
425
testInputRange(T,Cmp)426 static void testInputRange(T,Cmp)()
427 {
428 T it;
429 Cmp.initialize(it.arr);
430 for (size_t numRuns = 0; numRuns < 2; ++numRuns)
431 {
432 if (numRuns == 1)
433 {
434 static if (is(Unqual!(ElementType!(T)) == uint))
435 {
436 it.reinit();
437 }
438
439 Cmp.initialize(it.arr);
440 }
441
442 assert(equal!(Cmp.cmp)(it, Cmp.iota(1, 11)));
443
444 static if (hasLength!T)
445 {
446 assert(it.length == 10);
447 }
448
449 assert(!Cmp.cmp(it.front, Cmp.dummyValue));
450 auto s = it.front;
451 it.front = Cmp.dummyValue;
452 assert(Cmp.cmp(it.front, Cmp.dummyValue));
453 it.front = s;
454
455 auto cmp = Cmp.iota(1,11);
456
457 size_t jdx = 0;
458 while (!it.empty && !cmp.empty)
459 {
460 static if (hasLength!T)
461 {
462 assert(it.length == 10 - jdx);
463 }
464
465 assert(Cmp.cmp(it.front, cmp.front));
466 it.popFront();
467 cmp.popFront();
468
469 ++jdx;
470 }
471
472 assert(it.empty);
473 assert(cmp.empty);
474 }
475
476 }
477
testForwardRange(T,Cmp)478 static void testForwardRange(T,Cmp)()
479 {
480 T it;
481 Cmp.initialize(it.arr);
482 auto s = it.save();
483 s.popFront();
484 assert(!Cmp.cmp(s.front, it.front));
485 }
486
testBidirectionalRange(T,Cmp)487 static void testBidirectionalRange(T,Cmp)()
488 {
489 T it;
490 Cmp.initialize(it.arr);
491 assert(equal!(Cmp.cmp)(it.retro, Cmp.iota().retro));
492
493 auto s = it.back;
494 assert(!Cmp.cmp(s, Cmp.dummyValue));
495 it.back = Cmp.dummyValue;
496 assert( Cmp.cmp(it.back, Cmp.dummyValue));
497 it.back = s;
498 }
499
testRandomAccessRange(T,Cmp)500 static void testRandomAccessRange(T,Cmp)()
501 {
502 T it;
503 Cmp.initialize(it.arr);
504 size_t idx = 0;
505 foreach (jt; it)
506 {
507 assert(it[idx] == jt);
508
509 T copy = it[idx .. $];
510 auto cmp = Cmp.iota(idx + 1, it.length + 1);
511 assert(equal!(Cmp.cmp)(copy, cmp));
512
513 ++idx;
514 }
515
516 {
517 auto copy = it;
518 copy.arr = it.arr.dup;
519 for (size_t i = 0; i < copy.length; ++i)
520 {
521 copy[i] = Cmp.dummyValue;
522 copy[i] += Cmp.dummyValue;
523 }
524 assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length)));
525 }
526
527 static if (it.r == ReturnBy.Reference)
528 {
529 T copy;
530 copy.arr = it.arr.dup;
531 for (size_t i = 0; i < copy.length; ++i)
532 {
533 copy[i] = Cmp.dummyValue;
534 copy[i] += Cmp.dummyValue;
535 }
536
537 assert(equal!(Cmp.cmp)(copy, Cmp.dummyValueRslt.repeat(copy.length)));
538 }
539 }
540
541 import std.meta : AliasSeq;
542
543 foreach (S; AliasSeq!(uint, double, TestFoo))
544 {
545 foreach (T; AllDummyRangesType!(S[]))
546 {
547 testInputRange!(T,Cmp!S)();
548
549 static if (isForwardRange!T)
550 {
551 testForwardRange!(T,Cmp!S)();
552 }
553
554 static if (isBidirectionalRange!T)
555 {
556 testBidirectionalRange!(T,Cmp!S)();
557 }
558
559 static if (isRandomAccessRange!T)
560 {
561 testRandomAccessRange!(T,Cmp!S)();
562 }
563 }
564 }
565 }
566