Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
maltekliemann committed Aug 16, 2020
2 parents 96e5fba + 1625bef commit 7101423
Show file tree
Hide file tree
Showing 13 changed files with 359 additions and 4 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ You should have received a copy of the GNU General Public License
along with DrMock. If not, see <https://www.gnu.org/licenses/>.
-->

# DrMock 0.4.0

Release 2020/08/16

### Added/Changed:

* Add `DRTEST_ASSERT_DEATH` macro for death testing



# DrMock 0.3.0

Released 2020/07/05
Expand Down Expand Up @@ -50,6 +60,8 @@ Released 2020/07/05
defined, but rather serves as a catch-all (fallthru) state (the
documentation regarding this has been clarified)



# DrMock 0.2.0

Released 2020/05/15
Expand Down Expand Up @@ -108,6 +120,8 @@ Released 2020/05/15

* Throw error message if `DrMockTest` can't find files specified in `TESTS`



# DrMock 0.1.0

Released 2020/01/10
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cmake_minimum_required (VERSION 3.13)

project(
DrMock
VERSION 0.3.0
VERSION 0.4.0
DESCRIPTION "C++17 testing and mocking framework"
LANGUAGES CXX
)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ features for mock object configuration.

### Announcments

Release v0.3.x is now available. For details, see
Release v0.4.x is now available. For details, see
[changelog](CHANGELOG.md).

### Getting started
Expand Down
99 changes: 99 additions & 0 deletions docs/samples/death.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!--
Copyright 2020 Ole Kliemann, Malte Kliemann
This file is part of DrMock.
DrMock is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DrMock is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with DrMock. If not, see <https://www.gnu.org/licenses/>.
-->

# samples/death

DrMock v0.4.0 introduces death tests. A _death test_ checks if a certain
statement will cause the process to raise a certain signal. This may be
used to assert that in certain unrecoverable situations, the program
exits before causing further damage.

This sample demonstrates the death testing capabilities of **DrMock**.

### Table of contents

