Skip to content

Commit

Permalink
feat: add support to assert commands call not to receive errors (#287)
Browse files Browse the repository at this point in the history
Signed-off-by: Alfi Maulana <alfi.maulana.f@gmail.com>
  • Loading branch information
threeal authored Oct 21, 2024
1 parent fa946ac commit 855a4a8
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 85 deletions.
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,12 @@ Performs an assertion on the given command call.
```cmake
assert_call(
[CALL] <command> [<arguments>...]
EXPECT_ERROR [MATCHES|STREQUAL] <message>...)
[EXPECT_ERROR [MATCHES|STREQUAL] <message>...])
```

This function asserts the behavior of the function or macro named `<command>`, called with the specified `<arguments>`. It currently only supports asserting whether the given command receives error messages that satisfy the expected message. It captures all errors from the `message` function and compares them with the expected message. Each captured error is concatenated with new lines as separators.
This function asserts whether the function or macro named `<command>`, called with the specified `<arguments>`, does not receive any errors. Internally, the function captures all errors from the `message` function. Each captured error is concatenated with new lines as separators.

If `MATCHES` is specified, it asserts whether the captured messages match `<message>`. If `STREQUAL` is specified, it asserts whether the captured messages are equal to `<message>`. If neither is specified, it defaults to the `MATCHES` parameter.

If more than one `<message>` string is given, they are concatenated into a single message with no separators.
If `EXPECT_ERROR` is specified, it instead asserts whether the call to the function or macro received errors that satisfy the expected message. If `MATCHES` is specified, it asserts whether the received errors match `<message>`. If `STREQUAL` is specified, it asserts whether the received errors are equal to `<message>`. If neither is specified, it defaults to the `MATCHES` parameter. If more than one `<message>` string is given, they are concatenated into a single message with no separators.

#### Example

Expand All @@ -201,7 +199,7 @@ assert_call(
The above example asserts whether the call to `send_errors(first second)` receives errors equal to `first error\nsecond error`. If it does not receive any errors, it will throw the following error:

```
expected to receive an error message
expected to receive errors
```

### `assert_execute_process`
Expand Down
75 changes: 43 additions & 32 deletions cmake/Assertion.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -358,42 +358,45 @@ endfunction()
#
# assert_call(
# [CALL] <command> [<arguments>...]
# EXPECT_ERROR [MATCHES|STREQUAL] <message>...)
# [EXPECT_ERROR [MATCHES|STREQUAL] <message>...])
#
# This function asserts the behavior of the function or macro named `<command>`,
# called with the specified `<arguments>`. It currently only supports asserting
# whether the given command receives error messages that satisfy the expected
# message. It captures all errors from the `message` function and compares them
# with the expected message. Each captured error is concatenated with new lines
# as separators.
# This function asserts whether the function or macro named `<command>`, called
# with the specified `<arguments>`, does not receive any errors. Internally, the
# function captures all errors from the `message` function. Each captured error
# is concatenated with new lines as separators.
#
# If `MATCHES` is specified, it asserts whether the captured messages match
# `<message>`. If `STREQUAL` is specified, it asserts whether the captured
# messages are equal to `<message>`. If neither is specified, it defaults to the
# `MATCHES` parameter.
#
# If more than one `<message>` string is given, they are concatenated into a
# single message with no separators.
# If `EXPECT_ERROR` is specified, it instead asserts whether the call to the
# function or macro received errors that satisfy the expected message. If
# `MATCHES` is specified, it asserts whether the received errors match
# `<message>`. If `STREQUAL` is specified, it asserts whether the received
# errors are equal to `<message>`. If neither is specified, it defaults to the
# `MATCHES` parameter. If more than one `<message>` string is given, they are
# concatenated into a single message with no separators.
function(assert_call)
cmake_parse_arguments(PARSE_ARGV 0 ARG "" "" "CALL;EXPECT_ERROR")

if(NOT DEFINED ARG_CALL)
set(ARG_CALL ${ARG_UNPARSED_ARGUMENTS})
endif()

list(GET ARG_EXPECT_ERROR 0 OPERATOR)
if(OPERATOR MATCHES ^MATCHES|STREQUAL$)
list(REMOVE_AT ARG_EXPECT_ERROR 0)
if(DEFINED ARG_EXPECT_ERROR)
list(GET ARG_EXPECT_ERROR 0 EXPECTED_ERROR_OPERATOR)
if(EXPECTED_ERROR_OPERATOR MATCHES ^MATCHES|STREQUAL$)
list(REMOVE_AT ARG_EXPECT_ERROR 0)
else()
set(EXPECTED_ERROR_OPERATOR "MATCHES")
endif()
string(JOIN "" EXPECTED_ERROR ${ARG_EXPECT_ERROR})
else()
set(OPERATOR "MATCHES")
unset(EXPECTED_ERROR_OPERATOR)
unset(EXPECTED_ERROR)
endif()
string(JOIN "" EXPECTED_ERROR ${ARG_EXPECT_ERROR})

# Override the `message` function if it has not been overridden.
get_property(MESSAGE_MOCKED GLOBAL PROPERTY _message_mocked)
if(NOT MESSAGE_MOCKED)
# Override the `message` function to allow the behavior to be mocked by
# capturing an error message.
# capturing an error.
function(message MODE)
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "")

Expand All @@ -410,14 +413,14 @@ function(assert_call)
set_property(GLOBAL PROPERTY _message_mocked ON)
endif()

# Increase the level for capturing error messages.
# Increase the level for capturing errors.
if(_CAPTURE_LEVEL GREATER_EQUAL 0)
math(EXPR _CAPTURE_LEVEL "${_CAPTURE_LEVEL} + 1")
else()
set(_CAPTURE_LEVEL 1)
endif()

# Clear global property that hold the captured messages.
# Clear global property that hold the captured errors.
set_property(GLOBAL PROPERTY assert_captured_error_${_CAPTURE_LEVEL})

# Call the command with the specified arguments.
Expand All @@ -430,21 +433,29 @@ function(assert_call)
if(CAPTURED_ERROR_SET)
get_property(CAPTURED_ERROR GLOBAL
PROPERTY assert_captured_error_${_CAPTURE_LEVEL})
string(STRIP "${CAPTURED_ERROR}" CAPTURED_ERROR)

if(NOT "${CAPTURED_ERROR}" ${OPERATOR} "${EXPECTED_ERROR}")
math(EXPR _CAPTURE_LEVEL "${_CAPTURE_LEVEL} - 1")
if(OPERATOR STREQUAL "MATCHES")
fail("expected error message" CAPTURED_ERROR
"to match" EXPECTED_ERROR)
if(DEFINED EXPECTED_ERROR)
string(STRIP "${CAPTURED_ERROR}" CAPTURED_ERROR)
if(EXPECTED_ERROR_OPERATOR STREQUAL "MATCHES")
if(NOT "${CAPTURED_ERROR}" MATCHES "${EXPECTED_ERROR}")
math(EXPR _CAPTURE_LEVEL "${_CAPTURE_LEVEL} - 1")
fail("expected errors" CAPTURED_ERROR
"to match" EXPECTED_ERROR)
endif()
else()
fail("expected error message" CAPTURED_ERROR
"to be equal to" EXPECTED_ERROR)
if(NOT "${CAPTURED_ERROR}" STREQUAL "${EXPECTED_ERROR}")
math(EXPR _CAPTURE_LEVEL "${_CAPTURE_LEVEL} - 1")
fail("expected errors" CAPTURED_ERROR
"to be equal to" EXPECTED_ERROR)
endif()
endif()
else()
math(EXPR _CAPTURE_LEVEL "${_CAPTURE_LEVEL} - 1")
fail("expected not to receive errors" CAPTURED_ERROR)
endif()
else()
elseif(DEFINED EXPECTED_ERROR)
math(EXPR _CAPTURE_LEVEL "${_CAPTURE_LEVEL} - 1")
fail("expected to receive an error message")
fail("expected to receive errors")
endif()
endfunction()

Expand Down
104 changes: 57 additions & 47 deletions test/test_assert_call.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,70 @@ function(throw_errors)
message(FATAL_ERROR "a fatal error message")
endfunction()

section("it should assert error messages")
assert_call(throw_errors EXPECT_ERROR
"a se.*or message\n"
"a fa.*or message")
section("assert command calls")
section("it should assert command calls")
assert_call(message DEBUG "a debug message")
assert_call(CALL message DEBUG "a debug message")
endsection()

assert_call(CALL throw_errors EXPECT_ERROR
"a se.*or message\n"
"a fa.*or message")

assert_call(throw_errors EXPECT_ERROR MATCHES
"a se.*or message\n"
"a fa.*or message")

assert_call(throw_errors EXPECT_ERROR STREQUAL
"a send error message\n"
"a fatal error message")
section("it should fail to assert a command call")
assert_call(assert_call throw_errors EXPECT_ERROR STREQUAL
"expected not to receive errors:\n"
" a send error message\n"
" a fatal error message")
endsection()
endsection()

section("it should fail to assert error messages")
macro(assert_failures)
section("assert command call errors")
section("it should assert command call errors")
assert_call(throw_errors EXPECT_ERROR
"another se.*or message\n"
"another fa.*or message")
endmacro()
"a se.*or message\n"
"a fa.*or message")

