1 /**
2 Copyright: Copyright (c) 2016, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module test.extra_should;
11 
12 import std.ascii : newline;
13 import std.traits : isSomeString;
14 
15 /** Verify in lockstep that the two values are the same.
16  *
17  * Useful when the values can be treated as ranges.
18  * The lockstep comparison then results in a more comprehensible failure
19  * message.
20  *
21  * trusted: it only affects unittesting.
22  *
23  * Throws: UnitTestException on failure
24  * Params:
25  *  value = actual value.
26  *  expected = expected value.
27  *  file = file check is in.
28  *  line = line check is on.
29  */
30 void shouldEqualPretty(V, E)(lazy V value, lazy E expected, string file = __FILE__,
31         size_t line = __LINE__) @trusted if (!isAllSomeString!(V, E)) {
32     import std.algorithm : count;
33     import std.range : lockstep;
34     import unit_threaded : shouldEqual, UnitTestException;
35     import std.conv : text;
36 
37     size_t idx;
38 
39     try {
40         foreach (index, val, exp; lockstep(value, expected)) {
41             idx = index;
42             shouldEqual(val, exp, file, line);
43         }
44     }
45     catch (UnitTestException ex) {
46         string[] lines = ["Chunk:" ~ idx.text, ex.toString()];
47         throw new UnitTestException(lines, file, line);
48     }
49 
50     shouldEqual(count(value), count(expected), file, line);
51 }
52 
53 unittest {
54     // @Name("shouldEqualPretty should throw the first value that is different")
55     import unit_threaded : UnitTestException;
56 
57     string msg;
58     try {
59         auto value = [0, 2, 1];
60         auto expected = [0, 1, 2];
61         shouldEqualPretty!(typeof(value), typeof(expected))(value, expected, "file.d", 123);
62 
63         assert(false, "Didn't throw exception");
64     }
65     catch (UnitTestException ex) {
66         msg = ex.toString;
67     }
68 
69     msg = "foo";
70     shouldEqualPretty(msg, "foo");
71     assert(msg, "foo");
72 }
73 
74 /** Split with sep and verify in lockstep that the two values are the same.
75  *
76  * Throws: UnitTestException on failure.
77  * Params:
78  *  value = actual value.
79  *  expected = expected value.
80  *  sep = separator to split value and expected on.
81  *  file = file check is in.
82  *  line = line check is on.
83  */
84 void shouldEqualPretty(V, E, Separator)(lazy V value, lazy E expected,
85         lazy Separator sep, string file = __FILE__, size_t line = __LINE__) @safe 
86         if (!isAllSomeString!(V, E)) {
87     import std.algorithm : splitter;
88 
89     auto rValue = value.splitter(sep);
90     auto rExpected = expected.splitter(sep);
91 
92     shouldEqualPretty!(typeof(rValue), typeof(rExpected))(rValue, rExpected, file, line);
93 }
94 
95 /** Verify that two strings are the same.
96  *
97  * Performs tests per line to better isolate when a difference is found.
98  *
99  * Throws: UnitTestException on failure
100  * Params:
101  *  value = actual value.
102  *  expected = expected value.
103  *  file = file check is in.
104  *  line = line check is on.
105  */
106 void shouldEqualPretty(V, E)(lazy V value, lazy E expected, lazy string sep = newline,
107         string file = __FILE__, size_t line = __LINE__) @safe 
108         if (isAllSomeString!(V, E)) {
109     import std.algorithm : splitter;
110 
111     auto rValue = value.splitter(sep);
112     auto rExpected = expected.splitter(sep);
113 
114     shouldEqualPretty!(typeof(rValue), typeof(rExpected))(rValue, rExpected, file, line);
115 }
116 
117 private enum isAllSomeString(T0, T1) = isSomeString!T0 && isSomeString!T1;