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