aboutsummaryrefslogtreecommitdiff
path: root/libphobos/testsuite/libphobos.phobos/std_exception.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/testsuite/libphobos.phobos/std_exception.d')
-rw-r--r--libphobos/testsuite/libphobos.phobos/std_exception.d543
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);
+}
+