Add integration tests

This commit is contained in:
Victor Antonovich 2018-04-11 16:42:50 +03:00
parent fb3d6d6489
commit a6d204bbdd
15 changed files with 187 additions and 1686 deletions

View File

@ -6,6 +6,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/extern_GPL)
include(CheckFunctionExists)
include(CheckLibraryExists)
include(GNUInstallDirs)
include(FindUnixCommands)
include(FindSystemd)
#TODO ISC_Posix, prog_libtool
@ -98,11 +99,12 @@ add_custom_target(uninstall
${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake
)
# unittest target
option(TESTS "Enable unittests" OFF)
if(TESTS)
add_executable(test_basic tests/test_basics.c)
endif()
# integration tests target
enable_testing()
add_test(
NAME itests
COMMAND ${BASH} -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/tests && ./run_itests.sh $<TARGET_FILE:mbusd>"
)
## Please find Packaging stuff following
#@source http://xit0.org/2013/04/cmake-use-git-branch-and-commit-details-in-project/

View File

@ -0,0 +1,35 @@
#!/usr/bin/env bash
MBUSD_BIN=${MBUSD_BIN:=mbusd}
MBUSD_ARGS="-d -L - -v9 -p /tmp/pts0 -s 19200 -P 1025"
MBUSD_PID=/tmp/mbusd.pid
# check argument count
## https://stackoverflow.com/questions/4341630/checking-for-the-correct-number-of-arguments
if [ "$#" -ne 1 ]; then
echo "[E] usage: $0 <start|stop>" >&2
exit 1
fi
check_preconditions() {
# check if mbusd location is set
if ! [ -x "$MBUSD_BIN" ]; then
echo "[E] executable binary not found: ${MBUSD_BIN}" >&2
exit 1
fi
}
check_preconditions
CURRENT_DIR="$(dirname "$(realpath "$0")")"
. $CURRENT_DIR/subprocess_helper.sh
case "$1" in
up|start)
CMD="$MBUSD_BIN $MBUSD_ARGS &"
run_cmd_save_pid "$CMD" $MBUSD_PID
;;
down|stop)
kill_pid $MBUSD_PID
;;
esac

View File

@ -89,10 +89,11 @@ class ModbusSerialServer:
# store = ModbusSlaveContext(..., zero_mode=True)
#---------------------------------------------------------------------------#
store = ModbusSlaveContext(
di = ModbusSequentialDataBlock(0, [12]*100), # discrete input
co = ModbusSequentialDataBlock(0, [13]*100), # coils
hr = ModbusSequentialDataBlock(0, [14]*100), # holding reg
ir = ModbusSequentialDataBlock(0, [15]*100)) #
di = ModbusSequentialDataBlock(0, [True]*8), # discrete inputs
co = ModbusSequentialDataBlock(0, [False]*8), # coils
hr = ModbusSequentialDataBlock(0, [0]*8), # holding regs
ir = ModbusSequentialDataBlock(0, list(range(8))), # input regs
zero_mode=True) # request(0-7) will map to the address (0-7)
context = ModbusServerContext(slaves=store, single=True)
#---------------------------------------------------------------------------#
@ -127,7 +128,7 @@ class ModbusSerialServer:
def kill(self):
print("Going to terminat the process, this could throw exceptins")
print("Going to terminate the process, this could throw exceptions")
if self.p is not None:
self.p.terminate()

View File

@ -1,32 +1,28 @@
#!/usr/bin/env bash
MBUS_SERVER_PID=/tmp/modbus_server.pid
RTU_SLAVE_PID=/tmp/rtu_slave.pid
CURRENT_DIR="$(dirname "$(realpath "$0")")"
. $CURRENT_DIR/subprocess_helper.sh
check_preconditions() {
#TODO check if python module 'pymodbus' is installed
#python -c "import foo"
true
python -c "import pymodbus" || exit 1
}
# check argument count
## https://stackoverflow.com/questions/4341630/checking-for-the-correct-number-of-arguments
if [ "$#" -ne 1 ]; then
echo "Usage: $0 up|down" >&2
echo "[E] usage: $0 <start|stop>" >&2
exit 1
fi
check_preconditions
case "$1" in
up|start)
#TOOO obtain current directory
CMD="python ${CURRENT_DIR}/modbus_server_mock.py &"
run_cmd_save_pid "$CMD" $MBUS_SERVER_PID
CMD="python ${CURRENT_DIR}/rtu_slave.py &"
run_cmd_save_pid "$CMD" $RTU_SLAVE_PID
;;
down|stop)
kill_pid $MBUS_SERVER_PID
kill_pid $RTU_SLAVE_PID
;;
esac

