| @system unittest | |
| { | |
| import std.exception; | |
| import core.stdc.stdlib : malloc, free; | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map, splitter; | |
| import std.algorithm.searching : endsWith; | |
| import std.conv : ConvException, to; | |
| import std.range : front, retro; | |
| // use enforce like assert | |
| int a = 3; | |
| enforce(a > 2, "a needs to be higher than 2."); | |
| // enforce can throw a custom exception | |
| enforce!ConvException(a > 2, "a needs to be higher than 2."); | |
| // enforce will return it's input | |
| enum size = 42; | |
| auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; | |
| scope(exit) free(memory.ptr); | |
| // collectException can be used to test for exceptions | |
| Exception e = collectException("abc".to!int); | |
| assert(e.file.endsWith("conv.d")); | |
| // and just for the exception message | |
| string msg = collectExceptionMsg("abc".to!int); | |
| assert(msg == "Unexpected 'a' when converting from type string to type int"); | |
| // assertThrown can be used to assert that an exception is thrown | |
| assertThrown!ConvException("abc".to!int); | |
| // ifThrown can be used to provide a default value if an exception is thrown | |
| assert("x".to!int().ifThrown(0) == 0); | |
| // handle is a more advanced version of ifThrown for ranges | |
| auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a)); | |
| auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); | |
| assert(h.equal([12, 0, 54])); | |
| assertThrown!ConvException(h.retro.equal([54, 0, 12])); | |
| // basicExceptionCtors avoids the boilerplate when creating custom exceptions | |
| static class MeaCulpa : Exception | |
| { | |
| mixin basicExceptionCtors; | |
| } | |
| e = collectException((){throw new MeaCulpa("diagnostic message");}()); | |
| assert(e.msg == "diagnostic message"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__ - 3); | |
| // assumeWontThrow can be used to cast throwing code into `nothrow` | |
| void exceptionFreeCode() nothrow | |
| { | |
| // auto-decoding only throws if an invalid UTF char is given | |
| assumeWontThrow("abc".front); | |
| } | |
| // assumeUnique can be used to cast mutable instance to an `immutable` one | |
| // use with care | |
| char[] str = " mutable".dup; | |
| str[0 .. 2] = "im"; | |
| immutable res = assumeUnique(str); | |
| assert(res == "immutable"); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| import core.exception : AssertError; | |
| import std.string; | |
| assertNotThrown!StringException(enforce!StringException(true, "Error!")); | |
| //Exception is the default. | |
| assertNotThrown(enforce!StringException(true, "Error!")); | |
| assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( | |
| enforce!StringException(false, "Error!"))) == | |
| `assertNotThrown failed: StringException was thrown: Error!`); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| import core.exception : AssertError; | |
| import std.string; | |
| assertThrown!StringException(enforce!StringException(false, "Error!")); | |
| //Exception is the default. | |
| assertThrown(enforce!StringException(false, "Error!")); | |
| assert(collectExceptionMsg!AssertError(assertThrown!StringException( | |
| enforce!StringException(true, "Error!"))) == | |
| `assertThrown failed: No StringException was thrown.`); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| import core.stdc.stdlib : malloc, free; | |
| import std.conv : ConvException, to; | |
| // use enforce like assert | |
| int a = 3; | |
| enforce(a > 2, "a needs to be higher than 2."); | |
| // enforce can throw a custom exception | |
| enforce!ConvException(a > 2, "a needs to be higher than 2."); | |
| // enforce will return it's input | |
| enum size = 42; | |
| auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; | |
| scope(exit) free(memory.ptr); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| assertNotThrown(enforce(true, new Exception("this should not be thrown"))); | |
| assertThrown(enforce(false, new Exception("this should be thrown"))); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| assert(enforce(123) == 123); | |
| try | |
| { | |
| enforce(false, "error"); | |
| assert(false); | |
| } | |
| catch (Exception e) | |
| { | |
| assert(e.msg == "error"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__-7); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| import std.conv : ConvException; | |
| alias convEnforce = enforce!ConvException; | |
| assertNotThrown(convEnforce(true)); | |
| assertThrown!ConvException(convEnforce(false, "blah")); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| import core.stdc.stdio : fclose, fgets, fopen; | |
| import std.file : thisExePath; | |
| import std.string : toStringz; | |
| auto f = fopen(thisExePath.toStringz, "r").errnoEnforce; | |
| scope(exit) fclose(f); | |
| char[100] buf; | |
| auto line = fgets(buf.ptr, buf.length, f); | |
| enforce(line !is null); // expect a non-empty line | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| int b; | |
| int foo() { throw new Exception("blah"); } | |
| assert(collectException(foo(), b)); | |
| version (D_NoBoundsChecks) {} | |
| else | |
| { | |
| // check for out of bounds error | |
| int[] a = new int[3]; | |
| import core.exception : RangeError; | |
| assert(collectException!RangeError(a[4], b)); | |
| } | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| int foo() { throw new Exception("blah"); } | |
| assert(collectException(foo()).msg == "blah"); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| void throwFunc() { throw new Exception("My Message."); } | |
| assert(collectExceptionMsg(throwFunc()) == "My Message."); | |
| void nothrowFunc() {} | |
| assert(collectExceptionMsg(nothrowFunc()) is null); | |
| void throwEmptyFunc() { throw new Exception(""); } | |
| assert(collectExceptionMsg(throwEmptyFunc()) == emptyExceptionMsg); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| int[] arr = new int[1]; | |
| auto arr1 = arr.assumeUnique; | |
| static assert(is(typeof(arr1) == immutable(int)[])); | |
| assert(arr == null); | |
| assert(arr1 == [0]); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| int[string] arr = ["a":1]; | |
| auto arr1 = arr.assumeUnique; | |
| static assert(is(typeof(arr1) == immutable(int[string]))); | |
| assert(arr == null); | |
| assert(arr1.keys == ["a"]); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| import std.math.algebraic : sqrt; | |
| // This function may throw. | |
| int squareRoot(int x) | |
| { | |
| if (x < 0) | |
| throw new Exception("Tried to take root of negative number"); | |
| return cast(int) sqrt(cast(double) x); | |
| } | |
| // This function never throws. | |
| int computeLength(int x, int y) nothrow | |
| { | |
| // Since x*x + y*y is always positive, we can safely assume squareRoot | |
| // won't throw, and use it to implement this nothrow function. If it | |
| // does throw (e.g., if x*x + y*y overflows a 32-bit value), then the | |
| // program will terminate. | |
| return assumeWontThrow(squareRoot(x*x + y*y)); | |
| } | |
| assert(computeLength(3, 4) == 5); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| int i = 0; | |
| int* p = null; | |
| assert(!p.doesPointTo(i)); | |
| p = &i; | |
| assert( p.doesPointTo(i)); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| struct S | |
| { | |
| int v; | |
| int* p; | |
| } | |
| int i; | |
| auto s = S(0, &i); | |
| // structs and unions "own" their members | |
| // pointsTo will answer true if one of the members pointsTo. | |
| assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed. | |
| assert( s.p.doesPointTo(i)); //i is pointed by s.p. | |
| assert( s .doesPointTo(i)); //which means i is pointed by s itself. | |
| // Unions will behave exactly the same. Points to will check each "member" | |
| // individually, even if they share the same memory | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| int i; | |
| // trick the compiler when initializing slice | |
| // https://issues.dlang.org/show_bug.cgi?id=18637 | |
| int* p = &i; | |
| int[] slice = [0, 1, 2, 3, 4]; | |
| int[5] arr = [0, 1, 2, 3, 4]; | |
| int*[] slicep = [p]; | |
| int*[1] arrp = [&i]; | |
| // A slice points to all of its members: | |
| assert( slice.doesPointTo(slice[3])); | |
| assert(!slice[0 .. 2].doesPointTo(slice[3])); // Object 3 is outside of the | |
| // slice [0 .. 2] | |
| // Note that a slice will not take into account what its members point to. | |
| assert( slicep[0].doesPointTo(i)); | |
| assert(!slicep .doesPointTo(i)); | |
| // static arrays are objects that own their members, just like structs: | |
| assert(!arr.doesPointTo(arr[0])); // arr[0] is just a member of arr, so not | |
| // pointed. | |
| assert( arrp[0].doesPointTo(i)); // i is pointed by arrp[0]. | |
| assert( arrp .doesPointTo(i)); // which means i is pointed by arrp | |
| // itself. | |
| // Notice the difference between static and dynamic arrays: | |
| assert(!arr .doesPointTo(arr[0])); | |
| assert( arr[].doesPointTo(arr[0])); | |
| assert( arrp .doesPointTo(i)); | |
| assert(!arrp[].doesPointTo(i)); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| class C | |
| { | |
| this(int* p){this.p = p;} | |
| int* p; | |
| } | |
| int i; | |
| C a = new C(&i); | |
| C b = a; | |
| // Classes are a bit particular, as they are treated like simple pointers | |
| // to a class payload. | |
| assert( a.p.doesPointTo(i)); // a.p points to i. | |
| assert(!a .doesPointTo(i)); // Yet a itself does not point i. | |
| //To check the class payload itself, iterate on its members: | |
| () | |
| { | |
| import std.traits : Fields; | |
| foreach (index, _; Fields!C) | |
| if (doesPointTo(a.tupleof[index], i)) | |
| return; | |
| assert(0); | |
| }(); | |
| // To check if a class points a specific payload, a direct memmory check | |
| // can be done: | |
| auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a; | |
| assert(b.doesPointTo(*aLoc)); // b points to where a is pointing | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| import core.stdc.errno : EAGAIN; | |
| auto ex = new ErrnoException("oh no", EAGAIN); | |
| assert(ex.errno == EAGAIN); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| import core.stdc.errno : errno, EAGAIN; | |
| auto old = errno; | |
| scope(exit) errno = old; | |
| // fake that errno got set by the callee | |
| errno = EAGAIN; | |
| auto ex = new ErrnoException("oh no"); | |
| assert(ex.errno == EAGAIN); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| import std.conv : to; | |
| assert("x".to!int.ifThrown(0) == 0); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| import std.conv : ConvException, to; | |
| string s = "true"; | |
| assert(s.to!int.ifThrown(cast(int) s.to!double) | |
| .ifThrown(cast(int) s.to!bool) == 1); | |
| s = "2.0"; | |
| assert(s.to!int.ifThrown(cast(int) s.to!double) | |
| .ifThrown(cast(int) s.to!bool) == 2); | |
| // Respond differently to different types of errors | |
| alias orFallback = (lazy a) => a.ifThrown!ConvException("not a number") | |
| .ifThrown!Exception("number too small"); | |
| assert(orFallback(enforce("x".to!int < 1).to!string) == "not a number"); | |
| assert(orFallback(enforce("2".to!int < 1).to!string) == "number too small"); | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| // null and new Object have a common type(Object). | |
| static assert(is(typeof(null.ifThrown(new Object())) == Object)); | |
| static assert(is(typeof((new Object()).ifThrown(null)) == Object)); | |
| // 1 and new Object do not have a common type. | |
| static assert(!__traits(compiles, 1.ifThrown(new Object()))); | |
| static assert(!__traits(compiles, (new Object()).ifThrown(1))); | |
| } | |
| @system unittest | |
| { | |
| import std.exception; | |
| import std.format : format; | |
| assert("%s".format.ifThrown!Exception(e => typeid(e).name) == "std.format.FormatException"); | |
| } | |
| pure @safe unittest | |
| { | |
| import std.exception; | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map, splitter; | |
| import std.conv : to, ConvException; | |
| auto s = "12,1337z32,54,2,7,9,1z,6,8"; | |
| // The next line composition will throw when iterated | |
| // as some elements of the input do not convert to integer | |
| auto r = s.splitter(',').map!(a => to!int(a)); | |
| // Substitute 0 for cases of ConvException | |
| auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); | |
| assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); | |
| } | |
| pure @safe unittest | |
| { | |
| import std.exception; | |
| import std.algorithm.comparison : equal; | |
| import std.range : retro; | |
| import std.utf : UTFException; | |
| auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit | |
| auto handled = str.handle!(UTFException, RangePrimitive.access, | |
| (e, r) => ' '); // Replace invalid code points with spaces | |
| assert(handled.equal("hello world")); // `front` is handled, | |
| assert(handled.retro.equal("dlrow olleh")); // as well as `back` | |
| } | |
| pure @safe unittest | |
| { | |
| import std.exception; | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map, splitter; | |
| import std.conv : to, ConvException; | |
| auto s = "12,1337z32,54,2,7,9,1z,6,8"; | |
| // The next line composition will throw when iterated | |
| // as some elements of the input do not convert to integer | |
| auto r = s.splitter(',').map!(a => to!int(a)); | |
| // Substitute 0 for cases of ConvException | |
| auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); | |
| assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); | |
| } | |
| pure @safe unittest | |
| { | |
| import std.exception; | |
| import std.algorithm.comparison : equal; | |
| import std.range : retro; | |
| import std.utf : UTFException; | |
| auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit | |
| auto handled = str.handle!(UTFException, RangePrimitive.access, | |
| (e, r) => ' '); // Replace invalid code points with spaces | |
| assert(handled.equal("hello world")); // `front` is handled, | |
| assert(handled.retro.equal("dlrow olleh")); // as well as `back` | |
| } | |
| @safe unittest | |
| { | |
| import std.exception; | |
| class MeaCulpa: Exception | |
| { | |
| /// | |
| mixin basicExceptionCtors; | |
| } | |
| try | |
| throw new MeaCulpa("test"); | |
| catch (MeaCulpa e) | |
| { | |
| assert(e.msg == "test"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__ - 5); | |
| } | |
| } | |
| @safe pure nothrow unittest | |
| { | |
| import std.exception; | |
| class TestException : Exception { mixin basicExceptionCtors; } | |
| auto e = new Exception("msg"); | |
| auto te1 = new TestException("foo"); | |
| auto te2 = new TestException("foo", e); | |
| } | |