From d2be06f9c113812a1ffd56e0fdc25c28cdbf0abf Mon Sep 17 00:00:00 2001
From: Richard Carlsson
Date: Sat, 29 Oct 2016 17:24:25 +0200
Subject: Add comment-versions of assert macros
For all assert macros in assert.hrl, add corresponding versions with an
additional last Comment argument, assumed to be chardata. If an
exception occurs, it will contain an entry {comment, Comment}, which a
reporting tool may pretty-print for better readability.
---
lib/stdlib/doc/src/assert_hrl.xml | 33 +++++--
lib/stdlib/include/assert.hrl | 176 +++++++++++++++++++++++++++++++++++++-
2 files changed, 201 insertions(+), 8 deletions(-)
diff --git a/lib/stdlib/doc/src/assert_hrl.xml b/lib/stdlib/doc/src/assert_hrl.xml
index e2dfc2ab9b..a29f6d6ad7 100644
--- a/lib/stdlib/doc/src/assert_hrl.xml
+++ b/lib/stdlib/doc/src/assert_hrl.xml
@@ -28,7 +28,7 @@
- assert.hrl.xml
+ assert.hrl
Assert macros.
The include file assert.hrl provides macros for inserting
@@ -49,25 +49,33 @@
entries in the Info list are optional; do not rely programatically
on any of them being present.
+ Each assert macro has a corresponding version with an extra argument,
+ for adding comments to assertions. These can for example be printed as
+ part of error reports, to clarify the meaning of the check that
+ failed. For example, ?assertEqual(0, fib(0), "Fibonacci is defined
+ for zero"). The comment text can be any character data (string,
+ UTF8-binary, or deep list of such data), and will be included in the
+ error term as {comment, Text}.
+
If the macro NOASSERT is defined when assert.hrl is read
by the compiler, the macros are defined as equivalent to the atom
- ok. The test is not performed and there is no cost at runtime.
+ ok. The test will not be performed and there is no cost at runtime.
For example, using erlc to compile your modules, the following
- disable all assertions:
+ disables all assertions:
erlc -DNOASSERT=true *.erl
- The value of NOASSERT does not matter, only the fact that it is
- defined.
+ (The value of NOASSERT does not matter, only the fact that it is
+ defined.)
A few other macros also have effect on the enabling or disabling of
assertions:
- If NODEBUG is defined, it implies NOASSERT, unless
- DEBUG is also defined, which is assumed to take precedence.
+ If NODEBUG is defined, it implies NOASSERT (unless
+ DEBUG is also defined, which overrides NODEBUG).
If ASSERT is defined, it overrides NOASSERT, that
is, the assertions remain enabled.
@@ -84,16 +92,19 @@ erlc -DNOASSERT=true *.erl
Macros
assert(BoolExpr)
+ assert(BoolExpr, Comment)
-
Tests that BoolExpr completes normally returning
true.
assertNot(BoolExpr)
+ assertNot(BoolExpr, Comment)
-
Tests that BoolExpr completes normally returning
false.
assertMatch(GuardedPattern, Expr)
+ assertMatch(GuardedPattern, Expr, Comment)
-
Tests that Expr completes normally yielding a value that
matches GuardedPattern, for example:
@@ -104,6 +115,7 @@ erlc -DNOASSERT=true *.erl
?assertMatch({bork, X} when X > 0, f())
assertNotMatch(GuardedPattern, Expr)
+ assertNotMatch(GuardedPattern, Expr, Comment)
-
Tests that Expr completes normally yielding a value that does
not match GuardedPattern.
@@ -111,16 +123,19 @@ erlc -DNOASSERT=true *.erl
when part.
assertEqual(ExpectedValue, Expr)
+ assertEqual(ExpectedValue, Expr, Comment)
-
Tests that Expr completes normally yielding a value that is
exactly equal to ExpectedValue.
assertNotEqual(ExpectedValue, Expr)
+ assertNotEqual(ExpectedValue, Expr, Comment)
-
Tests that Expr completes normally yielding a value that is
not exactly equal to ExpectedValue.
assertException(Class, Term, Expr)
+ assertException(Class, Term, Expr, Comment)
-
Tests that Expr completes abnormally with an exception of type
Class and with the associated Term. The assertion fails
@@ -130,6 +145,7 @@ erlc -DNOASSERT=true *.erl
patterns, as in assertMatch.
assertNotException(Class, Term, Expr)
+ assertNotException(Class, Term, Expr, Comment)
-
Tests that Expr does not evaluate abnormally with an
exception of type Class and with the associated Term.
@@ -139,14 +155,17 @@ erlc -DNOASSERT=true *.erl
be guarded patterns.
assertError(Term, Expr)
+ assertError(Term, Expr, Comment)
-
Equivalent to assertException(error, Term, Expr)
assertExit(Term, Expr)
+ assertExit(Term, Expr, Comment)
-
Equivalent to assertException(exit, Term, Expr)
assertThrow(Term, Expr)
+ assertThrow(Term, Expr, Comment)
-
Equivalent to assertException(throw, Term, Expr)
diff --git a/lib/stdlib/include/assert.hrl b/lib/stdlib/include/assert.hrl
index 9e5d4eb598..5a752550ee 100644
--- a/lib/stdlib/include/assert.hrl
+++ b/lib/stdlib/include/assert.hrl
@@ -56,7 +56,8 @@
%% It is not possible to nest assert macros.
-ifdef(NOASSERT).
--define(assert(BoolExpr),ok).
+-define(assert(BoolExpr), ok).
+-define(assert(BoolExpr, Comment), ok).
-else.
%% The assert macro is written the way it is so as not to cause warnings
%% for clauses that cannot match, even if the expression is a constant or
@@ -79,11 +80,31 @@
end
end)())
end).
+-define(assert(BoolExpr, Comment),
+ begin
+ ((fun () ->
+ __T = is_process_alive(self()), % cheap source of truth
+ case (BoolExpr) of
+ __T -> ok;
+ __V -> erlang:error({assert,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??BoolExpr)},
+ {expected, true},
+ case not __T of
+ __V -> {value, false};
+ _ -> {not_boolean, __V}
+ end]})
+ end
+ end)())
+ end).
-endif.
%% This is the inverse case of assert, for convenience.
-ifdef(NOASSERT).
-define(assertNot(BoolExpr),ok).
+-define(assertNot(BoolExpr, Comment), ok).
-else.
-define(assertNot(BoolExpr),
begin
@@ -103,12 +124,32 @@
end
end)())
end).
+-define(assertNot(BoolExpr, Comment),
+ begin
+ ((fun () ->
+ __F = not is_process_alive(self()),
+ case (BoolExpr) of
+ __F -> ok;
+ __V -> erlang:error({assert,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??BoolExpr)},
+ {expected, false},
+ case not __F of
+ __V -> {value, true};
+ _ -> {not_boolean, __V}
+ end]})
+ end
+ end)())
+ end).
-endif.
%% This is mostly a convenience which gives more detailed reports.
%% Note: Guard is a guarded pattern, and can not be used for value.
-ifdef(NOASSERT).
-define(assertMatch(Guard, Expr), ok).
+-define(assertMatch(Guard, Expr, Comment), ok).
-else.
-define(assertMatch(Guard, Expr),
begin
@@ -124,11 +165,27 @@
end
end)())
end).
+-define(assertMatch(Guard, Expr, Comment),
+ begin
+ ((fun () ->
+ case (Expr) of
+ Guard -> ok;
+ __V -> erlang:error({assertMatch,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {pattern, (??Guard)},
+ {value, __V}]})
+ end
+ end)())
+ end).
-endif.
%% This is the inverse case of assertMatch, for convenience.
-ifdef(NOASSERT).
-define(assertNotMatch(Guard, Expr), ok).
+-define(assertNotMatch(Guard, Expr, Comment), ok).
-else.
-define(assertNotMatch(Guard, Expr),
begin
@@ -145,12 +202,29 @@
end
end)())
end).
+-define(assertNotMatch(Guard, Expr, Comment),
+ begin
+ ((fun () ->
+ __V = (Expr),
+ case __V of
+ Guard -> erlang:error({assertNotMatch,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {pattern, (??Guard)},
+ {value, __V}]});
+ _ -> ok
+ end
+ end)())
+ end).
-endif.
%% This is a convenience macro which gives more detailed reports when
%% the expected LHS value is not a pattern, but a computed value
-ifdef(NOASSERT).
-define(assertEqual(Expect, Expr), ok).
+-define(assertEqual(Expect, Expr, Comment), ok).
-else.
-define(assertEqual(Expect, Expr),
begin
@@ -167,11 +241,28 @@
end
end)())
end).
+-define(assertEqual(Expect, Expr, Comment),
+ begin
+ ((fun () ->
+ __X = (Expect),
+ case (Expr) of
+ __X -> ok;
+ __V -> erlang:error({assertEqual,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {expected, __X},
+ {value, __V}]})
+ end
+ end)())
+ end).
-endif.
%% This is the inverse case of assertEqual, for convenience.
-ifdef(NOASSERT).
-define(assertNotEqual(Unexpected, Expr), ok).
+-define(assertNotEqual(Unexpected, Expr, Comment), ok).
-else.
-define(assertNotEqual(Unexpected, Expr),
begin
@@ -187,12 +278,28 @@
end
end)())
end).
+-define(assertNotEqual(Unexpected, Expr, Comment),
+ begin
+ ((fun () ->
+ __X = (Unexpected),
+ case (Expr) of
+ __X -> erlang:error({assertNotEqual,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {value, __X}]});
+ _ -> ok
+ end
+ end)())
+ end).
-endif.
%% Note: Class and Term are patterns, and can not be used for value.
%% Term can be a guarded pattern, but Class cannot.
-ifdef(NOASSERT).
-define(assertException(Class, Term, Expr), ok).
+-define(assertException(Class, Term, Expr, Comment), ok).
-else.
-define(assertException(Class, Term, Expr),
begin
@@ -222,17 +329,54 @@
end
end)())
end).
+-define(assertException(Class, Term, Expr, Comment),
+ begin
+ ((fun () ->
+ try (Expr) of
+ __V -> erlang:error({assertException,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {pattern,
+ "{ "++(??Class)++" , "++(??Term)
+ ++" , [...] }"},
+ {unexpected_success, __V}]})
+ catch
+ Class:Term -> ok;
+ __C:__T ->
+ erlang:error({assertException,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {pattern,
+ "{ "++(??Class)++" , "++(??Term)
+ ++" , [...] }"},
+ {unexpected_exception,
+ {__C, __T,
+ erlang:get_stacktrace()}}]})
+ end
+ end)())
+ end).
-endif.
-define(assertError(Term, Expr), ?assertException(error, Term, Expr)).
+-define(assertError(Term, Expr, Comment),
+ ?assertException(error, Term, Expr, Comment)).
-define(assertExit(Term, Expr), ?assertException(exit, Term, Expr)).
+-define(assertExit(Term, Expr, Comment),
+ ?assertException(exit, Term, Expr, Comment)).
-define(assertThrow(Term, Expr), ?assertException(throw, Term, Expr)).
+-define(assertThrow(Term, Expr, Comment),
+ ?assertException(throw, Term, Expr, Comment)).
%% This is the inverse case of assertException, for convenience.
%% Note: Class and Term are patterns, and can not be used for value.
%% Both Class and Term can be guarded patterns.
-ifdef(NOASSERT).
-define(assertNotException(Class, Term, Expr), ok).
+-define(assertNotException(Class, Term, Expr, Comment), ok).
-else.
-define(assertNotException(Class, Term, Expr),
begin
@@ -263,6 +407,36 @@
end
end)())
end).
+-define(assertNotException(Class, Term, Expr, Comment),
+ begin
+ ((fun () ->
+ try (Expr) of
+ _ -> ok
+ catch
+ __C:__T ->
+ case __C of
+ Class ->
+ case __T of
+ Term ->
+ erlang:error({assertNotException,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {comment, (Comment)},
+ {expression, (??Expr)},
+ {pattern,
+ "{ "++(??Class)++" , "
+ ++(??Term)++" , [...] }"},
+ {unexpected_exception,
+ {__C, __T,
+ erlang:get_stacktrace()
+ }}]});
+ _ -> ok
+ end;
+ _ -> ok
+ end
+ end
+ end)())
+ end).
-endif.
-endif. % ASSERT_HRL
--
cgit v1.2.3