1 module unit_threaded.check; 2 3 @safe 4 5 import std.exception; 6 import std.conv; 7 import std.algorithm; 8 import std.traits; 9 10 //attributes 11 enum UnitTest; //opt-in to registration 12 enum DontTest; //opt-out of registration 13 enum HiddenTest; //hide test. Not run by default but can be run. 14 15 class UnitTestException: Exception { 16 this(string msg) { 17 super(msg); 18 } 19 } 20 21 void checkTrue(in bool condition, in string file = __FILE__, in ulong line = __LINE__) { 22 if(!condition) failEqual(condition, true, file, line); 23 } 24 25 void checkFalse(in bool condition, in string file = __FILE__, in ulong line = __LINE__) { 26 if(condition) failEqual(condition, false, file, line); 27 } 28 29 void checkEqual(T, U)(in T value, in U expected, in string file = __FILE__, in ulong line = __LINE__) 30 if(is(typeof(value != expected) == bool)) { 31 if(value != expected) failEqual(value, expected, file, line); 32 } 33 34 void checkNotEqual(T, U)(in T value, in U expected, in string file = __FILE__, in ulong line = __LINE__) 35 if(is(typeof(value == expected) == bool)) { 36 if(value == expected) failEqual(value, expected, file, line); 37 } 38 39 void checkNull(T)(in T value, in string file = __FILE__, in ulong line = __LINE__) { 40 if(value !is null) fail(getOutputPrefix(file, line) ~ "Value is null"); 41 } 42 43 void checkNotNull(T)(in T value, in string file = __FILE__, in ulong line = __LINE__) { 44 if(value is null) fail(getOutputPrefix(file, line) ~ "Value is null"); 45 } 46 47 void checkIn(T, U)(in T value, in U container, in string file = __FILE__, in ulong line = __LINE__) 48 if(isAssociativeArray!U) 49 { 50 if(value !in container) { 51 fail(getOutputPrefix(file, line) ~ "Value " ~ to!string(value) ~ " not in " ~ to!string(container)); 52 } 53 } 54 55 void checkIn(T, U)(in T value, in U container, in string file = __FILE__, in ulong line = __LINE__) 56 if(!isAssociativeArray!U) 57 { 58 if(!find(container, value)) { 59 fail(getOutputPrefix(file, line) ~ "Value " ~ to!string(value) ~ " not in " ~ to!string(container)); 60 } 61 } 62 63 void checkNotIn(T, U)(in T value, in U container, in string file = __FILE__, in ulong line = __LINE__) 64 if(isAssociativeArray!U) 65 { 66 if(value in container) { 67 fail(getOutputPrefix(file, line) ~ "Value " ~ to!string(value) ~ " in " ~ to!string(container)); 68 } 69 } 70 71 void checkNotIn(T, U)(in T value, in U container, in string file = __FILE__, in ulong line = __LINE__) 72 if(!isAssociativeArray!U) 73 { 74 if(find(container, value).length > 0) { 75 fail(getOutputPrefix(file, line) ~ "Value " ~ to!string(value) ~ " in " ~ to!string(container)); 76 } 77 } 78 79 void checkThrown(T: Throwable = Exception, E)(lazy E expr, in string file = __FILE__, in ulong line = __LINE__) { 80 if(!threw!T(expr)) fail(getOutputPrefix(file, line) ~ "Expression did not throw"); 81 } 82 83 void checkNotThrown(T: Throwable = Exception, E)(lazy E expr, in string file = __FILE__, in ulong line = __LINE__) { 84 if(threw!T(expr)) fail(getOutputPrefix(file, line) ~ "Expression threw"); 85 } 86 87 private bool threw(T: Throwable, E)(lazy E expr) { 88 try { 89 expr(); 90 } catch(T e) { 91 return true; 92 } 93 94 return false; 95 } 96 97 98 void utFail(in string output, in string file, in ulong line) { 99 fail(getOutputPrefix(file, line) ~ output); 100 } 101 102 private void fail(in string output) pure { 103 throw new UnitTestException(output); 104 } 105 106 private void failEqual(T, U)(in T value, in U expected, in string file, in ulong line) { 107 throw new UnitTestException(getOutput(value, expected, file, line)); 108 } 109 110 private string getOutput(T, U)(in T value, in U expected, in string file, in ulong line) { 111 return getOutputPrefix(file, line) ~ 112 "Value " ~ to!string(value) ~ 113 " is not the expected " ~ to!string(expected) ~ "\n"; 114 } 115 116 private string getOutputPrefix(in string file, in ulong line) { 117 return " " ~ file ~ ":" ~ to!string(line) ~ " - "; 118 } 119 120 121 private void assertCheck(E)(lazy E expression) { 122 assertNotThrown!UnitTestException(expression); 123 } 124 125 unittest { 126 assertCheck(checkTrue(true)); 127 assertCheck(checkFalse(false)); 128 } 129 130 131 unittest { 132 assertCheck(checkEqual(true, true)); 133 assertCheck(checkEqual(false, false)); 134 assertCheck(checkNotEqual(true, false)); 135 136 assertCheck(checkEqual(1, 1)); 137 assertCheck(checkNotEqual(1, 2)); 138 139 assertCheck(checkEqual("foo", "foo")); 140 assertCheck(checkNotEqual("f", "b")); 141 142 assertCheck(checkEqual(1.0, 1.0)); 143 assertCheck(checkNotEqual(1.0, 2.0)); 144 145 assertCheck(checkEqual([2, 3], [2, 3])); 146 assertCheck(checkNotEqual([2, 3], [2, 3, 4])); 147 148 int[] ints = [ 1, 2, 3]; 149 byte[] bytes = [ 1, 2, 3]; 150 byte[] bytes2 = [ 1, 2, 4]; 151 assertCheck(checkEqual(ints, bytes)); 152 assertCheck(checkEqual(bytes, ints)); 153 assertCheck(checkNotEqual(ints, bytes2)); 154 155 assertCheck(checkEqual([1: 2.0, 2: 4.0], [1: 2.0, 2: 4.0])); 156 assertCheck(checkNotEqual([1: 2.0, 2: 4.0], [1: 2.2, 2: 4.0])); 157 const constIntToInts = [ 1:2, 3: 7, 9: 345]; 158 auto intToInts = [ 1:2, 3: 7, 9: 345]; 159 assertCheck(checkEqual(intToInts, constIntToInts)); 160 assertCheck(checkEqual(constIntToInts, intToInts)); 161 } 162 163 unittest { 164 assertCheck(checkNull(null)); 165 class Foo { } 166 assertCheck(checkNotNull(new Foo)); 167 } 168 169 unittest { 170 assertCheck(checkIn(4, [1, 2, 4])); 171 assertCheck(checkNotIn(3.5, [1.1, 2.2, 4.4])); 172 assertCheck(checkIn("foo", ["foo": 1])); 173 assertCheck(checkNotIn(1.0, [2.0: 1, 3.0: 2])); 174 }