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