1 /** 2 This module is an attempt to alleviate compile times by including the bare 3 minimum. The idea is that while the reporting usually done by unit-threaded 4 is welcome, it only really matters when tests fail. Otherwise, no news is 5 good news. 6 7 Likewise, naming and selecting tests are features used when certain tests 8 fail. The usual way to run tests is to run all of them and be happy if 9 they all pass. 10 11 This module makes it so that unit-threaded gets out of the way, and if 12 needed the full features can be turned on at the cost of compiling 13 much more slowly. 14 15 There aren't even any template constraints on the `should` functions 16 to avoid imports as much as possible. 17 */ 18 module unit_threaded.light; 19 20 21 int runTests(T...)(in string[] args) { 22 23 import core.runtime: Runtime; 24 import core.stdc.stdio: printf; 25 26 try { 27 28 Runtime.moduleUnitTester(); 29 30 printf("\n"); 31 version(Posix) 32 printf("\033[32;1mOk\033[0;;m"); 33 else 34 printf("Ok"); 35 36 printf(": All tests passed\n\n"); 37 38 return 0; 39 } catch(Throwable _) 40 return 1; 41 } 42 43 void writelnUt(T...)(auto ref T args) { 44 45 } 46 47 void check(alias F)(int numFuncCalls = 100, 48 in string file = __FILE__, in size_t line = __LINE__) @trusted { 49 import unit_threaded.property: utCheck = check; 50 utCheck!F(numFuncCalls, file, line); 51 } 52 53 void checkCustom(alias Generator, alias Predicate) 54 (int numFuncCalls = 100, in string file = __FILE__, in size_t line = __LINE__) @trusted { 55 import unit_threaded.property: utCheckCustom = checkCustom; 56 utCheckCustom!(Generator, Predicate)(numFuncCalls, file, line); 57 } 58 59 60 interface Output { 61 void send(in string output) @safe; 62 void flush() @safe; 63 } 64 65 class TestCase { 66 abstract void test(); 67 void setup() {} 68 void shutdown() {} 69 static TestCase currentTest() { return new class TestCase { override void test() {}}; } 70 Output getWriter() { return new class Output { override void send(in string output) {} override void flush() {}}; } 71 } 72 73 74 auto mock(T)() { 75 import unit_threaded.mock: utMock = mock; 76 return utMock!T; 77 } 78 79 auto mockStruct(T...)(auto ref T returns) { 80 import unit_threaded.mock: utMockStruct = mockStruct; 81 return utMockStruct(returns); 82 } 83 84 void shouldBeTrue(E)(lazy E condition, in string file = __FILE__, in size_t line = __LINE__) { 85 assert_(cast(bool)condition(), file, line); 86 } 87 88 void shouldBeFalse(E)(lazy E condition, in string file = __FILE__, in size_t line = __LINE__) { 89 assert_(!cast(bool)condition(), file, line); 90 } 91 92 void shouldEqual(V, E)(auto ref V value, auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 93 94 void checkInputRange(T)(auto ref const(T) _) @trusted { 95 auto obj = cast(T)_; 96 bool e = obj.empty; 97 auto f = obj.front; 98 obj.popFront; 99 } 100 enum isInputRange(T) = is(T: Elt[], Elt) || is(typeof(checkInputRange(T.init))); 101 102 static if(is(V == class)) { 103 assert_(value.tupleof == expected.tupleof, file, line); 104 } else static if(isInputRange!V && isInputRange!E) { 105 auto ref unqual(T)(auto ref const(T) obj) @trusted { 106 static if(is(T == void[])) 107 return cast(ubyte[])obj; 108 else 109 return cast(T)obj; 110 } 111 import std.algorithm: equal; 112 assert_(equal(unqual(value), unqual(expected)), file, line); 113 } else { 114 assert_(cast(const)value == cast(const)expected, file, line); 115 } 116 } 117 118 void shouldNotEqual(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 119 assert_(value != expected, file, line); 120 } 121 122 void shouldBeNull(T)(in auto ref T value, in string file = __FILE__, in size_t line = __LINE__) { 123 assert_(value is null, file, line); 124 } 125 126 void shouldNotBeNull(T)(in auto ref T value, in string file = __FILE__, in size_t line = __LINE__) { 127 assert_(value !is null, file, line); 128 } 129 130 enum isLikeAssociativeArray(T, K) = is(typeof({ 131 if(K.init in T) { } 132 if(K.init !in T) { } 133 })); 134 static assert(isLikeAssociativeArray!(string[string], string)); 135 static assert(!isLikeAssociativeArray!(string[string], int)); 136 137 138 void shouldBeIn(T, U)(in auto ref T value, in auto ref U container, in string file = __FILE__, in size_t line = __LINE__) 139 if(isLikeAssociativeArray!U) { 140 assert_(cast(bool)(value in container), file, line); 141 } 142 143 void shouldBeIn(T, U)(in auto ref T value, U container, in string file = __FILE__, in size_t line = __LINE__) 144 if (!isLikeAssociativeArray!(U, T)) 145 { 146 import std.algorithm: find; 147 import std.array: empty; 148 assert_(!find(container, value).empty, file, line); 149 } 150 151 void shouldNotBeIn(T, U)(in auto ref T value, in auto ref U container, in string file = __FILE__, in size_t line = __LINE__) 152 if(isLikeAssociativeArray!U) { 153 assert_(!cast(bool)(value in container), file, line); 154 } 155 156 void shouldNotBeIn(T, U)(in auto ref T value, U container, in string file = __FILE__, in size_t line = __LINE__) 157 if (!isLikeAssociativeArray!(U, T)) 158 { 159 import std.algorithm: find; 160 import std.array: empty; 161 assert_(find(container, value).empty, file, line); 162 } 163 164 void shouldThrow(T : Throwable = Exception, E) 165 (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) { 166 () @trusted { 167 try { 168 expr(); 169 assert_(false, file, line); 170 } catch(T _) { 171 172 } 173 }(); 174 } 175 176 void shouldThrowExactly(T : Throwable = Exception, E)(lazy E expr, 177 in string file = __FILE__, in size_t line = __LINE__) 178 { 179 () @trusted { 180 try { 181 expr(); 182 assert_(false, file, line); 183 } catch(T _) { 184 //Object.opEquals is @system and impure 185 const sameType = () @trusted { return threw.typeInfo == typeid(T); }(); 186 assert_(sameType, file, line); 187 } 188 }(); 189 } 190 191 void shouldNotThrow(T: Throwable = Exception, E) 192 (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) { 193 () @trusted { 194 try 195 expr(); 196 catch(T _) 197 assert_(false, file, line); 198 }(); 199 } 200 201 void shouldThrowWithMessage(T : Throwable = Exception, E)(lazy E expr, 202 string msg, 203 string file = __FILE__, 204 size_t line = __LINE__) { 205 () @trusted { 206 try { 207 expr(); 208 assert_(false, file, line); 209 } catch(T ex) { 210 assert_(ex.msg == msg, file, line); 211 } 212 }(); 213 } 214 215 void shouldApproxEqual(V, E)(in V value, in E expected, string file = __FILE__, size_t line = __LINE__) { 216 import std.math: approxEqual; 217 assert_(approxEqual(value, expected), file, line); 218 } 219 220 void shouldBeEmpty(R)(in auto ref R rng, in string file = __FILE__, in size_t line = __LINE__) { 221 import std.range: isInputRange; 222 import std.traits: isAssociativeArray; 223 import std.array; 224 225 static if(isInputRange!R) 226 assert_(rng.empty, file, line); 227 else static if(isAssociativeArray!R) 228 () @trusted { assert_(rng.keys.empty, file, line); }(); 229 else 230 static assert(false, "Cannot call shouldBeEmpty on " ~ R.stringof); 231 } 232 233 void shouldNotBeEmpty(R)(in auto ref R rng, in string file = __FILE__, in size_t line = __LINE__) { 234 import std.range: isInputRange; 235 import std.traits: isAssociativeArray; 236 import std.array; 237 238 static if(isInputRange!R) 239 assert_(!rnd.empty, file, line); 240 else static if(isAssociativeArray!R) 241 () @trusted { assert_(!rng.keys.empty, file, line); }(); 242 else 243 static assert(false, "Cannot call shouldBeEmpty on " ~ R.stringof); 244 } 245 246 void shouldBeGreaterThan(T, U)(in auto ref T t, in auto ref U u, 247 in string file = __FILE__, in size_t line = __LINE__) 248 { 249 assert_(t > u, file, line); 250 } 251 252 void shouldBeSmallerThan(T, U)(in auto ref T t, in auto ref U u, 253 in string file = __FILE__, in size_t line = __LINE__) 254 { 255 assert_(t < u, file, line); 256 } 257 258 void shouldBeSameSetAs(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 259 assert_(isSameSet(value, expected), file, line); 260 } 261 262 void shouldNotBeSameSetAs(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 263 assert_(!isSameSet(value, expected), file, line); 264 } 265 266 private bool isSameSet(T, U)(in auto ref T t, in auto ref U u) { 267 import std.array: array; 268 import std.algorithm: canFind; 269 270 //sort makes the element types have to implement opCmp 271 //instead, try one by one 272 auto ta = t.array; 273 auto ua = u.array; 274 if (ta.length != ua.length) return false; 275 foreach(element; ta) 276 { 277 if (!ua.canFind(element)) return false; 278 } 279 280 return true; 281 } 282 283 void shouldBeSameJsonAs(in string actual, 284 in string expected, 285 in string file = __FILE__, 286 in size_t line = __LINE__) 287 @trusted // not @safe pure due to parseJSON 288 { 289 import std.json: parseJSON, JSONException; 290 291 auto parse(in string str) { 292 try 293 return str.parseJSON; 294 catch(JSONException ex) { 295 assert_(false, "Failed to parse " ~ str, file, line); 296 } 297 assert(0); 298 } 299 300 assert_(parse(actual) == parse(expected), file, line); 301 } 302 303 304 private void assert_(in bool value, in string file, in size_t line) @safe pure { 305 assert_(value, "Assertion failure", file, line); 306 } 307 308 private void assert_(bool value, in string message, in string file, in size_t line) @trusted pure { 309 if(!value) 310 throw new Exception(message, file, line); 311 } 312 313 void fail(in string output, in string file, in size_t line) @safe pure { 314 assert_(false, output, file, line); 315 }