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