View File

@ -19,7 +19,7 @@ check_preconditions() {
# check argument count
## https://stackoverflow.com/questions/4341630/checking-for-the-correct-number-of-arguments
if [ "$#" -ne 1 ]; then
echo "Usage: $0 up|down" >&2
echo "[E] usage: $0 <start|stop>" >&2
exit 1
fi

View File

@ -7,8 +7,14 @@ run_cmd_save_pid() {
local _PID_FILE=$2
if [[ -e $_PID_FILE ]]; then
echo "[D] pid_file ($_PID_FILE) exists, done"
_PID=`cat $_PID_FILE`
if ps -p $_PID > /dev/null; then
echo "[D] PID file ($_PID_FILE) exists, done"
return 1
else
echo "[D] removing stale PID file ($_PID_FILE)"
rm -f $_PID_FILE
fi
fi
echo "[I] running $_CMD in background..."

View File

@ -1,75 +0,0 @@
# Contributing to greatest
Thanks for taking time to contribute to greatest!
Please send patches or pull requests against the `develop` branch. This
makes it easier to avoid interface changes until they can be reflected
in version number updates.
Sending changes via patch or pull request acknowledges that you are
willing and able to contribute it under this project's license. (Please
don't contribute code you aren't legally able to share.)
## Bug Reports
Please report bugs at [the Issues page][issues].
[issues]: https://github.com/silentbicycle/greatest/issues).
If you are reporting a bug, please include:
+ Your operating system name and version.
+ Your compiler version and target platform.
+ Any details about your local setup that might be helpful in
troubleshooting.
+ Detailed steps to reproduce the bug.
## Documentation
Improvements to the documentation are welcome. So are requests for
clarification -- if the docs are unclear or misleading, that's a
potential source of bugs.
## Portability
greatest tries to assume little about its environment. It targets ANSI C
(C89) as a baseline, and features that are specific to C99 or later need
to be wrapped in a version check.
It doesn't require a particular OS, or that there is an OS at all. (It
may be running tests on an embedded system without an RTOS.) It uses
`fprintf(3)` for reporting, that's about it.
Improvements that don't fit the portability requirements can go in
`contrib/`, just not the main `greatest.h` header.
## contrib/: Extensions and Other Tools
There is a `contrib/` directory for extensions. This could include
scripts that generate test templates, add formatting to the reports, or
better integrate greatest into build / continuous integration systems.
Extension libraries that depend on dynamic allocation or
platform-specific features can also go here.
Please include a license in any standalone scripts or extensions.
## Versioning & Compatibility
The versioning format is MAJOR.MINOR.PATCH.
Improvements or minor bug fixes that do not break compatibility with
past releases lead to patch version increases. API changes that do not
break compatibility lead to minor version increases and reset the patch
version, and changes that do break compatibility lead to a major version
increase.
The version will be increased during the merge to master, as part of
the release process.

View File

