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.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 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 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 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 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 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 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 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