assert_call(assert_failures EXPECT_ERROR STREQUAL
"expected error message:\n"
" a send error message\n"
" a fatal error message\n"
"to match:\n"
" another se.*or message\n"
" another fa.*or message")
assert_call(throw_errors EXPECT_ERROR MATCHES
"a se.*or message\n"
"a fa.*or message")

macro(assert_failures)
assert_call(throw_errors EXPECT_ERROR
"another send error message\n"
"another fatal error message")
endmacro()
assert_call(throw_errors EXPECT_ERROR STREQUAL
"a send error message\n"
"a fatal error message")
endsection()

assert_call(assert_failures EXPECT_ERROR STREQUAL
"expected error message:\n"
" a send error message\n"
" a fatal error message\n"
"to match:\n"
" another send error message\n"
" another fatal error message")
endsection()
section("it should fail to assert command call errors")
macro(assert_failures)
assert_call(message DEBUG "a debug message"
EXPECT_ERROR "a debug message")
endmacro()

assert_call(assert_failures EXPECT_ERROR STREQUAL
"expected to receive errors")

macro(assert_failures)
assert_call(throw_errors EXPECT_ERROR
"another se.*or message\n"
"another fa.*or message")
endmacro()

assert_call(assert_failures EXPECT_ERROR STREQUAL
"expected errors:\n"
" a send error message\n"
" a fatal error message\n"
"to match:\n"
" another se.*or message\n"
" another fa.*or message")

section("it should fail to assert error messages "
"due to no error being received")
macro(failed_assertion)
assert_call(message "a message" EXPECT_ERROR "a message")
endmacro()
macro(assert_failures)
assert_call(throw_errors EXPECT_ERROR
"another send error message\n"
"another fatal error message")
endmacro()

assert_call(failed_assertion EXPECT_ERROR
"expected to receive an error message")
assert_call(assert_failures EXPECT_ERROR STREQUAL
"expected errors:\n"
" a send error message\n"
" a fatal error message\n"
"to match:\n"
" another send error message\n"
" another fatal error message")
endsection()
endsection()

0 comments on commit 855a4a8

Please sign in to comment.