@ -1,13 +0,0 @@
Copyright (c) 2011-2016 Scott Vokes <vokes.s@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,338 +0,0 @@
# greatest
A testing system for C, contained in 1 file.
## Key Features
- **Small, Portable, Lightweight**
greatest doesn't depend on anything beyond ANSI C89, and the test
scaffolding should build without warnings when compiled with
`-Wall -Wextra -pedantic`. It is under 1,000 LOC (SLOCCount),
and does no dynamic allocation.
- **Permissive License**
greatest is released under the [ISC License][ISC]. You can use it
freely, even for commercial purposes.
- **Easy To Set Up**
To use, just `#include "greatest.h"` in your project. There is
very little boilerplate. Most features are optional.
- **Un-Opinionated**
When a command-line test runner is useful, greatest can provide one,
but it can also run as part of other programs. It doesn't depend on
a particular build system or other tooling, and should accommodate a
variety of testing approaches. It actively avoids imposing
architectural choices on code under test. While greatest was
designed with C in mind, it attempts to be usable from C++.
- **Modular**
Tests can be run individually, or grouped into suites. Suites can
share common setup, and can be in distinct compilation
units.
- **Low Friction**
Specific tests or suites can be run by name, for focused and rapid
iteration during development. greatest adds very little startup
latency.
There are some compile-time options, and slightly nicer syntax for
parametric testing (running tests with arguments) if compiled
with a C99 or later language standard.
I wrote a
[blog post](http://spin.atomicobject.com/2013/07/31/greatest-c-testing-embedded/)
with more information.
[theft][], a related project, adds [property-based testing][pbt].
[1]: http://spin.atomicobject.com/2013/07/31/greatest-c-testing-embedded/
[theft]: https://github.com/silentbicycle/theft
[pbt]: https://spin.atomicobject.com/2014/09/17/property-based-testing-c/
[ISC]: https://opensource.org/licenses/isc-license.txt
## Basic Usage
```c
#include "greatest.h"
/* A test runs various assertions, then calls PASS(), FAIL(), or SKIP(). */
TEST x_should_equal_1(void) {
int x = 1;
ASSERT_EQ(1, x); /* default message */
ASSERT_EQm("yikes, x doesn't equal 1", 1, x); /* custom message */
/* printf expected and actual values as "%d" if they differ */
ASSERT_EQ_FMT(1, x, "%d");
PASS();
}
/* Suites can group multiple tests with common setup. */
SUITE(the_suite) {
RUN_TEST(x_should_equal_1);
}
/* Add definitions that need to be in the test runner's main file. */
GREATEST_MAIN_DEFS();
int main(int argc, char **argv) {
GREATEST_MAIN_BEGIN(); /* command-line options, initialization. */
/* Individual tests can be run directly. */
/* RUN_TEST(x_should_equal_1); */
/* Tests can also be gathered into test suites. */
RUN_SUITE(the_suite);
GREATEST_MAIN_END(); /* display results */
}
```
Output:
```sh
$ make simple && ./simple
cc -g -Wall -Werror -pedantic simple.c -o simple
* Suite the_suite:
.
1 test - 1 passed, 0 failed, 0 skipped (5 ticks, 0.000 sec)
Total: 1 test (47 ticks, 0.000 sec), 3 assertions
Pass: 1, fail: 0, skip: 0.
```
Test cases should call assertions and then end in `PASS()`, `SKIP()`,
`FAIL()`, or one of their message variants (e.g. `SKIPm("TODO");`).
If there are any test failures, the test runner will return 1,
otherwise it will return 0. (Skips do not cause a test runner to
report failure.)
Tests and suites are just functions, so normal C scoping rules apply.
For example, a test or suite named "main" will have a name collision.
(For more examples, look at `example.c` and `example_suite.c`.)
## Filtering By Name
greatest runs all tests by default, but can be configured to only run
suites or tests whose names contain a filter string, and/or exclude
tests whose name contains a filter string. When test name filtering and
exclusion are used together, exclusion takes precedence.
void greatest_set_suite_filter(const char *name);
void greatest_set_test_filter(const char *name);
void greatest_set_test_exclude(const char *name);
These correspond to the following command line test runner options:
`-s SUITE`: Only run suites whose names contain the string "SUITE"
`-t TEST`: Only run tests whose names contain the string "TEST"
`-x EXCLUDE`: Exclude tests whose names contain the string "EXCLUDE"
For example, to run any tests with "tree" in the name, in suites with
"pars" in the name (such as "parser"), but exclude any tests whose names
also contain "slow":
./test_project -s pars -t tree -x slow
## Available Assertions
Assertions fail the current test unless some condition holds. All
assertions have a "message" variant (with an `m` suffix), which takes a
custom failure message string as their first argument. For example, the
assertion `ASSERT_EQ(apple, orange);` could instead be used like
`ASSERT_EQm("these should match", apple, orange)`. Non-message
assertions create a default message.
### `ASSERT(COND)`
Assert that `COND` evaluates to a true value.
### `ASSERT_FALSE(COND)`
Assert that `COND` evaluates to a false value.
### `ASSERT_EQ(EXPECTED, ACTUAL)`
Assert that `EXPECTED == ACTUAL`. To compare with a custom equality test
function, use `ASSERT_EQUAL_T` instead. To print the values if they
differ, use `ASSERT_EQ_FMT`.
### `ASSERT_EQ_FMT(EXPECTED, ACTUAL, FORMAT)`
Assert that `EXPECTED == ACTUAL`. If they are not equal, print their
values using FORMAT as the `printf` format string.
For example: `ASSERT_EQ_FMT(123, result, "%d");`
Note: `EXPECTED` and `ACTUAL` will be evaluated more than once on
failure, so they should not be a function call with side effects.
(Since their type is not known by the macro, they cannot be
captured in a local variable.)
### `ASSERT_IN_RANGE(EXPECTED, ACTUAL, TOLERANCE)`
Assert that ACTUAL is within EXPECTED +/- TOLERANCE, once the values
have been converted to a configurable floating point type
(`GREATEST_FLOAT`).
### `ASSERT_STR_EQ(EXPECTED, ACTUAL)`
Assert that the strings are equal
(i.e., `strcmp(EXPECTED, ACTUAL) == 0`).
### `ASSERT_STRN_EQ(EXPECTED, ACTUAL, SIZE)`
Assert that the first SIZE bytes of the strings are equal
(i.e., `strncmp(EXPECTED, ACTUAL, SIZE) == 0`).
### `ASSERT_MEM_EQ(EXPECTED, ACTUAL, SIZE)`
Assert that the first SIZE bytes of memory pointed to
by EXPECTED and ACTUAL are equal. If the memory differs, print
a hexdump and highlight the lines and individual bytes which
do not match.
### `ASSERT_ENUM_EQ(EXPECTED, ACTUAL, ENUM_STR_FUN)`
Assert that the enum value EXPECTED is equal to ACTUAL. If not, convert
each enum value to a string using `ENUM_STR_FUN` before printing them.
`ENUM_STR_FUN` should have a type like:
const char *some_enum_str(enum some_enum x);
### `ASSERT_EQUAL_T(EXPECTED, ACTUAL, TYPE_INFO, UDATA)`
Assert that EXPECTED and ACTUAL are equal, using the `greatest_equal_cb`
function pointed to by `TYPE_INFO->equal` to compare them. The
assertion's `UDATA` argument can be used to pass in arbitrary user data
(or `NULL`). If the values are not equal and the `TYPE_INFO->print`
function is defined, it will be used to print an "Expected: X, Got: Y"
message.
### `ASSERT_OR_LONGJMP(COND)`
Assert that `COND` evaluates to a true value. If not, then use
`longjmp(3)` to immediately return from the test case and any
intermediate function calls. (If built with `GREATEST_USE_LONGJMP`
defined to 0, then all setjmp/longjmp-related functionality will be
compiled out.)
## Random Shuffling
Groups of suites or tests can be run in random order by using
`GREATEST_SHUFFLE_SUITES` and `GREATEST_SHUFFLE_TESTS`, respectively.
This can help find and eliminate coupling between tests.
The shuffling depends on the seed and the test/suite count, so a
consistent seed will only lead to reproducible ordering until the
group's count changes.
Shuffling suites:
SHUFFLE_SUITES(seed, {
RUN_SUITE(suite1);
RUN_SUITE(suite2);
RUN_SUITE(suite3);
RUN_SUITE(suite4);
RUN_SUITE(suite5);
});
Shuffling tests:
SHUFFLE_TESTS(seed, {
RUN_TEST(test_a);
RUN_TEST1(test_b, 12345);
RUN_TEST(test_c);
RUN_TESTp(test_d, "some_argument");
RUN_TEST(test_e);
});
Note: Any other code inside the block will be executed several times.
The shuffling macro expands to a loop with (count + 1) iterations -- the
first pass counts, and the following passes only execute the next chosen
suite/test. In particular, avoid running tests directly inside of a
`SHUFFLE_SUITES` block (without a suite), because the test will run over
and over.
## Sub-Functions
Because of how `PASS()`, `ASSERT()`, `FAIL()`, etc. are implemented
(returning a test result enum value), calls to functions that use them
directly from test functions must be wrapped in `CHECK_CALL`:
TEST example_using_subfunctions(void) {
CHECK_CALL(less_than_three(5));
PASS();
}
This is only necessary if the called function can cause test failures.
## Command Line Options
Test runners build with the following command line options:
Usage: (test_runner) [--help] [-hlfv] [-s SUITE] [-t TEST]
-h, --help print this Help
-l List suites and tests, then exit (dry run)
-f Stop runner after first failure
-v Verbose output
-s SUITE only run suite w/ name containing SUITE substring
-t TEST only run test w/ name containing TEST substring
-t EXCLUDE exclude tests containing string EXCLUDE substring
Any arguments after `--` will be ignored.
If you want to run multiple test suites in parallel, look at
[parade](https://github.com/silentbicycle/parade).
These command line options are processed by `GREATEST_MAIN_BEGIN();`.
## Aliases
Most of the macros have prefixed and unprefixed forms. For example,
`SUITE` is the same as `GREATEST_SUITE`.
Check the source for the list -- search for `#if GREATEST_USE_ABBREVS`.
These aliases can be disabled by `#define`-ing `GREATEST_USE_ABBREVS` to 0.
## Color Output
If you want color output (`PASS` in green, `FAIL` in red, etc.), you can
pipe the output through the included `greenest` script in `contrib/`:
```sh
$ ./example -v | greenest
```
(Note that `greenest` depends on a Unix-like environment.)
greatest itself doesn't have built-in coloring to stay small and portable.

View File

@ -1,2 +0,0 @@
# this is an external repository, its target is
https://github.com/silentbicycle/greatest

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +0,0 @@
#!/usr/bin/env python
#https://pymodbus.readthedocs.io/en/latest/examples/synchronous-client.html
#---------------------------------------------------------------------------#
# import the various server implementations
#---------------------------------------------------------------------------#
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.client.sync import ModbusUdpClient
from pymodbus.client.sync import ModbusSerialClient
#with ModbusSerialClient(method='rtu', port='/tmp/pts1', timeout=1, baudrate=19200) as client:
PORT_WITHOUT_ROOT_REQ = 1025
with ModbusTcpClient('127.0.0.1', port=PORT_WITHOUT_ROOT_REQ) as client:
result = client.read_holding_registers(1, 8, unit=1)
print result.registers

93
tests/run_itests.py Executable file
View File

@ -0,0 +1,93 @@
#!/usr/bin/env python
import random
import unittest
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.pdu import ExceptionResponse, ModbusExceptions
from pymodbus.bit_read_message import ReadDiscreteInputsResponse, ReadCoilsResponse
from pymodbus.bit_write_message import WriteMultipleCoilsResponse, WriteSingleCoilResponse
from pymodbus.register_read_message import ReadInputRegistersResponse, ReadHoldingRegistersResponse
from pymodbus.register_write_message import WriteMultipleRegistersResponse, WriteSingleRegisterResponse
MBUSD_PORT = 1025
class TestModbusRequests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.client = ModbusTcpClient('127.0.0.1', port=MBUSD_PORT)
cls.client.connect()
@classmethod
def tearDownClass(cls):
cls.client.close()
def test_coils(self):
bits = [random.randrange(2)>0 for i in range(8)]
# 15 Write Multiple Coils
result = self.client.write_coils(0, bits)
self.assertIsInstance(result, WriteMultipleCoilsResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.count, 8, result)
# 01 Read Coils
result = self.client.read_coils(0, 8)
self.assertIsInstance(result, ReadCoilsResponse, result)
self.assertEqual(result.bits, bits, result)
# 05 Write Single Coil
bit1 = not bits[0]
result = self.client.write_coil(0, bit1)
self.assertIsInstance(result, WriteSingleCoilResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.value, bit1, result)
result = self.client.read_coils(0, 1)
self.assertIsInstance(result, ReadCoilsResponse, result)
self.assertEqual(result.bits[0], bit1, result)
def test_discreteInputs(self):
# 02 Read Discrete Inputs
result = self.client.read_discrete_inputs(0, 8)
self.assertIsInstance(result, ReadDiscreteInputsResponse, result)
self.assertEqual(result.bits, [True]*8, result)
def test_inputRegisters(self):
# 04 Read Input Registers
result = self.client.read_input_registers(0, 8)
self.assertIsInstance(result, ReadInputRegistersResponse, result)
self.assertEqual(result.registers, list(range(8)), result)
def test_holdingRegisters(self):
registers = [random.randrange(8) for i in range(8)]
# 16 Write Multiple Holding Registers
result = self.client.write_registers(0, registers)
self.assertIsInstance(result, WriteMultipleRegistersResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.count, 8, result)
# 03 Read Multiple Holding Registers
result = self.client.read_holding_registers(0, 8)
self.assertIsInstance(result, ReadHoldingRegistersResponse, result)
self.assertEqual(result.registers, registers, result)
# 06 Write Single Holding Register
register1 = (registers[0] + 1) % 65535
result = self.client.write_register(0, register1)
self.assertIsInstance(result, WriteSingleRegisterResponse, result)
self.assertEqual(result.address, 0, result)
self.assertEqual(result.value, register1, result)
result = self.client.read_holding_registers(0, 1)
self.assertIsInstance(result, ReadHoldingRegistersResponse, result)
self.assertEqual(result.registers[0], register1, result)
def test_exception(self):
result = self.client.write_coil(1000, False) # invalid address 1000
self.assertIsInstance(result, ExceptionResponse, result)
self.assertEqual(result.original_code, 5, result) # fc05 Write Single Coil
self.assertEqual(result.exception_code, 2, result) # Illegal Data Address
if __name__ == '__main__':
unittest.main()

31
tests/run_itests.sh Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
CWD="$(dirname "$(realpath "$0")")"
if ! [ -x "$1" ]; then
echo "[E] usage: $0 <mbusd_binary_path>" >&2
exit 1
fi
export MBUSD_BIN=$1
function setup() {
echo "[I] do test environment setup"
$CWD/environment/socat_runner.sh start || return 1
$CWD/environment/rtu_slave_runner.sh start || return 1
$CWD/environment/mbusd_runner.sh start || return 1
}
function teardown() {
echo "[I] do test environment teardown"
$CWD/environment/mbusd_runner.sh stop
$CWD/environment/rtu_slave_runner.sh stop
$CWD/environment/socat_runner.sh stop
}
trap teardown EXIT
setup || exit 1
$CWD/run_itests.py || exit 1
exit 0

View File

@ -1,31 +0,0 @@
#include "greatest/greatest.h"
/* A test runs various assertions, then calls PASS(), FAIL(), or SKIP(). */
TEST x_should_equal_1(void) {
int x = 1;
ASSERT_EQ(1, x); /* default message */
ASSERT_EQm("yikes, x doesn't equal 1", 1, x); /* custom message */
/* printf expected and actual values as "%d" if they differ */
ASSERT_EQ_FMT(1, x, "%d");
PASS();
}
/* Suites can group multiple tests with common setup. */
SUITE(the_suite) {
RUN_TEST(x_should_equal_1);
}
/* Add definitions that need to be in the test runner's main file. */
GREATEST_MAIN_DEFS();
int main(int argc, char **argv) {
GREATEST_MAIN_BEGIN(); /* command-line options, initialization. */
/* Individual tests can be run directly. */
/* RUN_TEST(x_should_equal_1); */
/* Tests can also be gathered into test suites. */
RUN_SUITE(the_suite);
GREATEST_MAIN_END(); /* display results */
}