1 module dshould;
2 
3 import dshould.ShouldType;
4 
5 public import dshould.ShouldType : because, should;
6 public import dshould.basic;
7 public import dshould.contain;
8 public import dshould.empty;
9 public import dshould.stringcmp;
10 public import dshould.thrown;
11 
12 // dispatch based on type
13 /**
14  * The word `.equal` tests its parameter for equality with the left-hand side.
15  * If the parameters are strings, a colored diff is used.
16  */
17 public void equal(Should, T)(Should should, T value, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
18 if (isInstanceOf!(ShouldType, Should))
19 {
20     static if (is(typeof(should.got()) == string) && is(T == string) && !Should.hasWord!"not")
21     {
22         dshould.stringcmp.equal(should, value, Fence(), file, line);
23     }
24     else
25     {
26         dshould.basic.equal(should, value, Fence(), file, line);
27     }
28 }
29 
30 public auto equal(Should)(Should should)
31 if (isInstanceOf!(ShouldType, Should))
32 {
33     return dshould.basic.equal(should);
34 }
35 
36 /**
37  * The word `.throwA` (or `.throwAn`) expects its left-hand side expression to throw an exception.
38  * Instead of the cumbersome `.where.msg.should.equal("msg")`, the `msg` of the Exception to expect
39  * can be passed directly.
40  */
41 public template throwA(T : Throwable)
42 {
43     void throwA(Should)(Should should, string msgTest, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
44     if (isInstanceOf!(ShouldType, Should))
45     {
46         dshould.thrown.throwA!T(should, Fence(), file, line).where.msg.should.equal(msgTest, Fence(), file, line);
47     }
48 
49     auto throwA(Should)(Should should, Fence _ = Fence(), string file = __FILE__, size_t line = __LINE__)
50     if (isInstanceOf!(ShouldType, Should))
51     {
52         return dshould.thrown.throwA!T(should, Fence(), file, line);
53     }
54 }
55 
56 /// ditto
57 public alias throwAn = throwA;
58 
59 @("because defines reason for assertion")
60 unittest
61 {
62     2.should.be(5).because("string A")
63         .should.throwA!FluentException.where.reason.should.equal("string A");
64 }
65 
66 @("compares messages in throwA string overload")
67 unittest
68 {
69     2.should.be(5).because("string A")
70         .should.throwA!FluentException("string B")
71         .should.throwA!FluentException;
72 }
73 
74 @("prints informative errors for int comparison")
75 unittest
76 {
77     2.should.be(3).should.throwA!FluentException("Test failed: expected 3, but got 2");
78 }
79 
80 @("prints informative errors for object comparison")
81 unittest
82 {
83     Object obj;
84 
85     obj.should.not.be(null)
86         .should.throwA!FluentException("Test failed: expected non-null, but got null");
87 
88     obj = new Object;
89 
90     obj.should.be(null)
91         .should.throwA!FluentException("Test failed: expected null, but got object.Object");
92 
93     obj.should.not.be(obj)
94         .should.throwA!FluentException(
95             "Test failed: expected different reference than object.Object, but got same reference");
96 
97     obj.should.be(new Object)
98         .should.throwA!FluentException(
99             "Test failed: expected same reference as object.Object, but got object.Object");
100 }
101 
102 @("prints informative errors for inequalities")
103 unittest
104 {
105     2.should.be.greater.equal(5)
106         .should.throwA!FluentException("Test failed: expected value >= 5, but got 2");
107 
108     2.should.not.be.less.equal(5)
109         .should.throwA!FluentException("Test failed: expected value not <= 5, but got 2");
110 }
111 
112 @("prints informative errors for range emptiness")
113 unittest
114 {
115     [].should.not.be.empty
116         .should.throwA!FluentException("Test failed: expected nonempty range, but got []");
117 
118     [5].should.be.empty
119         .should.throwA!FluentException("Test failed: expected empty range, but got [5]");
120 }
121 
122 @("prints informative errors for approximate checks")
123 unittest
124 {
125     2.should.approximately.be(4, error=0.5)
126         .should.throwA!FluentException("Test failed: expected 4 ± 0.5, but got 2");
127 
128     (2.4).should.not.approximately.be(2, error=0.5)
129         .should.throwA!FluentException("Test failed: expected value outside 2 ± 0.5, but got 2.4");
130 }
131 
132 @("asserts when forgetting to terminate should expression")
133 unittest
134 {
135     import core.exception : AssertError;
136 
137     void test()
138     {
139         2.should;
140     }
141 
142     test.should.throwAn!AssertError("unterminated should-chain!");
143 }