1 /**
2  * This module implements custom assertions via $(D shouldXXX) functions
3  * that throw exceptions containing information about why the assertion
4  * failed.
5  */
6 
7 module unit_threaded.should;
8 
9 import std.exception;
10 import std.conv;
11 import std.algorithm;
12 import std.traits;
13 import std.range;
14 
15 
16 /**
17  * An exception to signal that a test case has failed.
18  */
19 class UnitTestException : Exception
20 {
21     this(in string msg, string file = __FILE__,
22          size_t line = __LINE__, Throwable next = null) @safe pure nothrow
23     {
24         this([msg], file, line, next);
25     }
26 
27     this(in string[] msgLines, string file = __FILE__,
28          size_t line = __LINE__, Throwable next = null) @safe pure nothrow
29     {
30         super(msgLines.join("\n"), next, file, line);
31         this.msgLines = msgLines;
32     }
33 
34     override string toString() @safe const pure
35     {
36         return () @trusted { return msgLines.map!(a => getOutputPrefix(file, line) ~ a).join("\n"); }();
37     }
38 
39 private:
40 
41     const string[] msgLines;
42 
43     string getOutputPrefix(in string file, in size_t line) @safe const pure
44     {
45         return "    " ~ file ~ ":" ~ line.to!string ~ " - ";
46     }
47 }
48 
49 /**
50  * Verify that the condition is `true`.
51  * Throws: UnitTestException on failure.
52  */
53 void shouldBeTrue(E)(lazy E condition, in string file = __FILE__, in size_t line = __LINE__)
54 {
55     shouldEqual(cast(bool)condition, true, file, line);
56 }
57 
58 ///
59 @safe pure unittest
60 {
61     shouldBeTrue(true);
62 }
63 
64 @safe pure unittest {
65     static struct Foo {
66         bool opCast(T: bool)() {
67             return true;
68         }
69     }
70     shouldBeTrue(Foo());
71 }
72 
73 /**
74  * Verify that the condition is `false`.
75  * Throws: UnitTestException on failure.
76  */
77 void shouldBeFalse(E)(lazy E condition, in string file = __FILE__, in size_t line = __LINE__)
78 {
79     shouldEqual(cast(bool)condition, false, file, line);
80 }
81 
82 ///
83 @safe pure unittest
84 {
85     shouldBeFalse(false);
86 }
87 
88 @safe pure unittest {
89     static struct Foo {
90         bool opCast(T: bool)() {
91             return false;
92         }
93     }
94     shouldBeFalse(Foo());
95 }
96 
97 /**
98  * Verify that two values are the same.
99  * Floating point values are compared using $(D std.math.approxEqual).
100  * Throws: UnitTestException on failure
101  */
102 void shouldEqual(V, E)(V value, E expected, in string file = __FILE__, in size_t line = __LINE__)
103 {
104     if (!isEqual(value, expected))
105     {
106         const msg = formatValue("Expected: ", expected) ~
107                     formatValue("     Got: ", value);
108         throw new UnitTestException(msg, file, line);
109     }
110 }
111 
112 ///
113 @safe pure unittest {
114     shouldEqual(true, true);
115     shouldEqual(false, false);
116     shouldEqual(1, 1) ;
117     shouldEqual("foo", "foo") ;
118     shouldEqual([2, 3], [2, 3]) ;
119 
120     shouldEqual(iota(3), [0, 1, 2]);
121     shouldEqual([[0, 1], [0, 1, 2]], [[0, 1], [0, 1, 2]]);
122     shouldEqual([[0, 1], [0, 1, 2]], [iota(2), iota(3)]);
123     shouldEqual([iota(2), iota(3)], [[0, 1], [0, 1, 2]]);
124 
125 }
126 
127 ///
128 @safe unittest {
129     //impure comparisons
130     shouldEqual(1.0, 1.0) ;
131 }
132 
133 /**
134  * Verify that two values are not the same.
135  * Throws: UnitTestException on failure
136  */
137 void shouldNotEqual(V, E)(V value, E expected, in string file = __FILE__, in size_t line = __LINE__)
138 {
139     if (isEqual(value, expected))
140     {
141         const msg = ["Value:",
142                      formatValue("", value).join(""),
143                      "is not expected to be equal to:",
144                      formatValue("", expected).join("")
145             ];
146         throw new UnitTestException(msg, file, line);
147     }
148 }
149 
150 ///
151 @safe pure unittest
152 {
153     shouldNotEqual(true, false);
154     shouldNotEqual(1, 2);
155     shouldNotEqual("f", "b");
156     shouldNotEqual([2, 3], [2, 3, 4]);
157 }
158 
159 ///
160 @safe unittest {
161     shouldNotEqual(1.0, 2.0);
162 }
163 
164 
165 @safe pure unittest {
166     import unit_threaded.asserts;
167 
168     assertExceptionMsg(3.shouldEqual(5),
169                        "    source/unit_threaded/should.d:123 - Expected: 5\n" ~
170                        "    source/unit_threaded/should.d:123 -      Got: 3");
171 
172     assertExceptionMsg("foo".shouldEqual("bar"),
173                        "    source/unit_threaded/should.d:123 - Expected: \"bar\"\n" ~
174                        "    source/unit_threaded/should.d:123 -      Got: \"foo\"");
175 
176     assertExceptionMsg([1, 2, 4].shouldEqual([1, 2, 3]),
177                        "    source/unit_threaded/should.d:123 - Expected: [1, 2, 3]\n" ~
178                        "    source/unit_threaded/should.d:123 -      Got: [1, 2, 4]");
179 
180     assertExceptionMsg([[0, 1, 2, 3, 4], [1], [2], [3], [4], [5]].shouldEqual([[0], [1], [2]]),
181                        "    source/unit_threaded/should.d:123 - Expected: [[0], [1], [2]]\n" ~
182                        "    source/unit_threaded/should.d:123 -      Got: [[0, 1, 2, 3, 4], [1], [2], [3], [4], [5]]");
183 
184     assertExceptionMsg([[0, 1, 2, 3, 4, 5], [1], [2], [3]].shouldEqual([[0], [1], [2]]),
185                        "    source/unit_threaded/should.d:123 - Expected: [[0], [1], [2]]\n" ~
186                        "    source/unit_threaded/should.d:123 -      Got: [[0, 1, 2, 3, 4, 5], [1], [2], [3]]");
187 
188 
189     assertExceptionMsg([[0, 1, 2, 3, 4, 5], [1], [2], [3], [4], [5]].shouldEqual([[0]]),
190                        "    source/unit_threaded/should.d:123 - Expected: [[0]]\n" ~
191                        "    source/unit_threaded/should.d:123 -      Got: [\n" ~
192                        "    source/unit_threaded/should.d:123 -               [0, 1, 2, 3, 4, 5],\n" ~
193                        "    source/unit_threaded/should.d:123 -               [1],\n" ~
194                        "    source/unit_threaded/should.d:123 -               [2],\n" ~
195                        "    source/unit_threaded/should.d:123 -               [3],\n" ~
196                        "    source/unit_threaded/should.d:123 -               [4],\n" ~
197                        "    source/unit_threaded/should.d:123 -               [5],\n" ~
198                        "    source/unit_threaded/should.d:123 -           ]");
199 
200     assertExceptionMsg(1.shouldNotEqual(1),
201                        "    source/unit_threaded/should.d:123 - Value:\n" ~
202                        "    source/unit_threaded/should.d:123 - 1\n" ~
203                        "    source/unit_threaded/should.d:123 - is not expected to be equal to:\n" ~
204                        "    source/unit_threaded/should.d:123 - 1");
205 }
206 
207 @safe pure unittest
208 {
209     ubyte[] arr;
210     arr.shouldEqual([]);
211 }
212 
213 
214 @safe pure unittest
215 {
216     int[] ints = [1, 2, 3];
217     byte[] bytes = [1, 2, 3];
218     byte[] bytes2 = [1, 2, 4];
219     shouldEqual(ints, bytes);
220     shouldEqual(bytes, ints) ;
221     shouldNotEqual(ints, bytes2) ;
222 
223     const constIntToInts = [1 : 2, 3 : 7, 9 : 345];
224     auto intToInts = [1 : 2, 3 : 7, 9 : 345];
225     shouldEqual(intToInts, constIntToInts) ;
226     shouldEqual(constIntToInts, intToInts) ;
227 }
228 
229 @safe unittest {
230     shouldEqual([1 : 2.0, 2 : 4.0], [1 : 2.0, 2 : 4.0]) ;
231     shouldNotEqual([1 : 2.0, 2 : 4.0], [1 : 2.2, 2 : 4.0]) ;
232 }
233 
234 /**
235  * Verify that the value is null.
236  * Throws: UnitTestException on failure
237  */
238 void shouldBeNull(T)(in T value, in string file = __FILE__, in size_t line = __LINE__)
239 {
240     if (value !is null)
241         fail("Value is null", file, line);
242 }
243 
244 ///
245 @safe pure unittest
246 {
247     shouldBeNull(null) ;
248 }
249 
250 
251 /**
252  * Verify that the value is not null.
253  * Throws: UnitTestException on failure
254  */
255 void shouldNotBeNull(T)(in T value, in string file = __FILE__, in size_t line = __LINE__)
256 {
257     if (value is null)
258         fail("Value is null", file, line);
259 }
260 
261 ///
262 @safe pure unittest
263 {
264     class Foo
265     {
266         this(int i) { this.i = i; }
267         override string toString() const
268         {
269             import std.conv: to;
270             return i.to!string;
271         }
272         int i;
273     }
274 
275     shouldNotBeNull(new Foo(4)) ;
276     shouldEqual(new Foo(5), new Foo(5));
277     assertFail(shouldEqual(new Foo(5), new Foo(4)));
278     shouldNotEqual(new Foo(5), new Foo(4)) ;
279     assertFail(shouldNotEqual(new Foo(5), new Foo(5)));
280 }
281 
282 
283 /**
284  * Verify that the value is in the container.
285  * Throws: UnitTestException on failure
286 */
287 void shouldBeIn(T, U)(in T value, in U container, in string file = __FILE__, in size_t line = __LINE__)
288 if (isAssociativeArray!U)
289 {
290     if (value !in container)
291     {
292         fail("Value " ~ to!string(value) ~ " not in " ~ to!string(container), file,
293             line);
294     }
295 }
296 
297 /**
298  * Verify that the value is in the container.
299  * Throws: UnitTestException on failure
300  */
301 void shouldBeIn(T, U)(in T value, U container, in string file = __FILE__, in size_t line = __LINE__)
302 if (!isAssociativeArray!U && isInputRange!U)
303 {
304     if (find(container, value).empty)
305     {
306         fail("Value " ~ to!string(value) ~ " not in " ~ to!string(container), file,
307             line);
308     }
309 }
310 
311 ///
312 @safe pure unittest
313 {
314     shouldBeIn(4, [1, 2, 4]);
315     shouldBeIn("foo", ["foo" : 1]);
316 }
317 
318 
319 /**
320  * Verify that the value is not in the container.
321  * Throws: UnitTestException on failure
322  */
323 void shouldNotBeIn(T, U)(in T value, in U container,
324                          in string file = __FILE__, in size_t line = __LINE__)
325 if (isAssociativeArray!U)
326 {
327     if (value in container)
328     {
329         fail("Value " ~ to!string(value) ~ " is in " ~ to!string(container), file,
330             line);
331     }
332 }
333 
334 
335 /**
336  * Verify that the value is not in the container.
337  * Throws: UnitTestException on failure
338  */
339 void shouldNotBeIn(T, U)(in T value, U container,
340                          in string file = __FILE__, in size_t line = __LINE__)
341 if (!isAssociativeArray!U && isInputRange!U)
342 {
343     if (find(container, value).length > 0)
344     {
345         fail("Value " ~ to!string(value) ~ " is in " ~ to!string(container), file,
346             line);
347     }
348 }
349 
350 ///
351 @safe unittest
352 {
353     shouldNotBeIn(3.5, [1.1, 2.2, 4.4]);
354     shouldNotBeIn(1.0, [2.0 : 1, 3.0 : 2]);
355 }
356 
357 /**
358  * Verify that expr throws the templated Exception class.
359  * This succeeds if the expression throws a child class of
360  * the template parameter.
361  * Throws: UnitTestException on failure (when expr does not
362  * throw the expected exception)
363  */
364 void shouldThrow(T : Throwable = Exception, E)(lazy E expr,
365     in string file = __FILE__, in size_t line = __LINE__)
366 {
367     if (!threw!T(expr))
368         fail("Expression did not throw", file, line);
369 }
370 
371 /**
372  * Verify that expr throws the templated Exception class.
373  * This only succeeds if the expression throws an exception of
374  * the exact type of the template parameter.
375  * Throws: UnitTestException on failure (when expr does not
376  * throw the expected exception)
377  */
378 void shouldThrowExactly(T : Throwable = Exception, E)(lazy E expr,
379     in string file = __FILE__, in size_t line = __LINE__)
380 {
381 
382     const threw = threw!T(expr);
383     if (!threw)
384         fail("Expression did not throw", file, line);
385 
386     //Object.opEquals is @system and impure
387     const sameType = () @trusted { return threw.typeInfo == typeid(T); }();
388     if (!sameType)
389         fail(text("Expression threw wrong type ", threw.typeInfo,
390             "instead of expected type ", typeid(T)), file, line);
391 }
392 
393 /**
394  * Verify that expr does not throw the templated Exception class.
395  * Throws: UnitTestException on failure
396  */
397 void shouldNotThrow(T : Throwable = Exception, E)(lazy E expr,
398     in string file = __FILE__, in size_t line = __LINE__)
399 {
400     if (threw!T(expr))
401         fail("Expression threw", file, line);
402 }
403 
404 /**
405  * Verify that an exception is thrown with the right message
406  */
407 void shouldThrowWithMessage(T : Throwable = Exception, E)(lazy E expr,
408                                                           string msg,
409                                                           string file = __FILE__,
410                                                           size_t line = __LINE__) {
411     auto threw = threw!T(expr);
412     if (!threw)
413         fail("Expression did not throw", file, line);
414 
415     threw.throwable.msg.shouldEqual(msg, file, line);
416 }
417 
418 ///
419 @safe pure unittest {
420     void funcThrows(string msg) { throw new Exception(msg); }
421     funcThrows("foo bar").shouldThrowWithMessage!Exception("foo bar");
422     funcThrows("foo bar").shouldThrowWithMessage("foo bar");
423     assertFail(funcThrows("boo boo").shouldThrowWithMessage("foo bar"));
424 }
425 
426 
427 //@trusted because the user might want to catch a throwable
428 //that's not derived from Exception, such as RangeError
429 private auto threw(T : Throwable, E)(lazy E expr) @trusted
430 {
431 
432     struct ThrowResult
433     {
434         bool threw;
435         TypeInfo typeInfo;
436         immutable(T) throwable;
437 
438         T opCast(T)() const pure if (is(T == bool))
439         {
440             return threw;
441         }
442     }
443 
444     try
445     {
446         expr();
447     }
448     catch (T e)
449     {
450         return ThrowResult(true, typeid(e), cast(immutable)e);
451     }
452 
453     return ThrowResult(false);
454 }
455 
456 // can't be made pure because of throwExactly, which in turn
457 // can't be pure because of Object.opEquals
458 @safe unittest
459 {
460     class CustomException : Exception
461     {
462         this(string msg = "")
463         {
464             super(msg);
465         }
466     }
467 
468     class ChildException : CustomException
469     {
470         this(string msg = "")
471         {
472             super(msg);
473         }
474     }
475 
476     void throwCustom()
477     {
478         throw new CustomException();
479     }
480 
481     throwCustom.shouldThrow;
482     throwCustom.shouldThrow!CustomException;
483 
484     void throwChild()
485     {
486         throw new ChildException();
487     }
488 
489     throwChild.shouldThrow;
490     throwChild.shouldThrow!CustomException;
491     throwChild.shouldThrow!ChildException;
492     throwChild.shouldThrowExactly!ChildException;
493     try
494     {
495         throwChild.shouldThrowExactly!CustomException; //should not succeed
496         assert(0, "shouldThrowExactly failed");
497     }
498     catch (Exception ex)
499     {
500     }
501 }
502 
503 @safe pure unittest
504 {
505     void throwRangeError()
506     {
507         ubyte[] bytes;
508         bytes = bytes[1 .. $];
509     }
510 
511     import core.exception : RangeError;
512 
513     throwRangeError.shouldThrow!RangeError;
514 }
515 
516 
517 void fail(in string output, in string file, in size_t line) @safe pure
518 {
519     throw new UnitTestException([output], file, line);
520 }
521 
522 
523 private string[] formatValue(T)(in string prefix, T value) {
524     static if(isSomeString!T) {
525         // isSomeString is true for wstring and dstring,
526         // so call .to!string anyway
527         return [ prefix ~ `"` ~ value.to!string ~ `"`];
528     } else static if(isInputRange!T) {
529         return formatRange(prefix, value);
530     } else {
531         return [() @trusted { return prefix ~ value.to!string; }()];
532     }
533 }
534 
535 private string[] formatRange(T)(in string prefix, T value) {
536     //some versions of `to` are @system
537     auto defaultLines = () @trusted { return [prefix ~ value.to!string]; }();
538 
539     static if (!isInputRange!(ElementType!T))
540         return defaultLines;
541     else
542     {
543         import std.array: array;
544         const maxElementSize = value.empty ? 0 : value.map!(a => a.array.length).reduce!max;
545         const tooBigForOneLine = (value.array.length > 5 && maxElementSize > 5) || maxElementSize > 10;
546         if (!tooBigForOneLine)
547             return defaultLines;
548         return [prefix ~ "["] ~
549             value.map!(a => formatValue("              ", a).join("") ~ ",").array ~
550             "          ]";
551     }
552 }
553 
554 private enum isObject(T) = is(T == class) || is(T == interface);
555 
556 private bool isEqual(V, E)(in V value, in E expected)
557  if (!isObject!V &&
558      (!isInputRange!V || !isInputRange!E) &&
559      !isFloatingPoint!V && !isFloatingPoint!E &&
560      is(typeof(value == expected) == bool))
561 {
562     return value == expected;
563 }
564 
565 private bool isEqual(V, E)(in V value, in E expected)
566  if (!isObject!V && (isFloatingPoint!V || isFloatingPoint!E) && is(typeof(value == expected) == bool))
567 {
568     return value == expected;
569 }
570 
571 @safe pure unittest {
572     assert(isEqual(1.0, 1.0));
573     assert(!isEqual(1.0, 1.0001));
574 }
575 
576 private bool isApproxEqual(V, E)(in V value, in E expected)
577  if (!isObject!V && (isFloatingPoint!V || isFloatingPoint!E) && is(typeof(value == expected) == bool))
578 {
579     import std.math;
580     return approxEqual(value, expected);
581 }
582 
583 @safe unittest {
584     assert(isApproxEqual(1.0, 1.0));
585     assert(isApproxEqual(1.0, 1.0001));
586 }
587 
588 void shouldApproxEqual(V, E)(in V value, in E expected, string file = __FILE__, size_t line = __LINE__)
589  if (!isObject!V && (isFloatingPoint!V || isFloatingPoint!E) && is(typeof(value == expected) == bool))
590 {
591     if (!isApproxEqual(value, expected))
592     {
593         const msg =
594             formatValue("Expected approx: ", expected) ~
595             formatValue("     Got       : ", value);
596         throw new UnitTestException(msg, file, line);
597     }
598 }
599 
600 ///
601 @safe unittest {
602     1.0.shouldApproxEqual(1.0001);
603 }
604 
605 
606 private bool isEqual(V, E)(V value, E expected)
607 if (!isObject!V && isInputRange!V && isInputRange!E && is(typeof(value.front == expected.front) == bool))
608 {
609     return equal(value, expected);
610 }
611 
612 private bool isEqual(V, E)(V value, E expected)
613 if (!isObject!V &&
614     isInputRange!V && isInputRange!E && !is(typeof(value.front == expected.front) == bool) &&
615     isInputRange!(ElementType!V) && isInputRange!(ElementType!E))
616 {
617     while (!value.empty && !expected.empty)
618     {
619         if (!equal(value.front, expected.front))
620             return false;
621 
622         value.popFront;
623         expected.popFront;
624     }
625 
626     return value.empty && expected.empty;
627 }
628 
629 private bool isEqual(V, E)(V value, E expected)
630 if (isObject!V && isObject!E)
631 {
632     static assert(is(typeof(() { string s1 = value.toString; string s2 = expected.toString;})),
633                   "Cannot compare instances of " ~ V.stringof ~
634                   " or " ~ E.stringof ~ " unless toString is overridden for both");
635 
636     return value.tupleof == expected.tupleof;
637 }
638 
639 
640 @safe pure unittest {
641     assert(isEqual(2, 2));
642     assert(!isEqual(2, 3));
643 
644     assert(isEqual(2.1, 2.1));
645     assert(!isEqual(2.1, 2.2));
646 
647     assert(isEqual("foo", "foo"));
648     assert(!isEqual("foo", "fooo"));
649 
650     assert(isEqual([1, 2], [1, 2]));
651     assert(!isEqual([1, 2], [1, 2, 3]));
652 
653     assert(isEqual(iota(2), [0, 1]));
654     assert(!isEqual(iota(2), [1, 2, 3]));
655 
656     assert(isEqual([[0, 1], [0, 1, 2]], [iota(2), iota(3)]));
657     assert(isEqual([[0, 1], [0, 1, 2]], [[0, 1], [0, 1, 2]]));
658     assert(!isEqual([[0, 1], [0, 1, 4]], [iota(2), iota(3)]));
659     assert(!isEqual([[0, 1], [0]], [iota(2), iota(3)]));
660 
661     assert(isEqual([0: 1], [0: 1]));
662 
663     const constIntToInts = [1 : 2, 3 : 7, 9 : 345];
664     auto intToInts = [1 : 2, 3 : 7, 9 : 345];
665 
666     assert(isEqual(intToInts, constIntToInts));
667     assert(isEqual(constIntToInts, intToInts));
668 
669     class Foo
670     {
671         this(int i) { this.i = i; }
672         override string toString() const { return i.to!string; }
673         int i;
674     }
675 
676     assert(isEqual(new Foo(5), new Foo(5)));
677     assert(!isEqual(new Foo(5), new Foo(4)));
678 
679     ubyte[] arr;
680     assert(isEqual(arr, []));
681 }
682 
683 
684 private void assertFail(E)(lazy E expression)
685 {
686     assertThrown!UnitTestException(expression);
687 }
688 
689 /**
690  * Verify that rng is empty.
691  * Throws: UnitTestException on failure.
692  */
693 void shouldBeEmpty(R)(R rng, in string file = __FILE__, in size_t line = __LINE__)
694 if (isInputRange!R)
695 {
696     if (!rng.empty)
697         fail("Range not empty", file, line);
698 }
699 
700 /**
701  * Verify that aa is empty.
702  * Throws: UnitTestException on failure.
703  */
704 void shouldBeEmpty(T)(in T aa, in string file = __FILE__, in size_t line = __LINE__)
705 if (isAssociativeArray!T)
706 {
707     //keys is @system
708     () @trusted{ if (!aa.keys.empty) fail("AA not empty", file, line); }();
709 }
710 
711 ///
712 @safe pure unittest
713 {
714     int[] ints;
715     string[] strings;
716     string[string] aa;
717 
718     shouldBeEmpty(ints);
719     shouldBeEmpty(strings);
720     shouldBeEmpty(aa);
721 
722     ints ~= 1;
723     strings ~= "foo";
724     aa["foo"] = "bar";
725 
726     assertFail(shouldBeEmpty(ints));
727     assertFail(shouldBeEmpty(strings));
728     assertFail(shouldBeEmpty(aa));
729 }
730 
731 
732 /**
733  * Verify that rng is not empty.
734  * Throws: UnitTestException on failure.
735  */
736 void shouldNotBeEmpty(R)(R rng, in string file = __FILE__, in size_t line = __LINE__)
737 if (isInputRange!R)
738 {
739     if (rng.empty)
740         fail("Range empty", file, line);
741 }
742 
743 /**
744  * Verify that aa is not empty.
745  * Throws: UnitTestException on failure.
746  */
747 void shouldNotBeEmpty(T)(in T aa, in string file = __FILE__, in size_t line = __LINE__)
748 if (isAssociativeArray!T)
749 {
750     //keys is @system
751     () @trusted{ if (aa.keys.empty)
752         fail("AA empty", file, line); }();
753 }
754 
755 ///
756 @safe pure unittest
757 {
758     int[] ints;
759     string[] strings;
760     string[string] aa;
761 
762     assertFail(shouldNotBeEmpty(ints));
763     assertFail(shouldNotBeEmpty(strings));
764     assertFail(shouldNotBeEmpty(aa));
765 
766     ints ~= 1;
767     strings ~= "foo";
768     aa["foo"] = "bar";
769 
770     shouldNotBeEmpty(ints);
771     shouldNotBeEmpty(strings);
772     shouldNotBeEmpty(aa);
773 }
774 
775 /**
776  * Verify that t is greater than u.
777  * Throws: UnitTestException on failure.
778  */
779 void shouldBeGreaterThan(T, U)(in T t, in U u,
780                                in string file = __FILE__, in size_t line = __LINE__)
781 {
782     if (t <= u)
783         fail(text(t, " is not > ", u), file, line);
784 }
785 
786 ///
787 @safe pure unittest
788 {
789     shouldBeGreaterThan(7, 5);
790     assertFail(shouldBeGreaterThan(5, 7));
791     assertFail(shouldBeGreaterThan(7, 7));
792 }
793 
794 
795 /**
796  * Verify that t is smaller than u.
797  * Throws: UnitTestException on failure.
798  */
799 void shouldBeSmallerThan(T, U)(in T t, in U u,
800                                in string file = __FILE__, in size_t line = __LINE__)
801 {
802     if (t >= u)
803         fail(text(t, " is not < ", u), file, line);
804 }
805 
806 ///
807 @safe pure unittest
808 {
809     shouldBeSmallerThan(5, 7);
810     assertFail(shouldBeSmallerThan(7, 5));
811     assertFail(shouldBeSmallerThan(7, 7));
812 }
813 
814 
815 
816 /**
817  * Verify that t and u represent the same set (ordering is not important).
818  * Throws: UnitTestException on failure.
819  */
820 void shouldBeSameSetAs(V, E)(V value, E expected, in string file = __FILE__, in size_t line = __LINE__)
821 if (isInputRange!V && isInputRange!E && is(typeof(value.front != expected.front) == bool))
822 {
823     if (!isSameSet(value, expected))
824     {
825         const msg = formatValue("Expected: ", expected) ~
826                     formatValue("     Got: ", value);
827         throw new UnitTestException(msg, file, line);
828     }
829 }
830 
831 ///
832 @safe pure unittest
833 {
834     auto inOrder = iota(4);
835     auto noOrder = [2, 3, 0, 1];
836     auto oops = [2, 3, 4, 5];
837 
838     inOrder.shouldBeSameSetAs(noOrder);
839     inOrder.shouldBeSameSetAs(oops).shouldThrow!UnitTestException;
840 
841     struct Struct
842     {
843         int i;
844     }
845 
846     [Struct(1), Struct(4)].shouldBeSameSetAs([Struct(4), Struct(1)]);
847 }
848 
849 private bool isSameSet(T, U)(T t, U u) {
850     //sort makes the element types have to implement opCmp
851     //instead, try one by one
852     auto ta = t.array;
853     auto ua = u.array;
854     if (ta.length != ua.length) return false;
855     foreach(element; ta)
856     {
857         if (!ua.canFind(element)) return false;
858     }
859 
860     return true;
861 }
862 
863 /**
864  * Verify that value and expected do not represent the same set (ordering is not important).
865  * Throws: UnitTestException on failure.
866  */
867 void shouldNotBeSameSetAs(V, E)(V value, E expected, in string file = __FILE__, in size_t line = __LINE__)
868 if (isInputRange!V && isInputRange!E && is(typeof(value.front != expected.front) == bool))
869 {
870     if (isSameSet(value, expected))
871     {
872         const msg = ["Value:",
873                      formatValue("", value).join(""),
874                      "is not expected to be equal to:",
875                      formatValue("", expected).join("")
876             ];
877         throw new UnitTestException(msg, file, line);
878     }
879 }
880 
881 
882 ///
883 @safe pure unittest
884 {
885     auto inOrder = iota(4);
886     auto noOrder = [2, 3, 0, 1];
887     auto oops = [2, 3, 4, 5];
888 
889     inOrder.shouldNotBeSameSetAs(oops);
890     inOrder.shouldNotBeSameSetAs(noOrder).shouldThrow!UnitTestException;
891 }
892 
893 
894 @safe pure unittest {
895     "foo"w.shouldEqual("foo");
896 }