diff options
Diffstat (limited to 'libphobos/testsuite/libphobos.phobos/std_exception.d')
-rw-r--r-- | libphobos/testsuite/libphobos.phobos/std_exception.d | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/libphobos/testsuite/libphobos.phobos/std_exception.d b/libphobos/testsuite/libphobos.phobos/std_exception.d new file mode 100644 index 0000000..8f97320 --- /dev/null +++ b/libphobos/testsuite/libphobos.phobos/std_exception.d @@ -0,0 +1,543 @@ +@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); +} + |