* [Source code](#source-code)
* [Running the tests](#running-the-tests)
* [Details](#details)
+ [Supported signals](#supported-signals)
+ [clone, fork, signal, multi-threading](#clone-fork-signal-multi-threading)

### Project structure

```
samples/death
│ CMakeLists.txt
│ Makefile
│ deathTest.cpp
```

## Source code

Open `deathTest.cpp`!
The `DRTEST_ASSERT_DEATH(statement, expected)` macro checks if executing
`statement` will cause the signal `expected` to be raised. As with
`DRTEST_ASSERT_THROW`, statement may contain multiple lines of code, if
they are seperated by semicolons (see below).

In the test `catch_segfault`, we test the classic segmentation fault
scenario, dereferencing a `nullptr`:
```cpp
DRTEST_TEST(catch_segfault)
{
DRTEST_ASSERT_DEATH(
int* foo = nullptr;
*foo = 0,
SIGSEGV
);
}
```
We expect this to raise the `SIGSEGV` signal. The test will verify this.
## Running the tests
Do `make`. This should yield the following:
```
Start 1: deathTest
1/1 Test #1: deathTest ........................ Passed 0.00 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) = 0.01 sec
```
## Details
### Supported signals
The following POSIX signals may be caught using `DRTEST_ASSERT_DEATH`:
```cpp
SIGABRT, SIGALRM, SIGBUS, SIGCHLD,
SIGCONT, SIGFPE, SIGHUP, SIGILL,
SIGINT, SIGPIPE, SIGPROF, SIGQUIT,
SIGSEGV, SIGTSTP, SIGSYS, SIGTERM,
SIGTRAP, SIGTTIN, SIGTTOU, SIGURG,
SIGUSR2, SIGVTALRM, SIGXCPU, SIGXFSZ
```
Note that `SIGKILL`, `SIGSTOP` and `SIGUSR1` are not supported.

### clone, fork, signal, multi-threading

Using `clone()`, `fork()`, `signal()` or multi-threading are not allowed
when using `DRTEST_ASSERT_DEATH`.
6 changes: 6 additions & 0 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ Learn how to use **DrMock**'s mock objects as stubs for state verification.
Learn how to use **DrMock** with Qt5.

---

[samples/death](samples/death.md)

Learn how to use **DrMock** for death tests.

---
2 changes: 1 addition & 1 deletion python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
setup(
name = "DrMockGenerator",
author = "Ole Kliemann, Malte Kliemann",
version = "0.3.0",
version = "0.4.0",
scripts = ["DrMockGenerator"],
packages = ["mocker"],
include_package_data = True,
Expand Down
2 changes: 1 addition & 1 deletion samples/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dirs = basic example mock qt states
dirs = basic example mock qt states death

.PHONY: default
default:
Expand Down
40 changes: 40 additions & 0 deletions samples/death/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2020 Ole Kliemann, Malte Kliemann
#
# This file is part of DrMock.
#
# DrMock is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DrMock is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with DrMock. If not, see <https://www.gnu.org/licenses/>.

cmake_minimum_required(VERSION 3.10)

#######################################
# Project data.
#######################################

project(DrMockSampleBasic)
set(CMAKE_CXX_STANDARD 17)

#######################################
# Dependencies.
#######################################

find_package(DrMock COMPONENTS Core REQUIRED)

#######################################
# Testing.
#######################################

enable_testing()
DrMockTest(TESTS
deathTest.cpp
)
18 changes: 18 additions & 0 deletions samples/death/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Discover operating system.
uname := $(shell uname -s)

# Get number of threads
ifeq ($(uname), Darwin)
num_threads := $(shell sysctl -n hw.activecpu)
else # Assuming Linux.
num_threads := $(shell nproc)
endif

.PHONY: default
default:
mkdir -p build && cd build && cmake .. -DCMAKE_PREFIX_PATH="../../prefix"
cd build && make -j$(num_threads) && ctest --output-on-failure

.PHONY: clean
clean:
rm -fr build && rm -fr prefix
28 changes: 28 additions & 0 deletions samples/death/deathTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* Copyright 2020 Ole Kliemann, Malte Kliemann
*
* This file is part of DrMock.
*
* DrMock is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DrMock is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DrMock. If not, see <https://www.gnu.org/licenses/>.
*/

#include "DrMock/Test.h"

DRTEST_TEST(catch_segfault)
{
DRTEST_ASSERT_DEATH(
int* foo = nullptr;
*foo = 0,
SIGSEGV
);
}
130 changes: 130 additions & 0 deletions src/test/Death.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/* Copyright 2020 Ole Kliemann, Malte Kliemann
*
* This file is part of DrMock.
*
* DrMock is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DrMock is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DrMock. If not, see <https://www.gnu.org/licenses/>.
*/

#ifndef DRMOCK_SRC_TEST_DEATH_H
#define DRMOCK_SRC_TEST_DEATH_H

#include <csignal>

#if defined(__unix__) || defined(__APPLE__)
#include <unistd.h>
#endif

#include "TestFailure.h"

namespace drtest { namespace death {

static int pipe_[2];
volatile std::sig_atomic_t atomic_pipe_; // Self-pipe write end; required due to https://en.cppreference.com/w/c/program/signal

#if defined(__unix__) || defined(__APPLE__)
static std::vector<int> signals_ = { // POSIX signals
SIGABRT,
SIGALRM,
SIGBUS,
SIGCHLD,
SIGCONT,
SIGFPE,
SIGHUP,
SIGILL,
SIGINT,
// SIGKILL,
SIGPIPE,
SIGPROF,
SIGQUIT,
SIGSEGV,
// SIGSTOP,
SIGTSTP,
SIGSYS,
SIGTERM,
SIGTRAP,
SIGTTIN,
SIGTTOU,
SIGURG,
// SIGUSR1,
SIGUSR2,
SIGVTALRM,
SIGXCPU,
SIGXFSZ
};
#endif

// Signal handler requires external linkage according to https://en.cppreference.com/w/c/program/signal
extern "C" {
void signal_handler(int x)
{
write(atomic_pipe_, &x, 4);
exit(0);
}
} // extern C

}} // namespace drtest::death

#define DRTEST_ASSERT_DEATH(statement, expected) \
do \
{ \
pipe(drtest::death::pipe_); \
drtest::death::atomic_pipe_ = drtest::death::pipe_[1]; \
\
pid_t pid = fork(); \
if (pid == 0) \
{ \
for (auto s: drtest::death::signals_) \
{ \
std::signal(s, drtest::death::signal_handler); \
} \
statement; /* Child exits here if signal is raised. */ \
int no_signal = -1; \
write(drtest::death::atomic_pipe_, &no_signal, 4); /* Wake up parent if no signal was raised. */ \
close(drtest::death::pipe_[0]); \
close(drtest::death::pipe_[1]); \
exit(0); \
} \
else \
{ \
assert(PIPE_BUF >= 4); \
std::vector<char> buffer(4); \
if (!read(drtest::death::pipe_[0], buffer.data(), 4)) \
{ \
throw std::runtime_error{"read to pipe failed"}; \
} \
\
int result = *(int*)buffer.data(); \
if (result != expected) \
{ \
\
/* Error message */ \
std::string e = strsignal(expected); \
std::string r; \
if (result != -1) \
{ \
r = strsignal(result); \
} \
else \
{ \
r = "No signal: -1"; \
} \
throw drtest::detail::TestFailure{__LINE__, "!=", "received", "expected", e, r}; \
} \
} \
\
close(drtest::death::pipe_[0]); \
close(drtest::death::pipe_[1]); \
} while(false)

#endif /* DRMOCK_SRC_TEST_DEATH_H */
Loading

0 comments on commit 7101423

Please sign in to comment.