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 static if(is(V == class)) { 95 assert_(value.tupleof == expected.tupleof, file, line); 96 } else static if(!__traits(compiles, value == expected)) { 97 import std.algorithm: equal; 98 assert_(equal(value, expected), file, line); 99 } else { 100 assert_(value == expected, file, line); 101 } 102 } 103 104 void shouldNotEqual(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 105 assert_(value != expected, file, line); 106 } 107 108 void shouldBeNull(T)(in auto ref T value, in string file = __FILE__, in size_t line = __LINE__) { 109 assert_(value is null, file, line); 110 } 111 112 void shouldNotBeNull(T)(in auto ref T value, in string file = __FILE__, in size_t line = __LINE__) { 113 assert_(value !is null, file, line); 114 } 115 116 enum isLikeAssociativeArray(T, K) = is(typeof({ 117 if(K.init in T) { } 118 if(K.init !in T) { } 119 })); 120 static assert(isLikeAssociativeArray!(string[string], string)); 121 static assert(!isLikeAssociativeArray!(string[string], int)); 122 123 124 void shouldBeIn(T, U)(in auto ref T value, in auto ref U container, in string file = __FILE__, in size_t line = __LINE__) 125 if(isLikeAssociativeArray!U) { 126 assert_(cast(bool)(value in container), file, line); 127 } 128 129 void shouldBeIn(T, U)(in auto ref T value, U container, in string file = __FILE__, in size_t line = __LINE__) 130 if (!isLikeAssociativeArray!(U, T)) 131 { 132 import std.algorithm: find; 133 import std.array: empty; 134 assert_(!find(container, value).empty, file, line); 135 } 136 137 void shouldNotBeIn(T, U)(in auto ref T value, in auto ref U container, in string file = __FILE__, in size_t line = __LINE__) 138 if(isLikeAssociativeArray!U) { 139 assert_(!cast(bool)(value in container), file, line); 140 } 141 142 void shouldNotBeIn(T, U)(in auto ref T value, U container, in string file = __FILE__, in size_t line = __LINE__) 143 if (!isLikeAssociativeArray!(U, T)) 144 { 145 import std.algorithm: find; 146 import std.array: empty; 147 assert_(find(container, value).empty, file, line); 148 } 149 150 void shouldThrow(T : Throwable = Exception, E) 151 (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) { 152 () @trusted { 153 try { 154 expr(); 155 assert_(false, file, line); 156 } catch(T _) { 157 158 } 159 }(); 160 } 161 162 void shouldThrowExactly(T : Throwable = Exception, E)(lazy E expr, 163 in string file = __FILE__, in size_t line = __LINE__) 164 { 165 () @trusted { 166 try { 167 expr(); 168 assert_(false, file, line); 169 } catch(T _) { 170 //Object.opEquals is @system and impure 171 const sameType = () @trusted { return threw.typeInfo == typeid(T); }(); 172 assert_(sameType, file, line); 173 } 174 }(); 175 } 176 177 void shouldNotThrow(T: Throwable = Exception, E) 178 (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) { 179 () @trusted { 180 try 181 expr(); 182 catch(T _) 183 assert_(false, file, line); 184 }(); 185 } 186 187 void shouldThrowWithMessage(T : Throwable = Exception, E)(lazy E expr, 188 string msg, 189 string file = __FILE__, 190 size_t line = __LINE__) { 191 () @trusted { 192 try { 193 expr(); 194 assert_(false, file, line); 195 } catch(T ex) { 196 assert_(ex.msg == msg, file, line); 197 } 198 }(); 199 } 200 201 void shouldApproxEqual(V, E)(in V value, in E expected, string file = __FILE__, size_t line = __LINE__) { 202 import std.math: approxEqual; 203 assert_(approxEqual(value, expected), file, line); 204 } 205 206 void shouldBeEmpty(R)(in auto ref R rng, in string file = __FILE__, in size_t line = __LINE__) { 207 import std.range: isInputRange; 208 import std.traits: isAssociativeArray; 209 import std.array; 210 211 static if(isInputRange!R) 212 assert_(rng.empty, file, line); 213 else static if(isAssociativeArray!R) 214 () @trusted { assert_(rng.keys.empty, file, line); }(); 215 else 216 static assert(false, "Cannot call shouldBeEmpty on " ~ R.stringof); 217 } 218 219 void shouldNotBeEmpty(R)(in auto ref R rng, in string file = __FILE__, in size_t line = __LINE__) { 220 import std.range: isInputRange; 221 import std.traits: isAssociativeArray; 222 import std.array; 223 224 static if(isInputRange!R) 225 assert_(!rnd.empty, file, line); 226 else static if(isAssociativeArray!R) 227 () @trusted { assert_(!rng.keys.empty, file, line); }(); 228 else 229 static assert(false, "Cannot call shouldBeEmpty on " ~ R.stringof); 230 } 231 232 void shouldBeGreaterThan(T, U)(in auto ref T t, in auto ref U u, 233 in string file = __FILE__, in size_t line = __LINE__) 234 { 235 assert_(t > u, file, line); 236 } 237 238 void shouldBeSmallerThan(T, U)(in auto ref T t, in auto ref U u, 239 in string file = __FILE__, in size_t line = __LINE__) 240 { 241 assert_(t < u, file, line); 242 } 243 244 void shouldBeSameSetAs(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 245 assert_(isSameSet(value, expected), file, line); 246 } 247 248 void shouldNotBeSameSetAs(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { 249 assert_(!isSameSet(value, expected), file, line); 250 } 251 252 private bool isSameSet(T, U)(in auto ref T t, in auto ref U u) { 253 import std.array: array; 254 import std.algorithm: canFind; 255 256 //sort makes the element types have to implement opCmp 257 //instead, try one by one 258 auto ta = t.array; 259 auto ua = u.array; 260 if (ta.length != ua.length) return false; 261 foreach(element; ta) 262 { 263 if (!ua.canFind(element)) return false; 264 } 265 266 return true; 267 } 268 269 void shouldBeSameJsonAs(in string actual, 270 in string expected, 271 in string file = __FILE__, 272 in size_t line = __LINE__) 273 @trusted // not @safe pure due to parseJSON 274 { 275 import std.json: parseJSON, JSONException; 276 277 auto parse(in string str) { 278 try 279 return str.parseJSON; 280 catch(JSONException ex) { 281 assert_(false, "Failed to parse " ~ str, file, line); 282 } 283 assert(0); 284 } 285 286 assert_(parse(actual) == parse(expected), file, line); 287 } 288 289 290 private void assert_(in bool value, in string file, in size_t line) @safe pure { 291 assert_(value, "Assertion failure", file, line); 292 } 293 294 private void assert_(bool value, in string message, in string file, in size_t line) @trusted pure { 295 if(!value) 296 throw new Exception(message, file, line); 297 } 298 299 void fail(in string output, in string file, in size_t line) @safe pure { 300 assert_(false, output, file, line); 301 }