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 59 void reinit() 60 { 61 // Workaround for DMD bug 4378 62 static if (is(T == uint[])) 63 { 64 arr = uinttestData.dup; 65 } 66 } 67 68 void popFront() 69 { 70 arr = arr[1..$]; 71 } 72 73 @property bool empty() const 74 { 75 return arr.length == 0; 76 } 77 78 static if (r == ReturnBy.Reference) 79 { 80 @property ref inout(RetType) front() inout 81 { 82 return arr[0]; 83 } 84 } 85 else 86 { 87 @property RetType front() const 88 { 89 return arr[0]; 90 } 91 92 @property void front(RetTypeNoAutoDecoding val) 93 { 94 arr[0] = val; 95 } 96 } 97 98 static if (rt >= RangeType.Forward) 99 { 100 @property typeof(this) save() 101 { 102 return this; 103 } 104 } 105 106 static if (rt >= RangeType.Bidirectional) 107 { 108 void popBack() 109 { 110 arr = arr[0..$ - 1]; 111 } 112 113 static if (r == ReturnBy.Reference) 114 { 115 @property ref inout(RetType) back() inout 116 { 117 return arr[$ - 1]; 118 } 119 } 120 else 121 { 122 @property RetType back() const 123 { 124 return arr[$ - 1]; 125 } 126 127 @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 { 138 ref inout(RetType) opIndex(size_t index) inout 139 { 140 return arr[index]; 141 } 142 } 143 else 144 { 145 RetType opIndex(size_t index) const 146 { 147 return arr[index]; 148 } 149 150 RetType opIndexAssign(RetTypeNoAutoDecoding val, size_t index) 151 { 152 return arr[index] = val; 153 } 154 155 RetType opIndexOpAssign(string op)(RetTypeNoAutoDecoding value, size_t index) 156 { 157 mixin("return arr[index] " ~ op ~ "= value;"); 158 } 159 160 RetType opIndexUnary(string op)(size_t index) 161 { 162 mixin("return " ~ op ~ "arr[index];"); 163 } 164 } 165 166 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 173 typeof(this) opSlice() 174 { 175 return this; 176 } 177 } 178 179 static if (l == Length.Yes) 180 { 181 @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 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 */ 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 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 */ 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 */ 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);} 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);} 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);} 302 final @property ref T back(){return _payload.back;} 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 328 static void initialize(ref uint[] arr) 329 { 330 import std.array : array; 331 arr = iota().array; 332 } 333 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 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 367 bool opEquals(const ref TestFoo other) const 368 { 369 return this.a == other.a; 370 } 371 372 TestFoo opBinary(string op)(TestFoo other) 373 { 374 TestFoo ret = this; 375 mixin("ret.a " ~ op ~ "= other.a;"); 376 return ret; 377 } 378 379 TestFoo opOpAssign(string op)(TestFoo other) 380 { 381 mixin("this.a " ~ op ~ "= other.a;"); 382 return this; 383 } 384 } 385 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 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 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 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 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