Merge pull request #152 from martinbremmer/mptproto3

Multi Process Testing framework
This commit is contained in:
eboasson 2019-04-25 21:56:52 +02:00 committed by GitHub
commit bf79e12e10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 4458 additions and 69 deletions

86
docs/dev/mpt_req.md Normal file
View file

@ -0,0 +1,86 @@
# Eclipse Cyclone DDS Multi Process Testing Requirements
This document present some requirements and considerations regarding the
[Multi Process Test Framework](multi_process_testing.md).
## Requirements
1.1) To test certain Cyclone DDS features, multiple processes running Cyclone
DDS are needed to force communication through the whole stack.
1.2) Should be buildable and runnable on platforms that support multiprocess
and filesystems including the ones used in the continues integration
context.
1.3) Results should be easily analyzable within the continues integration
context and when running locally. This can be done by reporting the
results in a standard format like xunit or cunit.
1.4) No processes must be left behind (f.i. deadlock in child process) when the
test finished (or timed out).
1.5) When running tests parallel, they should not interfere with each other.
1.6) Processes of the same test should be able to communicate (for settings,
syncing, etc).
1.7) It should be possible to analyze output/messages/tracing of the parent
and child processes to be able to draw a proper test conclusion.
## Considerations
2.1)
The files that actually contain the tests, should be focused on those tests.
This means that the process management and setting up (and usage of) IPC
between test processes should be handled by a test framework so that the
test files can remain as clean as possible.
2.2)
If possible, there shouldn't be a need for writing log files to a file system
when running the tests normally. It could be helpful, however, that these log
files are written when debugging related tests.
2.3)
Preferably, the DDS communication between the processes should not leave
localhost.
## Intentions
There doesn't seem to be a 3rd party test framework that addresses our
requirements in a satisfactory manner.
After some discussions with a few people (different people in different
meetings), it was decided to create our own framework and to go in the
following direction:
- Process creation/destruction/etc is (re)introduced in the ddsrt.<br>
[1.1/1.2]
- The files that contain the tests, should be easy to understand and focus on
the tests themselves.<br>
[2.1]
- Other files (generated or in the framework) should take care of the
intricacies of starting/monitoring the proper processes with the proper
settings.<br>
[1.4/1.6/2.1]
- To do this, a similar approach of the current CUnit build will be used;
CMake will scan the test files and create runners according to macros within
the test files.<br>
[2.1]
- The tests should be executed by CTest. For now this means that a proper
runner exit code for pass/fail is enough. We would like to add CUnit like
output in the future.<br>
[1.2/1.3]
- The Cyclone DDS API contains the possibility to monitor generated log traces.
This means we won't be needing to monitor actual log files. Just register a
log callback and go from there.<br>
[1.7/2.2]
- The framework should be able to generate unique domain ids and unique topic
names when necessary. That way, tests won't interfere with each other when
running in parallel.<br>
[1.5]

View file

@ -0,0 +1,86 @@
# Eclipse Cyclone DDS Multi Process Testing
Some features and functionalities of Cyclone DDS can only be tested when
there's communication between processes. Examples are durability, security,
etc. To really make sure that these kind of features work, extended tests
with multiple processes are needed.
This results in a number of [requirements](mpt_req.md).
There doesn't seem to be a 3rd party test framework that addresses our
requirements in a satisfactory manner. Therefore, it was decided to create
our own Multi Process Testing (MPT) framework.
This document will provide an overview of the MPT framework.
## Overview
An MPT application is basically divided into two components
1. Tests
2. Runner
The Tests are created by the developer. They don't need to worry about the
process management. They only have to provide process entry point(s), tests
and test processes that use these entry points. E.g.
```cpp
MPT_ProcessEntry(publisher, MPT_Args(int domain))
{
/* Publish a sample on the given domain. */
MPT_ASSERT(success, "publisher failed");
}
MPT_ProcessEntry(subscriber, MPT_Args(int domain))
{
/* Subscribe to a sample from the given domain. */
MPT_ASSERT(success, "subscriber failed");
}
MPT_TestProcess(helloworld, domain0, pub, publisher, MPT_ArgValues(0));
MPT_TestProcess(helloworld, domain0, sub, subscriber, MPT_ArgValues(0));
MPT_Test(helloworld, domain0);
MPT_TestProcess(helloworld, domain42, pub, publisher, MPT_ArgValues(42));
MPT_TestProcess(helloworld, domain42, sub, subscriber, MPT_ArgValues(42));
MPT_Test(helloworld, domain42);
```
There are more options, but see the
[usage test](../../src/mpt/tests/self/usage.c) for more elaborate examples.
CMake will identify suites, tests and processes depending on those MPT
macros.<br>
It'll use that to generate part of the MPT Runner. The Runner takes care
of starting test(s) and handling the process management.
The runner also takes care of preparing IPC between test processes so that
these processes can sync if they need to (NB, this will be a future extension).
#### Suite-Test-Process tree
A process is related to a test and that test is related to a suite.<br>
A suite can have multiple tests and tests can have multiple processes.<br>
This results in the following tree quite naturally.
<img src="pictures/mpt_tree.png" alt="Suite-Test-Process tree">
#### Test execution
There are 3 main ways to start an MPT application.
1. Without argument.<br>
All tests will be run.
2. With suite and/or test name patterns as arguments.<br>
A subset of tests will be run depending on the provided patterns.<br>
This allows ctest to execute single tests.
3. With a specific suite/test/process combination as arguments.<br>
An user will normally not use this.
The third option is used by the MPT application itself to start a specific
test related process. It does so by restarting itself with the proper
suite/test/process combination as indicated by the test. This results
in the following flow.
<img src="pictures/mpt_flow.png" alt="MPT application flow">

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -186,5 +186,11 @@ endif()
add_subdirectory(examples)
if (BUILD_TESTING)
# Multi Process Tests
add_subdirectory(mpt)
endif()
# Pull-in CPack and support for generating <Package>Config.cmake and packages.
include(Packaging)

View file

@ -22,6 +22,7 @@
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/strtod.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsi/q_config.h"
#include "dds/ddsi/q_log.h"
#include "dds/ddsrt/avl.h"
@ -32,7 +33,6 @@
#include "dds/ddsi/q_error.h"
#include "dds/ddsrt/xmlparser.h"
#include "dds/ddsrt/expand_envvars.h"
#include "dds/version.h"

View file

@ -74,13 +74,11 @@ list(APPEND sources
"${source_path}/io.c"
"${source_path}/log.c"
"${source_path}/retcode.c"
"${source_path}/process.c"
"${source_path}/strtod.c"
"${source_path}/strtol.c")
list(APPEND headers
"${source_path}/dds/ddsrt/avl.h"
"${source_path}/dds/ddsrt/expand_envvars.h"
"${source_path}/dds/ddsrt/fibheap.h"
"${source_path}/dds/ddsrt/hopscotch.h"
"${source_path}/dds/ddsrt/thread_pool.h")
@ -104,7 +102,7 @@ list(APPEND sources
# network stack. In order to mix-and-match various compilers, architectures,
# operating systems, etc input from the build system is required.
foreach(feature atomics cdtors environ heap ifaddrs random rusage
sockets string sync threads time md5)
sockets string sync threads time md5 process)
if(EXISTS "${include_path}/dds/ddsrt/${feature}.h")
list(APPEND headers "${include_path}/dds/ddsrt/${feature}.h")
file(GLOB

View file

@ -96,6 +96,50 @@ ddsrt_unsetenv(
const char *name)
ddsrt_nonnull_all;
/**
* @brief Expand environment variables within string.
*
* Expands ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, but not $X.
*
* The result string should be freed with ddsrt_free().
*
* @param[in] string String to expand.
*
* @returns Allocated char*.
*
* @retval NULL
* Expansion failed.
* @retval Pointer
* Copy of the string argument with the environment
* variables expanded.
*/
DDS_EXPORT char*
ddsrt_expand_envvars(
const char *string);
/**
* @brief Expand environment variables within string.
*
* Expands $X, ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, $ and \
* can be escaped with \.
*
* The result string should be freed with ddsrt_free().
*
* @param[in] string String to expand.
*
* @returns Allocated char*.
*
* @retval NULL
* Expansion failed.
* @retval Pointer
* Copy of the string argument with the environment
* variables expanded.
*/
DDS_EXPORT char*
ddsrt_expand_envvars_sh(
const char *string);
#if defined(__cplusplus)
}
#endif

View file

@ -1,31 +0,0 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef DDSRT_EXPAND_ENVVARS_H
#define DDSRT_EXPAND_ENVVARS_H
#include "dds/export.h"
#if defined (__cplusplus)
extern "C" {
#endif
/* Expands ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, but not $X */
DDS_EXPORT char *ddsrt_expand_envvars(const char *string);
/* Expands $X, ${X}, ${X:-Y}, ${X:+Y}, ${X:?Y} forms, $ and \ can be escaped with \ */
DDS_EXPORT char *ddsrt_expand_envvars_sh(const char *string);
#if defined (__cplusplus)
}
#endif
#endif

View file

@ -107,7 +107,7 @@ typedef struct {
/** Function signature that log and trace callbacks must adhere too. */
typedef void(*dds_log_write_fn_t)(void *, const dds_log_data_t *);
extern uint32_t *const dds_log_mask;
DDS_EXPORT extern uint32_t *const dds_log_mask;
/**
* @brief Get currently enabled log and trace categories.

View file

@ -13,11 +13,9 @@
#define DDSRT_PROCESS_H
#include "dds/export.h"
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/types.h"
#if defined (__cplusplus)
extern "C" {
#endif
#include "dds/ddsrt/retcode.h"
#if defined(_WIN32)
typedef DWORD ddsrt_pid_t;
@ -33,6 +31,11 @@ typedef pid_t ddsrt_pid_t;
#endif
#endif /* _WIN32 */
#if defined (__cplusplus)
extern "C" {
#endif
/**
* @brief Return process ID (PID) of the calling process.
*
@ -41,6 +44,168 @@ typedef pid_t ddsrt_pid_t;
DDS_EXPORT ddsrt_pid_t
ddsrt_getpid(void);
/**
* @brief Create new process.
*
* Creates a new process using the provided executable file. It will have
* default priority and scheduling.
*
* Process arguments are represented by argv, which can be null. If argv is
* not null, then the array must be null terminated. The argv array only has
* to contain the arguments, the executable filename doesn't have to be in
* the first element, which is normally the convention.
*
* @param[in] executable Executable file name.
* @param[in] argv Arguments array.
* @param[out] pid ID of the created process.
*
* @returns A dds_retcode_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* Process successfully created.
* @retval DDS_RETCODE_BAD_PARAMETER
* Provided file is not available or not executable.
* @retval DDS_RETCODE_NOT_ALLOWED
* Caller is not permitted to start the process.
* @retval DDS_RETCODE_OUT_OF_RESOURCES
* Not enough resources to start the process.
* @retval DDS_RETCODE_ERROR
* Process could not be created.
*/
DDS_EXPORT dds_retcode_t
ddsrt_proc_create(
const char *executable,
char *const argv[],
ddsrt_pid_t *pid);
/**
* @brief Wait for a specific child process to have finished.
*
* This function takes a process id and will wait until the related process
* has finished or the timeout is reached.
*
* If the process finished, then the exit code of that process will be copied
* into the given 'code' argument.
*
* Internally, the timeout can be round-up to the nearest milliseconds or
* seconds, depending on the platform.
*
* See ddsrt_proc_waitpids() for waiting on all child processes.
*
* @param[in] pid Process ID (PID) to get the exit code from.
* @param[in] timemout Time within the process is expected to finish.
* @param[out] code The exit code of the process.
*
* @returns A dds_retcode_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* Process has terminated and its exit code has been captured.
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* Process is still alive (only when timeout == 0).
* @retval DDS_RETCODE_TIMEOUT
* Process is still alive (even after the timeout).
* @retval DDS_RETCODE_BAD_PARAMETER
* Negative timeout.
* @retval DDS_RETCODE_NOT_FOUND
* Process unknown.
* @retval DDS_RETCODE_ERROR
* Getting the exit code failed for an unknown reason.
*/
DDS_EXPORT dds_retcode_t
ddsrt_proc_waitpid(
ddsrt_pid_t pid,
dds_duration_t timeout,
int32_t *code);
/**
* @brief Wait for a random child process to have finished.
*
* This function will wait until anyone of the child processes has
* finished or the timeout is reached.
*
* If a process finished, then the exit code of that process will be
* copied into the given 'code' argument. The pid of the process will
* be put in the 'pid' argument.
*
* Internally, the timeout can be round-up to the nearest milliseconds or
* seconds, depending on the platform.
*
* See ddsrt_proc_waitpid() for waiting on a specific child process.
*
* @param[in] timemout Time within a process is expected to finish.
* @param[out] pid Process ID (PID) of the finished process.
* @param[out] code The exit code of the process.
*
* @returns A dds_retcode_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* A process has terminated.
* Its exit code and pid have been captured.
* @retval DDS_RETCODE_PRECONDITION_NOT_MET
* All child processes are still alive (only when timeout == 0).
* @retval DDS_RETCODE_TIMEOUT
* All child processes are still alive (even after the timeout).
* @retval DDS_RETCODE_BAD_PARAMETER
* Negative timeout.
* @retval DDS_RETCODE_NOT_FOUND
* There are no processes to wait for.
* @retval DDS_RETCODE_ERROR
* Getting the exit code failed for an unknown reason.
*/
DDS_EXPORT dds_retcode_t
ddsrt_proc_waitpids(
dds_duration_t timeout,
ddsrt_pid_t *pid,
int32_t *code);
/**
* @brief Checks if a process exists.
*
* @param[in] pid Process ID (PID) to check if it exists.
*
* @returns A dds_retcode_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* The process exists.
* @retval DDS_RETCODE_NOT_FOUND
* The process does not exist.
* @retval DDS_RETCODE_ERROR
* Determining if a process exists or not, failed.
*/
DDS_EXPORT dds_retcode_t
ddsrt_proc_exists(
ddsrt_pid_t pid);
/**
* @brief Kill a process.
*
* This function will try to forcefully kill the process (identified
* by pid).
*
* When DDS_RETCODE_OK is returned, it doesn't mean that the process
* was actually killed. It only indicates that the process was
* 'told' to terminate. Call ddsrt_proc_exists() to know
* for sure if the process was killed.
*
* @param[in] pid Process ID (PID) of the process to terminate.
*
* @returns A dds_retcode_t indicating success or failure.
*
* @retval DDS_RETCODE_OK
* Kill attempt has been started.
* @retval DDS_RETCODE_BAD_PARAMETER
* Process unknown.
* @retval DDS_RETCODE_ILLEGAL_OPERATION
* Caller is not allowed to kill the process.
* @retval DDS_RETCODE_ERROR
* Kill failed for an unknown reason.
*/
DDS_EXPORT dds_retcode_t
ddsrt_proc_kill(
ddsrt_pid_t pid);
#if defined (__cplusplus)
}
#endif

View file

@ -19,7 +19,6 @@
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/log.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/expand_envvars.h"
typedef char * (*expand_fn)(const char *src0);

View file

@ -1,27 +0,0 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include "dds/ddsrt/process.h"
#if !defined(_WIN32)
# include <unistd.h>
#endif
ddsrt_pid_t
ddsrt_getpid(void)
{
#if defined(_WIN32)
return GetCurrentProcessId();
#else
/* Mapped to taskIdSelf() in VxWorks kernel mode. */
return getpid();
#endif
}

View file

@ -0,0 +1,309 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/log.h"
ddsrt_pid_t
ddsrt_getpid(void)
{
/* Mapped to taskIdSelf() in VxWorks kernel mode. */
return getpid();
}
/*
* This'll take a argv and prefixes it with the given prefix.
* If argv is NULL, the new argv array will only contain the prefix and a NULL.
* The result array is always terminated with NULL.
*/
static char**
prefix_argv(const char *prefix, char *const argv_in[])
{
char **argv_out;
size_t argc = 0;
assert(prefix);
if (argv_in != NULL) {
while (argv_in[argc] != NULL) {
argc++;
}
}
argv_out = ddsrt_calloc((argc + 2), sizeof(char*));
if (argv_out) {
size_t argi;
argv_out[0] = (char*)prefix;
for (argi = 0; argi < argc; argi++) {
argv_out[argi + 1] = (char*)argv_in[argi];
}
argv_out[argc + 1] = NULL;
}
return argv_out;
}
static void no_op(int sig)
{
(void)sig;
}
static dds_retcode_t
waitpids(
ddsrt_pid_t request_pid,
dds_duration_t timeout,
ddsrt_pid_t *child_pid,
int32_t *code)
{
struct sigaction sigactold;
struct sigaction sigact;
dds_retcode_t rv;
int options = 0;
int ret;
int s;
if (timeout < 0) {
return DDS_RETCODE_BAD_PARAMETER;
}
if (timeout == 0) {
options = WNOHANG;
} else if (timeout != DDS_INFINITY) {
/* Round-up timemout to alarm seconds. */
unsigned secs;
secs = (unsigned)(timeout / DDS_NSECS_IN_SEC);
if ((timeout % DDS_NSECS_IN_SEC) != 0) {
secs++;
}
/* Be sure that the SIGALRM only wakes up waitpid. */
sigemptyset (&sigact.sa_mask);
sigact.sa_handler = no_op;
sigact.sa_flags = 0;
sigaction (SIGALRM, &sigact, &sigactold);
/* Now, set the alarm. */
alarm(secs);
}
ret = waitpid(request_pid, &s, options);
if (ret > 0) {
if (code) {
if (WIFEXITED(s)) {
*code = WEXITSTATUS(s);
} else {
*code = 1;
}
}
if (child_pid) {
*child_pid = ret;
}
rv = DDS_RETCODE_OK;
} else if (ret == 0) {
/* Process is still alive. */
rv = DDS_RETCODE_PRECONDITION_NOT_MET;
} else if ((ret == -1) && (errno == EINTR)) {
/* Interrupted,
* so process(es) likely didn't change state and are/is alive. */
rv = DDS_RETCODE_TIMEOUT;
} else if ((ret == -1) && (errno == ECHILD)) {
/* Unknown pid. */
rv = DDS_RETCODE_NOT_FOUND;
} else {
/* Unknown error. */
rv = DDS_RETCODE_ERROR;
}
if ((timeout != 0) && (timeout != DDS_INFINITY)) {
/* Clean the alarm. */
alarm(0);
/* Reset SIGALRM actions. */
sigaction(SIGALRM, &sigactold, NULL);
}
return rv;
}
dds_retcode_t
ddsrt_proc_create(
const char *executable,
char *const argv[],
ddsrt_pid_t *pid)
{
dds_retcode_t rv;
char **exec_argv;
int exec_fds[2];
int exec_err;
pid_t spawn;
ssize_t nr;
assert(executable != NULL);
assert(pid != NULL);
/* Prefix the argv with the executable, which is the convention. */
exec_argv = prefix_argv(executable, argv);
if (exec_argv == NULL) {
return DDS_RETCODE_OUT_OF_RESOURCES;
}
/* Prepare pipe to know the result of the exec. */
if (pipe(exec_fds) == -1) {
rv = DDS_RETCODE_OUT_OF_RESOURCES;
goto fail_pipe;
}
if ((fcntl(exec_fds[0], F_SETFD, fcntl(exec_fds[0], F_GETFD) | FD_CLOEXEC) == -1) ||
(fcntl(exec_fds[1], F_SETFD, fcntl(exec_fds[1], F_GETFD) | FD_CLOEXEC) == -1) ){
rv = DDS_RETCODE_ERROR;
goto fail_fctl;
}
/* Be sure to not trigger the SIGCHLD. */
signal(SIGCHLD, SIG_DFL);
/* Create a new process. */
spawn = fork();
if (spawn == -1)
{
rv = DDS_RETCODE_ERROR;
goto fail_fork;
}
else if (spawn == 0)
{
/* Child process */
/* Run the executable, replacing current process. */
execv(executable, exec_argv);
/* If executing this, something has gone wrong */
exec_err = errno;
if (write(exec_fds[1], &exec_err, sizeof(int)) < (ssize_t)sizeof(int)) {
DDS_ERROR("Could not write proc error pipe.\n");
}
close(exec_fds[1]);
close(exec_fds[0]);
ddsrt_free(exec_argv);
_exit(1);
}
else
{
/* Parent process */
/* Get execv result. */
rv = DDS_RETCODE_ERROR;
close(exec_fds[1]);
nr = read(exec_fds[0], &exec_err, sizeof(int));
if (nr == 0) {
/* Pipe closed by successful execv. */
rv = DDS_RETCODE_OK;
} else if (nr == sizeof(int)) {
/* Translate execv error. */
if ((exec_err == ENOENT ) ||
(exec_err == ENOEXEC) ){
rv = DDS_RETCODE_BAD_PARAMETER;
} else if (exec_err == EACCES) {
rv = DDS_RETCODE_NOT_ALLOWED;
}
}
close(exec_fds[0]);
if (rv == DDS_RETCODE_OK) {
/* Remember child pid. */
*pid = spawn;
} else {
/* Remove the failed fork pid from the system list. */
waitpid(spawn, NULL, 0);
}
}
ddsrt_free(exec_argv);
return rv;
fail_fork:
fail_fctl:
close(exec_fds[0]);
close(exec_fds[1]);
fail_pipe:
ddsrt_free(exec_argv);
return rv;
}
dds_retcode_t
ddsrt_proc_waitpid(
ddsrt_pid_t pid,
dds_duration_t timeout,
int32_t *code)
{
if (pid > 0) {
return waitpids(pid, timeout, NULL, code);
}
return DDS_RETCODE_BAD_PARAMETER;
}
dds_retcode_t
ddsrt_proc_waitpids(
dds_duration_t timeout,
ddsrt_pid_t *pid,
int32_t *code)
{
return waitpids(0, timeout, pid, code);
}
dds_retcode_t
ddsrt_proc_exists(
ddsrt_pid_t pid)
{
if (kill(pid, 0) == 0)
return DDS_RETCODE_OK;
else if (errno == EPERM)
return DDS_RETCODE_OK;
else if (errno == ESRCH)
return DDS_RETCODE_NOT_FOUND;
else
return DDS_RETCODE_ERROR;
}
dds_retcode_t
ddsrt_proc_kill(
ddsrt_pid_t pid)
{
if (kill(pid, SIGKILL) == 0)
return DDS_RETCODE_OK;
else if (errno == EPERM)
return DDS_RETCODE_ILLEGAL_OPERATION;
else if (errno == ESRCH)
return DDS_RETCODE_BAD_PARAMETER;
else
return DDS_RETCODE_ERROR;
}

View file

@ -0,0 +1,447 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <process.h>
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/string.h"
#include "dds/ddsrt/atomics.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/timeconv.h"
ddsrt_pid_t
ddsrt_getpid(void)
{
return GetCurrentProcessId();
}
static HANDLE pid_to_phdl (ddsrt_pid_t pid);
static dds_retcode_t process_get_exit_code(HANDLE phdl, int32_t *code);
static dds_retcode_t process_kill (HANDLE phdl);
static char* commandline (const char *exe, char *const argv_in[]);
static BOOL child_add (HANDLE phdl);
static void child_remove (HANDLE phdl);
static DWORD child_list (HANDLE *list, DWORD max);
static HANDLE child_handle (ddsrt_pid_t pid);
dds_retcode_t
ddsrt_proc_create(
const char *executable,
char *const argv[],
ddsrt_pid_t *pid)
{
dds_retcode_t rv = DDS_RETCODE_ERROR;
PROCESS_INFORMATION process_info;
STARTUPINFO si;
char *cmd;
LPTCH environment;
assert(executable != NULL);
assert(pid != NULL);
cmd = commandline(executable, argv);
if (cmd == NULL) {
return DDS_RETCODE_OUT_OF_RESOURCES;
}
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
/* The new process will inherit the input/output handles. */
/* TODO: Redirect is not working yet. */
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
/* Get the environment variables to pass along. */
environment = GetEnvironmentStrings();
if(environment){
BOOL created;
created = CreateProcess(executable, // ApplicationName
cmd, // CommandLine
NULL, // ProcessAttributes
NULL, // ThreadAttributes
TRUE, // InheritHandles
CREATE_NO_WINDOW, // dwCreationFlags
(LPVOID)environment, // Environment
NULL, // CurrentDirectory
&si, // StartupInfo
&process_info); // ProcessInformation
if (created) {
if (child_add(process_info.hProcess)) {
*pid = process_info.dwProcessId;
rv = DDS_RETCODE_OK;
} else {
process_kill(process_info.hProcess);
rv = DDS_RETCODE_OUT_OF_RESOURCES;
}
} else {
DWORD error = GetLastError();
if ((ERROR_FILE_NOT_FOUND == error) ||
(ERROR_PATH_NOT_FOUND == error)) {
rv = DDS_RETCODE_BAD_PARAMETER;
} else if (ERROR_ACCESS_DENIED == error) {
rv = DDS_RETCODE_NOT_ALLOWED;
}
}
FreeEnvironmentStrings(environment);
}
ddsrt_free(cmd);
return rv;
}
dds_retcode_t
ddsrt_proc_waitpid(
ddsrt_pid_t pid,
dds_duration_t timeout,
int32_t *code)
{
dds_retcode_t rv = DDS_RETCODE_OK;
HANDLE phdl;
DWORD ret;
if (timeout < 0) {
return DDS_RETCODE_BAD_PARAMETER;
}
phdl = child_handle(pid);
if (phdl == 0) {
return DDS_RETCODE_NOT_FOUND;
}
if (timeout > 0) {
ret = WaitForSingleObject(phdl, ddsrt_duration_to_msecs_ceil(timeout));
if (ret != WAIT_OBJECT_0) {
if (ret == WAIT_TIMEOUT) {
rv = DDS_RETCODE_TIMEOUT;
} else {
rv = DDS_RETCODE_ERROR;
}
}
}
if (rv == DDS_RETCODE_OK) {
rv = process_get_exit_code(phdl, code);
}
if (rv == DDS_RETCODE_OK) {
child_remove(phdl);
}
return rv;
}
dds_retcode_t
ddsrt_proc_waitpids(
dds_duration_t timeout,
ddsrt_pid_t *pid,
int32_t *code)
{
dds_retcode_t rv = DDS_RETCODE_OK;
HANDLE hdls[MAXIMUM_WAIT_OBJECTS];
HANDLE phdl;
DWORD cnt;
DWORD ret;
if (timeout < 0) {
return DDS_RETCODE_BAD_PARAMETER;
}
cnt = child_list(hdls, MAXIMUM_WAIT_OBJECTS);
if (cnt == 0) {
return DDS_RETCODE_NOT_FOUND;
}
ret = WaitForMultipleObjects(cnt, hdls, FALSE, ddsrt_duration_to_msecs_ceil(timeout));
if ((ret < WAIT_OBJECT_0) || (ret >= (WAIT_OBJECT_0 + cnt))) {
if (ret == WAIT_TIMEOUT) {
if (timeout == 0) {
rv = DDS_RETCODE_PRECONDITION_NOT_MET;
} else {
rv = DDS_RETCODE_TIMEOUT;
}
} else {
rv = DDS_RETCODE_ERROR;
}
} else {
/* Get the handle of the specific child that was triggered. */
phdl = hdls[ret - WAIT_OBJECT_0];
}
if (rv == DDS_RETCODE_OK) {
rv = process_get_exit_code(phdl, code);
}
if (rv == DDS_RETCODE_OK) {
if (pid) {
*pid = GetProcessId(phdl);
}
child_remove(phdl);
}
return rv;
}
dds_retcode_t
ddsrt_proc_exists(
ddsrt_pid_t pid)
{
dds_retcode_t rv = DDS_RETCODE_NOT_FOUND;
HANDLE phdl;
phdl = pid_to_phdl(pid);
if (phdl != 0) {
rv = process_get_exit_code(phdl, NULL);
if (rv == DDS_RETCODE_PRECONDITION_NOT_MET) {
/* Process still exists. */
rv = DDS_RETCODE_OK;
} else if (rv == DDS_RETCODE_OK) {
/* The process has gone. */
rv = DDS_RETCODE_NOT_FOUND;
} else {
rv = DDS_RETCODE_ERROR;
}
CloseHandle(phdl);
}
return rv;
}
dds_retcode_t
ddsrt_proc_kill(
ddsrt_pid_t pid)
{
dds_retcode_t rv = DDS_RETCODE_BAD_PARAMETER;
HANDLE phdl;
phdl = pid_to_phdl(pid);
if (phdl != 0) {
/* Forcefully kill. */
rv = process_kill(phdl);
CloseHandle(phdl);
}
return rv;
}
static HANDLE
pid_to_phdl(ddsrt_pid_t pid)
{
return OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
}
static dds_retcode_t
process_get_exit_code(
HANDLE phdl,
int32_t *code)
{
dds_retcode_t rv = DDS_RETCODE_ERROR;
DWORD tr;
assert(phdl != 0);
if (GetExitCodeProcess(phdl, &tr)) {
if (tr == STILL_ACTIVE) {
rv = DDS_RETCODE_PRECONDITION_NOT_MET;
} else {
if (code) {
*code = (int32_t)tr;
}
rv = DDS_RETCODE_OK;
}
}
return rv;
}
/* Forcefully kill the given process. */
static dds_retcode_t
process_kill(HANDLE phdl)
{
assert(phdl != 0);
if (TerminateProcess(phdl, 1 /* exit code */) != 0) {
return DDS_RETCODE_OK;
}
return DDS_RETCODE_ERROR;
}
/* Add quotes to a given string, escape it and add it to a buffer. */
static char*
insert_char(char *buf, size_t *len, size_t *max, char c)
{
static const size_t buf_inc = 64;
if (*len == *max) {
*max += buf_inc;
buf = ddsrt_realloc(buf, *max);
}
if (buf) {
buf[(*len)++] = c;
}
return buf;
}
static char*
stringify_cat(char *buf, size_t *len, size_t *max, const char *str)
{
char last = '\0';
/* Start stringification with an opening double-quote. */
buf = insert_char(buf, len, max, '\"');
if (!buf) goto end;
/* Copy and escape the string. */
while ((*str) != '\0') {
if (*str == '\"') {
buf = insert_char(buf, len, max, '\\');
if (!buf) goto end;
}
buf = insert_char(buf, len, max, *str);
if (!buf) goto end;
last = *str;
str++;
}
/* For some reason, only the last backslash will be stripped.
* No need to escape the other backslashes. */
if (last == '\\') {
buf = insert_char(buf, len, max, '\\');
if (!buf) goto end;
}
/* End stringification. */
buf = insert_char(buf, len, max, '\"');
if (!buf) goto end;
buf = insert_char(buf, len, max, ' ');
end:
return buf;
}
/* Create command line with executable and arguments. */
static char*
commandline(const char *exe, char *const argv_in[])
{
char *cmd = NULL;
size_t len = 0;
size_t max = 0;
size_t argi;
assert(exe);
/* Add quoted and escaped executable. */
cmd = stringify_cat(cmd, &len, &max, exe);
if (!cmd) goto end;
/* Add quoted and escaped arguments. */
if (argv_in != NULL) {
for (argi = 0; argv_in[argi] != NULL; argi++) {
cmd = stringify_cat(cmd, &len, &max, argv_in[argi]);
if (!cmd) goto end;
}
}
/* Terminate command line string. */
cmd = insert_char(cmd, &len, &max, '\0');
end:
return cmd;
}
/* Maintain a list of children to be able to wait for all them. */
static ddsrt_atomic_voidp_t g_children[MAXIMUM_WAIT_OBJECTS] = {0};
static BOOL
child_update(HANDLE old, HANDLE new)
{
BOOL updated = FALSE;
for (int i = 0; (i < MAXIMUM_WAIT_OBJECTS) && (!updated); i++)
{
updated = ddsrt_atomic_casvoidp(&(g_children[i]), old, new);
}
return updated;
}
static BOOL
child_add(HANDLE phdl)
{
return child_update(0, phdl);
}
static void
child_remove(HANDLE phdl)
{
(void)child_update(phdl, 0);
}
static DWORD
child_list(HANDLE *list, DWORD max)
{
HANDLE hdl;
int cnt = 0;
assert(list);
assert(max <= MAXIMUM_WAIT_OBJECTS);
for (int i = 0; (i < MAXIMUM_WAIT_OBJECTS); i++)
{
hdl = ddsrt_atomic_ldvoidp(&(g_children[i]));
if (hdl != 0) {
list[cnt++] = hdl;
}
}
return cnt;
}
static HANDLE
child_handle(ddsrt_pid_t pid)
{
HANDLE phdl = 0;
for (int i = 0; (i < MAXIMUM_WAIT_OBJECTS) && (phdl == 0); i++)
{
phdl = ddsrt_atomic_ldvoidp(&(g_children[i]));
if (phdl != 0) {
if (GetProcessId(phdl) != pid) {
phdl = 0;
}
}
}
return phdl;
}

View file

@ -25,6 +25,7 @@ set(sources
"random.c"
"strlcpy.c"
"socket.c"
"process.c"
"select.c")
add_cunit_executable(cunit_ddsrt ${sources})
@ -42,3 +43,26 @@ endif()
target_include_directories(
cunit_ddsrt PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
# Create a separate test application that will be used to
# test process management.
add_executable(process_app process_app.c)
target_link_libraries(process_app PRIVATE ddsrt)
target_include_directories(
process_app
PRIVATE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
# Force the app to be at the same location, no matter what platform or build type.
set_target_properties(
process_app
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_CURRENT_BINARY_DIR} )
# Let the cunit application know the location and name of the test application.
set(process_app_name "${CMAKE_CURRENT_BINARY_DIR}/process_app${CMAKE_EXECUTABLE_SUFFIX}")
configure_file(
"process_test.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/process_test.h" @ONLY)

View file

@ -14,6 +14,7 @@
#include "CUnit/Theory.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/misc.h"
#include "dds/ddsrt/heap.h"
CU_TheoryDataPoints(ddsrt_environ, bad_name) = {
CU_DataPoints(const char *, "", "foo=")
@ -91,8 +92,110 @@ CU_Test(ddsrt_environ, getenv)
CU_ASSERT_STRING_EQUAL(ptr, "bar");
}
/* Ensure environement is as it was. */
/* Ensure environment is as it was. */
rc = ddsrt_unsetenv(name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
}
CU_TheoryDataPoints(ddsrt_environ, expand) = {
CU_DataPoints(const char *,
"${X}", "$X", "X", "${Y}", "${Q}", "${X",
"${X:-ALT}", "${Q:-ALT}", "${X:-${Y}}", "${Q:-${Y}}", "${X:-$Y}", "${Q:-$Y}", "${X:-}", "${Q:-}",
"${X:+SET}", "${Q:+SET}", "${X:+${Y}}", "${Q:+${Y}}", "${X:+$Y}", "${Q:+$Y}", "${X:+}", "${Q:+}",
"${X:?SET}", "${Q:?SET}", "${X:?${Y}}", "${Q:?${Y}}", "${X:?$Y}", "${Q:?$Y}", "${X:?}", "${Q:?}"),
CU_DataPoints(const char *,
"TEST", "$X", "X", "FOO", "", NULL,
"TEST", "ALT", "TEST", "FOO", "TEST", "$Y", "TEST", "",
"SET", "", "FOO", "", "$Y", "", "", "",
"TEST", NULL, "TEST", NULL, "TEST", NULL, "TEST", NULL)
};
CU_Theory((const char *var, const char *expect), ddsrt_environ, expand)
{
dds_retcode_t rc;
static const char x_name[] = "X";
static const char x_value[] = "TEST";
static const char y_name[] = "Y";
static const char y_value[] = "FOO";
char *ptr;
/* Ensure that the vars are not used yet. */
rc = ddsrt_unsetenv(x_name);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_unsetenv(y_name);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
/* Set the env vars to check expansion. */
rc = ddsrt_setenv(x_name, x_value);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_setenv(y_name, y_value);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
/* Expand a string with available environment variables. */
ptr = ddsrt_expand_envvars(var);
if (ptr) {
/* printf("==== %10s: expand(%s), expect(%s))\n", var, ptr, expect); */
CU_ASSERT_STRING_EQUAL(ptr, expect);
ddsrt_free(ptr);
} else {
/* printf("==== %10s: expand(<null>), expect(<null>))\n", var ? var : "<null>"); */
CU_ASSERT_PTR_NULL(expect);
}
/* Ensure to reset the environment is as it was. */
rc = ddsrt_unsetenv(y_name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
rc = ddsrt_unsetenv(x_name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
}
CU_TheoryDataPoints(ddsrt_environ, expand_sh) = {
CU_DataPoints(const char *,
"${X}", "$X", "X", "${Y}", "${Q}", "${X",
"${X:-ALT}", "${Q:-ALT}", "${X:-${Y}}", "${Q:-${Y}}", "${X:-$Y}", "${Q:-$Y}", "${X:-}", "${Q:-}",
"${X:+SET}", "${Q:+SET}", "${X:+${Y}}", "${Q:+${Y}}", "${X:+$Y}", "${Q:+$Y}", "${X:+}", "${Q:+}",
"${X:?SET}", "${Q:?SET}", "${X:?${Y}}", "${Q:?${Y}}", "${X:?$Y}", "${Q:?$Y}", "${X:?}", "${Q:?}"),
CU_DataPoints(const char *,
"TEST", "TEST", "X", "FOO", "", NULL,
"TEST", "ALT", "TEST", "FOO", "TEST", "FOO", "TEST", "",
"SET", "", "FOO", "", "FOO", "", "", "",
"TEST", NULL, "TEST", NULL, "TEST", NULL, "TEST", NULL)
};
CU_Theory((const char *var, const char *expect), ddsrt_environ, expand_sh)
{
dds_retcode_t rc;
static const char x_name[] = "X";
static const char x_value[] = "TEST";
static const char y_name[] = "Y";
static const char y_value[] = "FOO";
char *ptr;
/* Ensure that the vars are not used yet. */
rc = ddsrt_unsetenv(x_name);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_unsetenv(y_name);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
/* Set the env vars to check expansion. */
rc = ddsrt_setenv(x_name, x_value);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
rc = ddsrt_setenv(y_name, y_value);
CU_ASSERT_EQUAL_FATAL(rc, DDS_RETCODE_OK);
/* Expand a string with available environment variables. */
ptr = ddsrt_expand_envvars_sh(var);
if (ptr) {
/* printf("==== %10s: expand(%s), expect(%s))\n", var, ptr, expect); */
CU_ASSERT_STRING_EQUAL(ptr, expect);
ddsrt_free(ptr);
} else {
/* printf("==== %10s: expand(<null>), expect(<null>))\n", var ? var : "<null>"); */
CU_ASSERT_PTR_NULL(expect);
}
/* Ensure to reset the environment is as it was. */
rc = ddsrt_unsetenv(y_name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
rc = ddsrt_unsetenv(x_name);
CU_ASSERT_EQUAL(rc, DDS_RETCODE_OK);
}

284
src/ddsrt/tests/process.c Normal file
View file

@ -0,0 +1,284 @@
/*
* Copyright(c) 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "CUnit/Test.h"
#include "process_test.h"
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/strtol.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/process.h"
/*
* Create a process that is expected to exit quickly.
* Compare the exit code with the expected exit code.
*/
static void create_and_test_exit(const char *arg, int code)
{
dds_retcode_t ret;
ddsrt_pid_t pid;
int32_t status;
char *argv[] = { NULL, NULL };
argv[0] = (char*)arg;
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), &status);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
/* Check result. */
CU_ASSERT_EQUAL(status, code);
/* Garbage collection when needed. */
if (ret != DDS_RETCODE_OK) {
ddsrt_proc_kill(pid);
}
}
/*
* Try to create a process without arguments.
* The exit status of the process should be PROCESS_DONE_NOTHING_EXIT_CODE.
*/
CU_Test(ddsrt_process, create)
{
create_and_test_exit(NULL, TEST_CREATE_EXIT);
}
/*
* Create a process that'll sleep for a while.
* Try to kill that process.
*/
CU_Test(ddsrt_process, kill)
{
dds_retcode_t ret;
ddsrt_pid_t pid;
/* Sleep for 20 seconds. It should be killed before then. */
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
/* Check if process is running. */
ret = ddsrt_proc_exists(pid);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
/* Kill it. */
ret = ddsrt_proc_kill(pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* Check if process is actually gone. */
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
ret = ddsrt_proc_exists(pid);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_NOT_FOUND);
}
/*
* Create a process that'll return it's own pid value (reduced
* to fit the exit code range). It should match the pid that was
* returned by the process create (also reduced to be able to
* match the returned semi-pid value).
*/
CU_Test(ddsrt_process, pid)
{
dds_retcode_t ret;
ddsrt_pid_t pid;
int32_t status;
char *argv[] = { TEST_PID_ARG, NULL };
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), &status);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
/* Compare the pid values. */
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid));
/* Garbage collection when needed. */
if (ret != DDS_RETCODE_OK) {
ddsrt_proc_kill(pid);
}
}
/*
* Set a environment variable in the parent process.
* Create a process that should have access to that env var.
*/
CU_Test(ddsrt_process, env)
{
dds_retcode_t ret;
ret = ddsrt_setenv(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
create_and_test_exit(TEST_ENV_ARG, TEST_ENV_EXIT);
}
/*
* Try to create a process with an non-existing executable file.
* It should fail.
*/
CU_Test(ddsrt_process, invalid)
{
dds_retcode_t ret;
ddsrt_pid_t pid;
ret = ddsrt_proc_create("ProbablyNotAnValidExecutable", NULL, &pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_BAD_PARAMETER);
/* Garbage collection when needed. */
if (ret == DDS_RETCODE_OK) {
ddsrt_proc_kill(pid);
}
}
/*
* Create a process with a backslash in the argument
*/
CU_Test(ddsrt_process, arg_bslash)
{
create_and_test_exit(TEST_BSLASH_ARG, TEST_BSLASH_EXIT);
}
/*
* Create a process with a double-quote in the argument
*/
CU_Test(ddsrt_process, arg_dquote)
{
create_and_test_exit(TEST_DQUOTE_ARG, TEST_DQUOTE_EXIT);
}
/*
* Create two processes and wait for them simultaneously.
*/
CU_Test(ddsrt_process, waitpids)
{
dds_retcode_t ret;
ddsrt_pid_t child;
ddsrt_pid_t pid1 = 0;
ddsrt_pid_t pid2 = 0;
int32_t status;
/* Use retpid option to identify return values. */
char *argv[] = { TEST_PID_ARG, NULL };
/* Start two processes. */
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid1);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid2);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
/* Wait for both processes to have finished. */
while (((pid1 != 0) || (pid2 != 0)) && (ret == DDS_RETCODE_OK)) {
ret = ddsrt_proc_waitpids(DDS_SECS(10), &child, &status);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
if (child == pid1) {
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid1));
pid1 = 0;
} else if (child == pid2) {
CU_ASSERT_EQUAL(status, TEST_PID_EXIT(pid2));
pid2 = 0;
} else {
CU_ASSERT(0);
}
}
ret = ddsrt_proc_waitpids(DDS_SECS(10), &child, &status);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_NOT_FOUND);
/* Garbage collection when needed. */
if (pid1 != 0) {
ddsrt_proc_kill(pid1);
}
if (pid2 != 0) {
ddsrt_proc_kill(pid2);
}
}
/*
* Create a sleeping process. Waiting for it should timeout.
*/
CU_Test(ddsrt_process, waitpid_timeout)
{
dds_retcode_t ret;
ddsrt_pid_t pid;
/* Sleep for 20 seconds. We should have a timeout before then. */
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
/* Timeout 0 should return DDS_RETCODE_PRECONDITION_NOT_MET when alive. */
ret = ddsrt_proc_waitpid(pid, 0, NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_PRECONDITION_NOT_MET);
/* Valid timeout should return DDS_RETCODE_TIMEOUT when alive. */
ret = ddsrt_proc_waitpid(pid, DDS_SECS(1), NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_TIMEOUT);
/* Kill it. */
ret = ddsrt_proc_kill(pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* When killed, DDS_RETCODE_OK should be returned. */
ret = ddsrt_proc_waitpid(pid, DDS_SECS(10), NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
}
/*
* Create a sleeping process. Waiting for it should timeout.
*/
CU_Test(ddsrt_process, waitpids_timeout)
{
dds_retcode_t ret;
ddsrt_pid_t pid;
/* Sleep for 20 seconds. We should have a timeout before then. */
char *argv[] = { TEST_SLEEP_ARG, "20", NULL };
ret = ddsrt_proc_create(TEST_APPLICATION, argv, &pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
CU_ASSERT_NOT_EQUAL_FATAL(pid, 0);
/* Timeout 0 should return DDS_RETCODE_PRECONDITION_NOT_MET when alive. */
ret = ddsrt_proc_waitpids(0, NULL, NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_PRECONDITION_NOT_MET);
/* Valid timeout should return DDS_RETCODE_TIMEOUT when alive. */
ret = ddsrt_proc_waitpids(DDS_SECS(1), NULL, NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_TIMEOUT);
/* Kill it. */
ret = ddsrt_proc_kill(pid);
CU_ASSERT_EQUAL_FATAL(ret, DDS_RETCODE_OK);
/* When killed, DDS_RETCODE_OK should be returned. */
ret = ddsrt_proc_waitpids(DDS_SECS(10), NULL, NULL);
CU_ASSERT_EQUAL(ret, DDS_RETCODE_OK);
}

View file

@ -0,0 +1,108 @@
/*
* Copyright(c) 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "process_test.h"
#include "dds/ddsrt/strtol.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/process.h"
static int test_create(void)
{
printf(" Process: created without args.\n");
return TEST_CREATE_EXIT;
}
static int test_sleep(int argi, int argc, char **argv)
{
argi++;
if (argi < argc) {
long long dorment;
ddsrt_strtoll(argv[argi], NULL, 0, &dorment);
printf(" Process: sleep %d seconds.\n", (int)dorment);
dds_sleepfor(DDS_SECS((int64_t)dorment));
} else {
printf(" Process: no --sleep value.\n");
return TEST_EXIT_WRONG_ARGS;
}
/* Expected to be destroyed before reaching this. */
return TEST_EXIT_FAILURE;
}
static int test_pid(void)
{
int ret;
ddsrt_pid_t pid;
pid = ddsrt_getpid();
ret = TEST_PID_EXIT(pid);
printf(" Process: pid %d reduced to %d exit code.\n", (int)pid, ret);
return ret;
}
static int test_env(void)
{
int ret = TEST_EXIT_FAILURE;
char *envptr = NULL;
if (ddsrt_getenv(TEST_ENV_VAR_NAME, &envptr) == DDS_RETCODE_OK) {
printf(" Process: env %s=%s.\n", TEST_ENV_VAR_NAME, envptr);
if (strcmp(envptr, TEST_ENV_VAR_VALUE) == 0) {
ret = TEST_ENV_EXIT;
}
} else {
printf(" Process: failed to get environment variable.\n");
}
return ret;
}
static int test_bslash(void)
{
printf(" Process: backslash argument.\n");
return TEST_BSLASH_EXIT;
}
static int test_dquote(void)
{
printf(" Process: double-quote argument.\n");
return TEST_DQUOTE_EXIT;
}
/*
* The spawned application used in the process tests.
*/
int main(int argc, char **argv)
{
int ret;
if (argc == 1) {
ret = test_create();
} else {
if (strcmp(argv[1], TEST_SLEEP_ARG) == 0) {
ret = test_sleep(1, argc, argv);
} else if (strcmp(argv[1], TEST_PID_ARG) == 0) {
ret = test_pid();
} else if (strcmp(argv[1], TEST_ENV_ARG) == 0) {
ret = test_env();
} else if (strcmp(argv[1], TEST_BSLASH_ARG) == 0) {
ret = test_bslash();
} else if (strcmp(argv[1], TEST_DQUOTE_ARG) == 0) {
ret = test_dquote();
} else {
printf(" Process: unknown argument.\n");
ret = TEST_EXIT_WRONG_ARGS;
}
}
return ret;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef DDSRT_TEST_PROCESS_TEST_H
#define DDSRT_TEST_PROCESS_TEST_H
/* Get the application name from cmake to automatically
* get the proper extension and location. */
#define TEST_APPLICATION "@process_app_name@"
#define TEST_SLEEP_ARG "--sleep"
#define TEST_EXIT_GENERIC_OK (0)
#define TEST_EXIT_FAILURE (1)
#define TEST_EXIT_WRONG_ARGS (2)
#define TEST_CREATE_ARG NULL
#define TEST_CREATE_EXIT (0)
#define TEST_PID_ARG "--retpid"
#define TEST_PID_EXIT(pid) ((int)(int32_t)(pid % 127))
#define TEST_ENV_ARG "--checkenv"
#define TEST_ENV_EXIT (12)
#define TEST_ENV_VAR_NAME "TEST_ENV_VAR_NAME"
#define TEST_ENV_VAR_VALUE "TEST_ENV_VAR_VALUE"
#define TEST_BSLASH_ARG "\\left\\\\right\\"
#define TEST_BSLASH_EXIT (int)('\\')
#define TEST_DQUOTE_ARG "\"left\"\"right\""
#define TEST_DQUOTE_EXIT (int)('"')
#endif /* DDSRT_TEST_PROCESS_TEST_H */

23
src/mpt/CMakeLists.txt Normal file
View file

@ -0,0 +1,23 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
cmake_minimum_required(VERSION 3.7)
option(MPT_ENABLE_SELFTEST "Enable multi-process test-framework self test" OFF)
set(MPT_CMAKE "${CMAKE_CURRENT_SOURCE_DIR}/mpt/cmake/MPT.cmake")
set(MPT_SOURCE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(MPT_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}")
set(MPT_DEFAULT_TIMEOUT "60")
add_subdirectory(mpt)
add_subdirectory(tests)

View file

@ -0,0 +1,17 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/include/mpt/resource.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/mpt/resource.h" @ONLY)

173
src/mpt/mpt/cmake/MPT.cmake Normal file
View file

@ -0,0 +1,173 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
set(MPT_DIR "${CMAKE_CURRENT_LIST_DIR}/..")
function(parse_mpt_fixtures INPUT TEST_DISABLED TEST_TIMEOUT TEST_XFAIL)
set(s "[ \t\r\n]")
if(INPUT MATCHES ".disabled${s}*=${s}*([tT][rR][uU][eE]|[0-9]+)")
set(${TEST_DISABLED} "TRUE" PARENT_SCOPE)
else()
set(${TEST_DISABLED} "FALSE" PARENT_SCOPE)
endif()
if(INPUT MATCHES ".timeout${s}*=${s}*([0-9]+)")
set(${TEST_TIMEOUT} "${CMAKE_MATCH_1}" PARENT_SCOPE)
else()
set(${TEST_TIMEOUT} "${MPT_DEFAULT_TIMEOUT}" PARENT_SCOPE)
endif()
if(INPUT MATCHES ".xfail${s}*=${s}*([tT][rR][uU][eE]|[0-9]+)")
set(${TEST_XFAIL} "true" PARENT_SCOPE)
else()
set(${TEST_XFAIL} "false" PARENT_SCOPE)
endif()
endfunction()
function(process_mpt_source_file SOURCE_FILE SUITES TESTS PROCESSES)
unset(tests)
unset(processes)
set(x "\\*")
set(s "[ \t\r\n]")
set(s_or_x "[ \t\r\n\\*]")
set(w "[_a-zA-Z0-9]")
set(ident_expr "(${s}*${w}+${s}*)")
# Very basic type recognition, only things that contain word characters and
# pointers are handled. And since this script does not actually have to
# compile any code, type checking is left to the compiler. An error (or
# warning) will be thrown if something is off.
#
# The "type" regular expression below will match things like:
# - <word> <word>
# - <word> *<word>
# - <word>* <word> *<word>
set(type_expr "(${s}*${w}+${x}*${s}+${s_or_x}*)+")
set(param_expr "${type_expr}${ident_expr}")
# Test fixture support (based on test fixtures as implemented in Criterion),
# to enable per-test (de)initializers, which is very different from
# per-suite (de)initializers, and timeouts.
#
# The following fixtures are supported:
# - init
# - fini
# - disabled
# - timeout
set(data_expr "(${s}*,${s}*\\.${w}+${s}*=[^,\\)]+)*")
file(READ "${SOURCE_FILE}" content)
# MPT_Test
set(test_expr "MPT_Test${s}*\\(${ident_expr},${ident_expr}${data_expr}\\)")
string(REGEX MATCHALL "${test_expr}" matches "${content}")
foreach(match ${matches})
string(REGEX REPLACE "${test_expr}" "\\1" suite "${match}")
string(REGEX REPLACE "${test_expr}" "\\2" test "${match}")
# Remove leading and trailing whitespace
string(STRIP "${suite}" suite)
string(STRIP "${test}" test)
# Extract fixtures that must be handled by CMake (.disabled and .timeout).
parse_mpt_fixtures("${match}" disabled timeout xfail)
list(APPEND tests "${suite}:${test}:${disabled}:${timeout}:${xfail}")
list(APPEND suites "${suite}")
endforeach()
# MPT_TestProcess
set(process_expr "MPT_TestProcess${s}*\\(${ident_expr},${ident_expr},${ident_expr}")
string(REGEX MATCHALL "${process_expr}" matches "${content}")
foreach(match ${matches})
string(REGEX REPLACE "${process_expr}" "\\1" suite "${match}")
string(REGEX REPLACE "${process_expr}" "\\2" test "${match}")
string(REGEX REPLACE "${process_expr}" "\\3" id "${match}")
# Remove leading and trailing whitespace
string(STRIP "${suite}" suite)
string(STRIP "${test}" test)
string(STRIP "${id}" id)
list(APPEND processes "${suite}:${test}:${id}")
endforeach()
set(${PROCESSES} ${processes} PARENT_SCOPE)
set(${TESTS} ${tests} PARENT_SCOPE)
set(${SUITES} ${suites} PARENT_SCOPE)
endfunction()
function(add_mpt_executable TARGET)
set(sources)
set(processes)
foreach(source ${ARGN})
if((EXISTS "${source}" OR EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${source}"))
unset(processes)
unset(tests)
unset(suites)
# Disable missing-field-initializers warnings as not having to specify
# every member, aka fixture, is intended behavior.
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang" OR
${CMAKE_C_COMPILER_ID} STREQUAL "AppleClang" OR
${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
set_property(
SOURCE "${source}"
PROPERTY COMPILE_FLAGS -Wno-missing-field-initializers)
endif()
process_mpt_source_file("${source}" suites tests processes)
foreach(suite ${suites})
set(addsuites "${addsuites}\n mpt_add_suite(mpt_suite_new(\"${suite}\"));")
endforeach()
foreach(testcase ${tests})
string(REPLACE ":" ";" testcase ${testcase})
list(GET testcase 0 suite)
list(GET testcase 1 test)
list(GET testcase 2 disabled)
list(GET testcase 3 timeout)
list(GET testcase 4 xfail)
set(addtests "${addtests}\n mpt_add_test(\"${suite}\", mpt_test_new(\"${test}\", ${timeout}, ${xfail}));")
# Add this test to ctest.
set(ctest "${TARGET}_${suite}_${test}")
add_test(
NAME ${ctest}
COMMAND ${TARGET} -s ${suite} -t ${test})
set_property(TEST ${ctest} PROPERTY TIMEOUT ${timeout})
set_property(TEST ${ctest} PROPERTY DISABLED ${disabled})
endforeach()
foreach(process ${processes})
string(REPLACE ":" ";" process ${process})
list(GET process 0 suite)
list(GET process 1 test)
list(GET process 2 id)
set(addprocs "${addprocs}\n mpt_add_process(\"${suite}\", \"${test}\", mpt_process_new(\"${id}\", MPT_TestProcessName(${suite}, ${test}, ${id})));")
set(procdecls "${procdecls}\nextern MPT_TestProcessDeclaration(${suite}, ${test}, ${id});")
endforeach()
list(APPEND sources "${source}")
endif()
endforeach()
configure_file(
"${MPT_DIR}/src/main.c.in" "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.c" @ONLY)
add_executable(
${TARGET} "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.c" ${sources})
target_include_directories(${TARGET} PRIVATE "${MPT_DIR}/include" "${MPT_BINARY_ROOT_DIR}/mpt/include")
target_link_libraries(${TARGET} PRIVATE ddsc)
endfunction()

View file

@ -0,0 +1,157 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef MPT_H_INCLUDED
#define MPT_H_INCLUDED
#include <stdio.h>
#include <stdbool.h>
#include "mpt/resource.h"
#include "mpt/private/mpt.h"
#include "dds/ddsrt/environ.h"
/* Environment name/value pair. */
typedef struct mpt_env_ {
const char *name;
const char *value;
} mpt_env_t;
/* Process entry argument definitions. */
#define MPT_Args(...) MPT_ProcessArgsSyntax, __VA_ARGS__
#define MPT_NoArgs() MPT_ProcessArgsSyntax
#define MPT_ArgValues(...) MPT_ProcessArgs, __VA_ARGS__
#define MPT_NoArgValues() MPT_ProcessArgs
/* Process entry definition. */
#define MPT_ProcessEntry(process, args)\
void MPT_ProcessEntryName(process)(args)
/* IPC functions. */
#define MPT_Send(str) mpt_ipc_send(MPT_ProcessArgs, str);
#define MPT_Wait(str) mpt_ipc_wait(MPT_ProcessArgs, str);
/*
* MPT_TestProcess generates a wrapper function that takes care of
* per-process initialization, environment settings,
* deinitialization and the actual process entry call.
*/
#define MPT_TestProcess(suite, test, name, process, args, ...) \
MPT_TestInitDeclaration(suite, test); \
MPT_TestFiniDeclaration(suite, test); \
MPT_TestProcessDeclaration(suite, test, name) \
{ \
mpt_data_t data = MPT_Fixture(__VA_ARGS__); \
\
/* Always export the process name. */ \
/* This can be used to generate unique log files fi. */ \
ddsrt_setenv("MPT_PROCESS_NAME", \
MPT_XSTR(MPT_TestProcessName(suite, test, name))); \
\
/* Initialize test related stuff first. */ \
MPT_TestInitName(suite, test)(); \
\
/* Pre-process initialization. */ \
mpt_export_env(data.environment); \
if (data.init != NULL) { \
data.init(); \
} \
\
/* Execute the actual process entry function. */ \
MPT_ProcessEntryName(process)(args); \
\
/* Teardown process and test. */ \
if (data.fini != NULL) { \
data.fini(); \
} \
MPT_TestFiniName(suite, test)(); \
}
/*
* MPT_Test generates wrapper functions that take care of
* per-test initialization, environment settings and
* deinitialization.
* This is also used by CMake to determine the ctest timeout
* and disabled settings.
*/
#define MPT_Test(suite, test, ...) \
MPT_TestInitDeclaration(suite, test) \
{ \
mpt_data_t data = MPT_Fixture(__VA_ARGS__); \
mpt_export_env(data.environment); \
if (data.init != NULL) { \
data.init(); \
} \
} \
MPT_TestFiniDeclaration(suite, test) \
{ \
mpt_data_t data = MPT_Fixture(__VA_ARGS__); \
if (data.fini != NULL) { \
data.fini(); \
} \
}
/*
* Test asserts.
* Printing is supported eg MPT_ASSERT_EQ(a, b, "foo: %s", bar")
*/
#define MPT_ASSERT(cond, ...) MPT__ASSERT__(cond, MPT_FATAL_NO, __VA_ARGS__)
#define MPT_ASSERT_FAIL(...) MPT_ASSERT(0, __VA_ARGS__)
#define MPT_ASSERT_EQ(value, expected, ...) MPT_ASSERT((value == expected), __VA_ARGS__)
#define MPT_ASSERT_NEQ(value, expected, ...) MPT_ASSERT((value != expected), __VA_ARGS__)
#define MPT_ASSERT_LEQ(value, expected, ...) MPT_ASSERT((value <= expected), __VA_ARGS__)
#define MPT_ASSERT_GEQ(value, expected, ...) MPT_ASSERT((value >= expected), __VA_ARGS__)
#define MPT_ASSERT_LT(value, expected, ...) MPT_ASSERT((value < expected), __VA_ARGS__)
#define MPT_ASSERT_GT(value, expected, ...) MPT_ASSERT((value > expected), __VA_ARGS__)
#define MPT_ASSERT_NULL(value, ...) MPT_ASSERT((value == NULL), __VA_ARGS__)
#define MPT_ASSERT_NOT_NULL(value, ...) MPT_ASSERT((value != NULL), __VA_ARGS__)
#define MPT_ASSERT_STR_EQ(value, expected, ...) MPT_ASSERT((MPT_STRCMP(value, expected, 1) == 0), __VA_ARGS__)
#define MPT_ASSERT_STR_NEQ(value, expected, ...) MPT_ASSERT((MPT_STRCMP(value, expected, 0) != 0), __VA_ARGS__)
#define MPT_ASSERT_STR_EMPTY(value, ...) MPT_ASSERT((MPT_STRLEN(value, 1) == 0), __VA_ARGS__)
#define MPT_ASSERT_STR_NOT_EMPTY(value, ...) MPT_ASSERT((MPT_STRLEN(value, 0) > 0), __VA_ARGS__)
/* Fatal just means that control is returned to the parent function. */
#define MPT_ASSERT_FATAL(cond, ...) MPT__ASSERT__(cond, MPT_FATAL_YES, __VA_ARGS__)
#define MPT_ASSERT_FATAL_FAIL(...) MPT_ASSERT_FATAL(0, __VA_ARGS__)
#define MPT_ASSERT_FATAL_EQ(value, expected, ...) MPT_ASSERT_FATAL((value == expected), __VA_ARGS__)
#define MPT_ASSERT_FATAL_NEQ(value, expected, ...) MPT_ASSERT_FATAL((value != expected), __VA_ARGS__)
#define MPT_ASSERT_FATAL_LEQ(value, expected, ...) MPT_ASSERT_FATAL((value <= expected), __VA_ARGS__)
#define MPT_ASSERT_FATAL_GEQ(value, expected, ...) MPT_ASSERT_FATAL((value >= expected), __VA_ARGS__)
#define MPT_ASSERT_FATAL_LT(value, expected, ...) MPT_ASSERT_FATAL((value < expected), __VA_ARGS__)
#define MPT_ASSERT_FATAL_GT(value, expected, ...) MPT_ASSERT_FATAL((value > expected), __VA_ARGS__)
#define MPT_ASSERT_FATAL_NULL(value, ...) MPT_ASSERT_FATAL((value == NULL), __VA_ARGS__)
#define MPT_ASSERT_FATAL_NOT_NULL(value, ...) MPT_ASSERT_FATAL((value != NULL), __VA_ARGS__)
#define MPT_ASSERT_FATAL_STR_EQ(value, expected, ...) MPT_ASSERT_FATAL((MPT_STRCMP(value, expected, 1) == 0), __VA_ARGS__)
#define MPT_ASSERT_FATAL_STR_NEQ(value, expected, ...) MPT_ASSERT_FATAL((MPT_STRCMP(value, expected, 0) != 0), __VA_ARGS__)
#define MPT_ASSERT_FATAL_STR_EMPTY(value, ...) MPT_ASSERT_FATAL((MPT_STRLEN(value, 1) == 0), __VA_ARGS__)
#define MPT_ASSERT_FATAL_STR_NOT_EMPTY(value, ...) MPT_ASSERT_FATAL((MPT_STRLEN(value, 0) > 0), __VA_ARGS__)
/* Helpful function to check for patterns in log callbacks. */
int mpt_patmatch(const char *pat, const char *str);
#endif /* MPT_H_INCLUDED */

View file

@ -0,0 +1,153 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef MPT_PRIVATE_H_INCLUDED
#define MPT_PRIVATE_H_INCLUDED
#include <stdio.h>
#include <string.h>
/*
* Just some helpful macros.
*/
#define MPT_XSTR(s) MPT_STR(s)
#define MPT_STR(s) #s
#define MPT_STRCMP(value, expected, err) \
(((value != NULL) && (expected != NULL)) ? strcmp(value, expected) : err)
#define MPT_STRLEN(value, err) \
((value != NULL) ? strlen(value) : err)
#define MPT_ProcessArgs mpt__args__, mpt__retval__
#define MPT_ProcessArgsSyntax \
const mpt_data_t *mpt__args__, mpt_retval_t *mpt__retval__
/*
* Name and declaration macros.
*/
#define MPT_ProcessEntryName(process) \
MPT_ProcessEntry__ ## process
#define MPT_TestInitName(suite, test) \
MPT_TestInit__##suite##_##test
#define MPT_TestInitDeclaration(suite, test) \
void MPT_TestInitName(suite, test)(void)
#define MPT_TestFiniName(suite, test) \
MPT_TestFini__##suite##_##test
#define MPT_TestFiniDeclaration(suite, test) \
void MPT_TestFiniName(suite, test)(void)
#define MPT_TestProcessName(suite, test, name) \
MPT_TestProcess__##suite##_##test##_##name
#define MPT_TestProcessDeclaration(suite, test, name) \
void MPT_TestProcessName(suite, test, name) (MPT_ProcessArgsSyntax)
/*
* MPT Assert impl.
*/
typedef enum {
MPT_SUCCESS = 0,
MPT_FAILURE
} mpt_retval_t;
#define MPT_FATAL_YES 1
#define MPT_FATAL_NO 0
#ifdef _WIN32
/* Microsoft Visual Studio does not expand __VA_ARGS__ correctly. */
#define MPT__ASSERT__(...) MPT__ASSERT____((__VA_ARGS__))
#define MPT__ASSERT____(tuple) MPT__ASSERT___ tuple
#else
#define MPT__ASSERT__(...) MPT__ASSERT___(__VA_ARGS__)
#endif /* _WIN32 */
#define MPT__ASSERT___(cond, fatal, ...) \
do { \
(void)mpt__args__; /* Satisfy compiler. */ \
if (!(cond)) { \
if (*mpt__retval__ == MPT_SUCCESS) { \
*mpt__retval__ = MPT_FAILURE; \
} \
printf("MPT_FAIL(%s, %d):\n", __FILE__, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
if (fatal == MPT_FATAL_YES) { \
return; \
} \
} \
} while(0)
/*
* MPT Fixture impl.
*/
struct mpt_env_;
typedef void(*mpt_init_func_t)(void);
typedef void(*mpt_fini_func_t)(void);
typedef struct {
/* Test and process fixtures. */
mpt_init_func_t init;
mpt_fini_func_t fini;
struct mpt_env_ *environment;
/* Test fixtures. */
bool disabled;
int timeout;
bool xfail;
/* IPC information. */
int todo;
} mpt_data_t;
/* Microsoft Visual Studio does not like empty struct initializers, i.e.
no fixtures are specified. To work around that issue MPT_Fixture inserts a
NULL initializer as fall back. */
#define MPT_Comma() ,
#define MPT_Reduce(one, ...) one
#ifdef _WIN32
/* Microsoft Visual Studio does not expand __VA_ARGS__ correctly. */
#define MPT_Fixture__(...) MPT_Fixture____((__VA_ARGS__))
#define MPT_Fixture____(tuple) MPT_Fixture___ tuple
#else
#define MPT_Fixture__(...) MPT_Fixture___(__VA_ARGS__)
#endif /* _WIN32 */
#define MPT_Fixture___(throw, away, value, ...) value
#define MPT_Fixture_(throwaway, ...) \
MPT_Fixture__(throwaway, ((mpt_data_t){ 0 }), ((mpt_data_t){ __VA_ARGS__ }))
#define MPT_Fixture(...) \
MPT_Fixture_( MPT_Comma MPT_Reduce(__VA_ARGS__,) (), __VA_ARGS__ )
/*
* MPT Support functions.
*/
void mpt_export_env(const struct mpt_env_ *env);
void mpt_ipc_send(MPT_ProcessArgsSyntax, const char *str);
void mpt_ipc_wait(MPT_ProcessArgsSyntax, const char *str);
#endif /* MPT_PRIVATE_H_INCLUDED */

View file

@ -0,0 +1,7 @@
#ifndef MPT_RESOURCE_H_INCLUDED
#define MPT_RESOURCE_H_INCLUDED
#define MPT_SOURCE_ROOT_DIR "@MPT_SOURCE_ROOT_DIR@"
#define MPT_DEFAULT_TIMEOUT (@MPT_DEFAULT_TIMEOUT@) /* Seconds. */
#endif /* MPT_RESOURCE_H_INCLUDED */

549
src/mpt/mpt/src/main.c.in Normal file
View file

@ -0,0 +1,549 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "mpt/mpt.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/environ.h"
#ifndef _WIN32
#include <sysexits.h>
#else
#define EX_USAGE (64)
#define EX_SOFTWARE (70)
#endif /* _WIN32 */
/************************************************
* Support functions.
************************************************/
int
mpt_patmatch(const char *pat, const char *str)
{
while (*pat) {
if (*pat == '?') {
/* any character will do */
if (*str++ == 0) {
return 0;
}
pat++;
} else if (*pat == '*') {
/* collapse a sequence of wildcards, requiring as many
characters in str as there are ?s in the sequence */
while (*pat == '*' || *pat == '?') {
if (*pat == '?' && *str++ == 0) {
return 0;
}
pat++;
}
/* try matching on all positions where str matches pat */
while (*str) {
if (*str == *pat && mpt_patmatch(pat+1, str+1)) {
return 1;
}
str++;
}
return *pat == 0;
} else {
/* only an exact match */
if (*str++ != *pat++) {
return 0;
}
}
}
return *str == 0;
}
void
mpt_export_env(const mpt_env_t *env)
{
if (env) {
while ((env->name != NULL) && (env->value != NULL)) {
char *expanded = ddsrt_expand_envvars(env->value);
ddsrt_setenv(env->name, expanded);
ddsrt_free(expanded);
env++;
}
}
}
void
mpt_ipc_send(MPT_ProcessArgsSyntax, const char *str)
{
(void)str;
(void)mpt__args__;
/* TODO: implement. */
MPT_ASSERT(0, "mpt_ipc_send not implemented");
}
void
mpt_ipc_wait(MPT_ProcessArgsSyntax, const char *str)
{
(void)str;
(void)mpt__args__;
/* TODO: implement. */
MPT_ASSERT(0, "mpt_ipc_wait not implemented");
}
/************************************************
* Processes.
************************************************/
@procdecls@
typedef void(*mpt_func_proc_t)(
const mpt_data_t *mpt__args__, mpt_retval_t *mpt__retval__);
typedef struct mpt_process_ {
const char* name;
ddsrt_pid_t pid;
mpt_func_proc_t process;
struct mpt_process_ *next;
} mpt_process_t;
static mpt_process_t*
mpt_process_new(const char* name, mpt_func_proc_t process)
{
mpt_process_t *proc;
proc = ddsrt_malloc(sizeof(mpt_process_t));
proc->pid = 0;
proc->name = name;
proc->process = process;
proc->next = NULL;
return proc;
}
static void
mpt_process_freelist(mpt_process_t *proc)
{
if (proc) {
mpt_process_freelist(proc->next);
if (proc->pid != 0) {
printf("Process %s kill(%d)\n", proc->name, (int)proc->pid);
ddsrt_proc_kill(proc->pid);
}
ddsrt_free(proc);
}
}
/************************************************
* Tests.
************************************************/
typedef struct mpt_test_ {
const char* name;
bool xfail;
dds_duration_t timeout;
mpt_process_t *procs;
struct mpt_test_ *next;
} mpt_test_t;
static mpt_test_t*
mpt_test_new(const char* name, int secs, bool xf)
{
mpt_test_t *test;
test = ddsrt_malloc(sizeof(mpt_test_t));
test->procs = NULL;
test->name = name;
test->xfail = xf;
/* This test will stop after a given timeout. However, when running in
* ctest, we'd like to use the ctest provided timeout functionality.
* So, make sure that the 'manual' timeout takes longer than the ctest
* timeout. */
if (secs == 0) {
secs = MPT_DEFAULT_TIMEOUT;
}
test->timeout = DDS_SECS(secs + 5);
return test;
}
static void
mpt_test_freelist(mpt_test_t *test)
{
if (test) {
mpt_process_freelist(test->procs);
mpt_test_freelist(test->next);
}
ddsrt_free(test);
}
static void
mpt_test_add_process(mpt_test_t *test, mpt_process_t *proc)
{
/* Prepend */
proc->next = test->procs;
test->procs = proc;
}
/************************************************
* Suites.
************************************************/
typedef struct mpt_suite_ {
const char* name;
mpt_test_t *tests;
struct mpt_suite_ *next;
} mpt_suite_t;
static mpt_suite_t*
mpt_suite_new(const char* name)
{
mpt_suite_t *suite;
suite = ddsrt_malloc(sizeof(mpt_suite_t));
suite->tests = NULL;
suite->name = name;
return suite;
}
static void
mpt_suite_freelist(mpt_suite_t *suite)
{
if (suite) {
mpt_test_freelist(suite->tests);
mpt_suite_freelist(suite->next);
}
ddsrt_free(suite);
}
static void
mpt_suite_add_test(mpt_suite_t *suite, mpt_test_t *test)
{
/* Prepend */
test->next = suite->tests;
suite->tests = test;
}
static mpt_test_t*
mpt_suite_find_test(
mpt_suite_t *suite, const char *tname)
{
mpt_test_t *found = suite->tests;
while (found) {
if (mpt_patmatch(tname, found->name)) {
break;
}
found = found->next;
}
return found;
}
/************************************************
* Root.
************************************************/
mpt_suite_t *root = NULL;
static void
mpt_add_suite(mpt_suite_t *suite)
{
/* Prepend */
suite->next = root;
root = suite;
}
static mpt_suite_t*
mpt_find_suite(const char *sname)
{
mpt_suite_t *found = root;
while (found) {
if (mpt_patmatch(sname, found->name)) {
break;
}
found = found->next;
}
return found;
}
static void
mpt_add_test(const char *sname, mpt_test_t *test)
{
mpt_suite_t *suite = mpt_find_suite(sname);
assert(suite);
mpt_suite_add_test(suite, test);
}
static void
mpt_add_process(const char *sname, const char *tname, mpt_process_t *proc)
{
mpt_suite_t *suite = mpt_find_suite(sname);
mpt_test_t *test = mpt_suite_find_test(suite, tname);
assert(suite);
assert(test);
mpt_test_add_process(test, proc);
}
static void
mpt_free(void)
{
mpt_suite_freelist(root);
root = NULL;
}
/************************************************
* Runner.
************************************************/
static int
mpt_run_test(const char *exe, mpt_suite_t *suite, mpt_test_t *test)
{
int result = EXIT_SUCCESS;
mpt_process_t *proc;
dds_retcode_t retcode;
char *argv[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
argv[0] = "-s";
argv[1] = (char*)suite->name;
argv[2] = "-t";
argv[3] = (char*)test->name;
/* Start the processes. */
proc = test->procs;
while ((proc) && (result == EXIT_SUCCESS)) {
if (proc == test->procs) {
printf("\n\n");
printf("=====================================================\n");
printf("Suite: %s\n", suite->name);
printf("Test: %s\n", test->name);
printf("=====================================================\n");
}
argv[4] = "-p";
argv[5] = (char*)proc->name;
retcode = ddsrt_proc_create(exe, argv, &proc->pid);
if (retcode != DDS_RETCODE_OK) {
printf("Start %s::%s::%s failed\n", suite->name, test->name, proc->name);
proc->pid = 0;
result = EXIT_FAILURE;
}
proc = proc->next;
}
/* Wait for the processes. */
retcode = DDS_RETCODE_OK;
while ((result == EXIT_SUCCESS) && (retcode == DDS_RETCODE_OK)) {
int32_t status;
ddsrt_pid_t pid;
/* A second/third/etc wait will restart the timeout.
* This means that the end timeout can take longer than the requested
* test timeout. However, that's good enough for our purpose. */
retcode = ddsrt_proc_waitpids(test->timeout, &pid, &status);
if (retcode == DDS_RETCODE_OK) {
proc = test->procs;
while (proc) {
if (proc->pid == pid) {
break;
}
proc = proc->next;
}
if (proc) {
proc->pid = 0;
if (status != 0) {
printf("Process %s::%s::%s failed (%d)\n", suite->name, test->name, proc->name, status);
result = EXIT_FAILURE;
}
} else {
printf("Wait for processes of %s::%s return unknown pid %d\n", suite->name, test->name, (int)pid);
result = EXIT_FAILURE;
}
} else if (retcode != DDS_RETCODE_NOT_FOUND) {
printf("Wait for processes of %s::%s failed (%d)\n", suite->name, test->name, (int)retcode);
result = EXIT_FAILURE;
}
}
/* Be sure to kill all remaining processes when needed. */
if (result != EXIT_SUCCESS) {
proc = test->procs;
while (proc) {
if (proc->pid != 0) {
printf("Process %s::%s::%s kill(%d)\n", suite->name, test->name, proc->name, (int)proc->pid);
ddsrt_proc_kill(proc->pid);
ddsrt_proc_waitpid(proc->pid, DDS_SECS(10), NULL);
proc->pid = 0;
}
proc = proc->next;
}
}
/* Revert result when we expect the test to have failed. */
if (test->xfail) {
result = ((result == EXIT_SUCCESS) ? EXIT_FAILURE : EXIT_SUCCESS);
}
return result;
}
static int
mpt_run_tests(const char *exe, const char *spattern, const char *tpattern)
{
int result = EXIT_SUCCESS;
mpt_suite_t *suite = root;
while (suite) {
if (mpt_patmatch(spattern, suite->name)) {
mpt_test_t *test = suite->tests;
while (test) {
if (mpt_patmatch(tpattern, test->name)) {
int run = mpt_run_test(exe, suite, test);
if (run != EXIT_SUCCESS) {
result = run;
}
}
test = test->next;
}
}
suite = suite->next;
}
return result;
}
static int
mpt_run_proc(mpt_process_t *proc)
{
mpt_retval_t retval = MPT_SUCCESS;
mpt_data_t args;
proc->process(&args, &retval);
return (retval == MPT_SUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE;
}
static int
mpt_run_procs(const char *spattern, const char *tpattern, const char *ppattern)
{
int result = EXIT_SUCCESS;
mpt_suite_t *suite = root;
while (suite) {
if (mpt_patmatch(spattern, suite->name)) {
mpt_test_t *test = suite->tests;
while (test) {
if (mpt_patmatch(tpattern, test->name)) {
mpt_process_t *proc = test->procs;
while (proc) {
if (mpt_patmatch(ppattern, proc->name)) {
int run = mpt_run_proc(proc);
if (run != EXIT_SUCCESS) {
result = run;
}
}
proc = proc->next;
}
}
test = test->next;
}
}
suite = suite->next;
}
return result;
}
/************************************************
* Main functionality.
************************************************/
static struct {
bool print_help;
const char *suite;
const char *test;
const char *process;
} opts = {
false,
"*",
"*",
NULL
};
static int parse_options(int argc, char *argv[])
{
int err = 0;
for (int i = 1; err == 0 && i < argc; i++) {
switch ((argv[i][0] == '-') ? argv[i][1] : 0) {
case 'h':
opts.print_help = true;
break;
case 's':
if ((i+1) < argc) {
opts.suite = argv[++i];
break;
}
/* FALLS THROUGH */
case 't':
if ((i+1) < argc) {
opts.test = argv[++i];
break;
}
/* FALLS THROUGH */
case 'p':
if ((i+1) < argc) {
opts.process = argv[++i];
break;
}
/* FALLS THROUGH */
default:
err = 1;
break;
}
}
return err;
}
static void usage(const char *name)
{
fprintf(stderr, "Usage: %s OPTIONS\n", name);
fprintf(stderr, "Try '%s -h' for more information\n", name);
}
static void help(const char *name)
{
printf("Usage: %s [OPTIONS]\n", name);
printf("\n");
printf("Possible options:\n");
printf(" -h display this help and exit\n");
printf(" -s PATTERN run only tests in suites matching pattern\n");
printf(" -t PATTERN run only test matching pattern\n");
printf(" -p PROCESS run only process matching pattern\n");
printf("\n");
printf("Exit codes:\n");
printf(" %-2d Successful termination\n", EXIT_SUCCESS);
printf(" %-2d One or more failing test cases\n", EXIT_FAILURE);
printf(" %-2d Command line usage error\n", EX_USAGE);
printf(" %-2d Internal software error\n", EX_SOFTWARE);
}
int main(int argc, char *argv[])
{
int result = EXIT_SUCCESS;
if (parse_options(argc, argv) != 0) {
usage(argv[0]);
return EX_USAGE;
} else if (opts.print_help) {
help(argv[0]);
return result;
}
atexit(mpt_free);
@addsuites@
@addtests@
@addprocs@
if (opts.process == NULL) {
/* Run test(s). */
result = mpt_run_tests(argv[0], opts.suite, opts.test);
} else {
/* Run process(es). */
result = mpt_run_procs(opts.suite, opts.test, opts.process);
}
return result;
}

View file

@ -0,0 +1,19 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
cmake_minimum_required(VERSION 3.7)
if(MPT_ENABLE_SELFTEST)
add_subdirectory(self)
endif()
add_subdirectory(basic)

View file

@ -0,0 +1,23 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
include(${MPT_CMAKE})
set(sources
"procs/hello.c"
"helloworld.c"
"multi.c")
add_mpt_executable(mpt_basic ${sources})
idlc_generate(mpt_basic_helloworlddata_lib "procs/helloworlddata.idl")
target_link_libraries(mpt_basic PRIVATE mpt_basic_helloworlddata_lib)

View file

@ -0,0 +1,30 @@
<!--
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
v. 1.0 which is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
-->
<CycloneDDS>
<Domain>
<Id>any</Id>
</Domain>
<DDSI2E>
<General>
<NetworkInterfaceAddress>auto</NetworkInterfaceAddress>
<AllowMulticast>true</AllowMulticast>
<EnableMulticastLoopback>true</EnableMulticastLoopback>
</General>
<Compatibility>
<StandardsConformance>lax</StandardsConformance>
</Compatibility>
<!--Tracing>
<Verbosity>finest</Verbosity>
<OutputFile>ddsi_${MPT_PROCESS_NAME}.log</OutputFile>
</Tracing-->
</DDSI2E>
</CycloneDDS>

View file

@ -0,0 +1,30 @@
<!--
Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
v. 1.0 which is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
-->
<CycloneDDS>
<Domain>
<Id>${DOMAIN_ID}</Id>
</Domain>
<DDSI2E>
<General>
<NetworkInterfaceAddress>auto</NetworkInterfaceAddress>
<AllowMulticast>true</AllowMulticast>
<EnableMulticastLoopback>true</EnableMulticastLoopback>
</General>
<Compatibility>
<StandardsConformance>lax</StandardsConformance>
</Compatibility>
<!--Tracing>
<Verbosity>finest</Verbosity>
<OutputFile>ddsi_${MPT_PROCESS_NAME}.log</OutputFile>
</Tracing-->
</DDSI2E>
</CycloneDDS>

View file

@ -0,0 +1,63 @@
#include "dds/dds.h"
#include "mpt/mpt.h"
#include "mpt/resource.h" /* MPT_SOURCE_ROOT_DIR */
#include "procs/hello.h" /* publisher and subscriber entry points. */
/*
* Tests to check simple communication between a publisher and subscriber.
* The published text should be received by the subscriber.
*/
/* Environments */
static mpt_env_t environment_any[] = {
{ "ETC_DIR", MPT_SOURCE_ROOT_DIR"/tests/basic/etc" },
{ "CYCLONEDDS_URI", "file://${ETC_DIR}/config_any.xml" },
{ NULL, NULL }
};
static mpt_env_t environment_42[] = {
{ "ETC_DIR", MPT_SOURCE_ROOT_DIR"/tests/basic/etc" },
{ "DOMAIN_ID", "42" },
{ "CYCLONEDDS_URI", "file://${ETC_DIR}/config_specific.xml" },
{ NULL, NULL }
};
/**********************************************************************
* No CYCLONEDDS_URI set.
**********************************************************************/
#define TEST_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "hello_def", 1, "No environment set")
MPT_TestProcess(helloworld, default, pub, hello_publisher, TEST_ARGS);
MPT_TestProcess(helloworld, default, sub, hello_subscriber, TEST_ARGS);
MPT_Test(helloworld, default, .init=hello_init, .fini=hello_fini);
#undef TEST_ARGS
/**********************************************************************
* Config domain is any. Test domain is default.
**********************************************************************/
#define TEST_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "hello_any", 1, "Some nice text over any domain")
MPT_TestProcess(helloworld, domain_any, pub, hello_publisher, TEST_ARGS);
MPT_TestProcess(helloworld, domain_any, sub, hello_subscriber, TEST_ARGS);
MPT_Test(helloworld, domain_any, .init=hello_init, .fini=hello_fini, .environment=environment_any);
#undef TEST_ARGS
/**********************************************************************
* Pub: Config domain is any. Test domain is 42.
* Sub: Config domain is 42 (through DOMAIN_ID env). Test domain is default.
**********************************************************************/
#define TEST_PUB_ARGS MPT_ArgValues(42, "hello_42", 1, "Now domain 42 is used")
#define TEST_SUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "hello_42", 1, "Now domain 42 is used")
MPT_TestProcess(helloworld, domain_42, pub, hello_publisher, TEST_PUB_ARGS, .environment=environment_any);
MPT_TestProcess(helloworld, domain_42, sub, hello_subscriber, TEST_SUB_ARGS, .environment=environment_42);
MPT_Test(helloworld, domain_42, .init=hello_init, .fini=hello_fini);
#undef TEST_SUB_ARGS
#undef TEST_PUB_ARGS

View file

@ -0,0 +1,50 @@
#include "mpt/mpt.h"
#include "procs/hello.h"
/*
* Tests to check communication between multiple publisher(s) and subscriber(s).
*/
/*
* The publisher expects 2 publication matched.
* The subscribers expect 1 sample each.
*/
#define TEST_PUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "multi_pubsubsub", 2, "pubsubsub")
#define TEST_SUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "multi_pubsubsub", 1, "pubsubsub")
MPT_TestProcess(multi, pubsubsub, pub, hello_publisher, TEST_PUB_ARGS);
MPT_TestProcess(multi, pubsubsub, sub1, hello_subscriber, TEST_SUB_ARGS);
MPT_TestProcess(multi, pubsubsub, sub2, hello_subscriber, TEST_SUB_ARGS);
MPT_Test(multi, pubsubsub, .init=hello_init, .fini=hello_fini);
#undef TEST_SUB_ARGS
#undef TEST_PUB_ARGS
/*
* The publishers expect 1 publication matched each.
* The subscriber expects 2 samples.
*/
#define TEST_PUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "multi_pubpubsub", 1, "pubpubsub")
#define TEST_SUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "multi_pubpubsub", 2, "pubpubsub")
MPT_TestProcess(multi, pubpubsub, pub1, hello_publisher, TEST_PUB_ARGS);
MPT_TestProcess(multi, pubpubsub, pub2, hello_publisher, TEST_PUB_ARGS);
MPT_TestProcess(multi, pubpubsub, sub, hello_subscriber, TEST_SUB_ARGS);
MPT_Test(multi, pubpubsub, .init=hello_init, .fini=hello_fini);
#undef TEST_SUB_ARGS
#undef TEST_PUB_ARGS
/*
* The publishers expect 2 publication matched each.
* The subscribers expect 2 samples each.
*/
#define TEST_PUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "multi_pubpubsubsub", 2, "pubpubsubsub")
#define TEST_SUB_ARGS MPT_ArgValues(DDS_DOMAIN_DEFAULT, "multi_pubpubsubsub", 2, "pubpubsubsub")
MPT_TestProcess(multi, pubpubsubsub, pub1, hello_publisher, TEST_PUB_ARGS);
MPT_TestProcess(multi, pubpubsubsub, pub2, hello_publisher, TEST_PUB_ARGS);
MPT_TestProcess(multi, pubpubsubsub, sub1, hello_subscriber, TEST_SUB_ARGS);
MPT_TestProcess(multi, pubpubsubsub, sub2, hello_subscriber, TEST_SUB_ARGS);
MPT_Test(multi, pubpubsubsub, .init=hello_init, .fini=hello_fini);
#undef TEST_SUB_ARGS
#undef TEST_PUB_ARGS

View file

@ -0,0 +1,239 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "mpt/mpt.h"
#include "dds/dds.h"
#include "helloworlddata.h"
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/strtol.h"
#include "dds/ddsrt/process.h"
#include "dds/ddsrt/environ.h"
#include "dds/ddsrt/cdtors.h"
#include "dds/ddsrt/sync.h"
/* An array of one message (aka sample in dds terms) will be used. */
#define MAX_SAMPLES 1
static int g_publication_matched_count = 0;
static ddsrt_mutex_t g_mutex;
static ddsrt_cond_t g_cond;
static void
publication_matched_cb(
dds_entity_t writer,
const dds_publication_matched_status_t status,
void* arg)
{
(void)arg;
(void)writer;
ddsrt_mutex_lock(&g_mutex);
g_publication_matched_count = (int)status.current_count;
ddsrt_cond_broadcast(&g_cond);
ddsrt_mutex_unlock(&g_mutex);
}
static void
data_available_cb(
dds_entity_t reader,
void* arg)
{
(void)arg;
(void)reader;
ddsrt_mutex_lock(&g_mutex);
ddsrt_cond_broadcast(&g_cond);
ddsrt_mutex_unlock(&g_mutex);
}
void
hello_init(void)
{
ddsrt_init();
ddsrt_mutex_init(&g_mutex);
ddsrt_cond_init(&g_cond);
}
void
hello_fini(void)
{
ddsrt_cond_destroy(&g_cond);
ddsrt_mutex_destroy(&g_mutex);
ddsrt_fini();
}
/*
* The HelloWorld publisher.
* It waits for a publication matched, and then writes a sample.
* It quits when the publication matched has been reset again.
*/
MPT_ProcessEntry(hello_publisher,
MPT_Args(dds_domainid_t domainid,
const char *topic_name,
int sub_cnt,
const char *text))
{
HelloWorldData_Msg msg;
dds_listener_t *listener;
dds_entity_t participant;
dds_entity_t topic;
dds_entity_t writer;
dds_return_t rc;
dds_qos_t *qos;
int id = (int)ddsrt_getpid();
assert(topic_name);
assert(text);
printf("=== [Publisher(%d)] Start(%d) ...\n", id, domainid);
/*
* A reliable volatile sample, written after publication matched, can still
* be lost when the subscriber wasn't able to match its subscription yet.
* Use transient_local reliable to make sure the sample is received.
*/
qos = dds_create_qos();
dds_qset_durability(qos, DDS_DURABILITY_TRANSIENT_LOCAL);
dds_qset_reliability(qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
/* Use listener to get number of publications matched. */
listener = dds_create_listener(NULL);
MPT_ASSERT_FATAL_NOT_NULL(listener, "Could not create listener");
dds_lset_publication_matched(listener, publication_matched_cb);
/* Create a Writer. */
participant = dds_create_participant (domainid, NULL, NULL);
MPT_ASSERT_FATAL_GT(participant, 0, "Could not create participant: %s\n", dds_strretcode(-participant));
topic = dds_create_topic (
participant, &HelloWorldData_Msg_desc, topic_name, qos, NULL);
MPT_ASSERT_FATAL_GT(topic, 0, "Could not create topic: %s\n", dds_strretcode(-topic));
writer = dds_create_writer (participant, topic, qos, listener);
MPT_ASSERT_FATAL_GT(writer, 0, "Could not create writer: %s\n", dds_strretcode(-writer));
/* Wait for expected nr of subscriber(s). */
ddsrt_mutex_lock(&g_mutex);
while (g_publication_matched_count != sub_cnt) {
ddsrt_cond_waitfor(&g_cond, &g_mutex, DDS_INFINITY);
}
ddsrt_mutex_unlock(&g_mutex);
/* Write sample. */
msg.userID = (int32_t)id;
msg.message = (char*)text;
printf("=== [Publisher(%d)] Send: { %d, %s }\n", id, msg.userID, msg.message);
rc = dds_write (writer, &msg);
MPT_ASSERT_EQ(rc, DDS_RETCODE_OK, "Could not write sample\n");
/* Wait for subscriber(s) to have finished. */
ddsrt_mutex_lock(&g_mutex);
while (g_publication_matched_count != 0) {
ddsrt_cond_waitfor(&g_cond, &g_mutex, DDS_INFINITY);
}
ddsrt_mutex_unlock(&g_mutex);
rc = dds_delete (participant);
MPT_ASSERT_EQ(rc, DDS_RETCODE_OK, "Teardown failed\n");
dds_delete_listener(listener);
dds_delete_qos(qos);
printf("=== [Publisher(%d)] Done\n", id);
}
/*
* The HelloWorld subscriber.
* It waits for sample(s) and checks the content.
*/
MPT_ProcessEntry(hello_subscriber,
MPT_Args(dds_domainid_t domainid,
const char *topic_name,
int sample_cnt,
const char *text))
{
HelloWorldData_Msg *msg;
void *samples[MAX_SAMPLES];
dds_sample_info_t infos[MAX_SAMPLES];
dds_listener_t *listener;
dds_entity_t participant;
dds_entity_t topic;
dds_entity_t reader;
dds_return_t rc;
dds_qos_t *qos;
int recv_cnt;
int id = (int)ddsrt_getpid();
assert(topic_name);
assert(text);
printf("--- [Subscriber(%d)] Start(%d) ...\n", id, domainid);
/*
* A reliable volatile sample, written after publication matched, can still
* be lost when the subscriber wasn't able to match its subscription yet.
* Use transient_local reliable to make sure the sample is received.
*/
qos = dds_create_qos();
dds_qset_durability(qos, DDS_DURABILITY_TRANSIENT_LOCAL);
dds_qset_reliability(qos, DDS_RELIABILITY_RELIABLE, DDS_SECS(10));
/* Use listener to get data available trigger. */
listener = dds_create_listener(NULL);
MPT_ASSERT_FATAL_NOT_NULL(listener, "Could not create listener");
dds_lset_data_available(listener, data_available_cb);
/* Create a Reader. */
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
MPT_ASSERT_FATAL_GT(participant, 0, "Could not create participant: %s\n", dds_strretcode(-participant));
topic = dds_create_topic (
participant, &HelloWorldData_Msg_desc, topic_name, qos, NULL);
MPT_ASSERT_FATAL_GT(topic, 0, "Could not create topic: %s\n", dds_strretcode(-topic));
reader = dds_create_reader (participant, topic, qos, listener);
MPT_ASSERT_FATAL_GT(reader, 0, "Could not create reader: %s\n", dds_strretcode(-reader));
printf("--- [Subscriber(%d)] Waiting for %d sample(s) ...\n", id, sample_cnt);
/* Initialize sample buffer, by pointing the void pointer within
* the buffer array to a valid sample memory location. */
samples[0] = HelloWorldData_Msg__alloc ();
/* Wait until expected nr of samples have been taken. */
ddsrt_mutex_lock(&g_mutex);
recv_cnt = 0;
while (recv_cnt < sample_cnt) {
/* Use a take with mask to work around the #146 issue. */
rc = dds_take_mask(reader, samples, infos, MAX_SAMPLES, MAX_SAMPLES, DDS_NEW_VIEW_STATE);
MPT_ASSERT_GEQ(rc, 0, "Could not read: %s\n", dds_strretcode(-rc));
/* Check if we read some data and it is valid. */
if ((rc > 0) && (infos[0].valid_data)) {
/* Print Message. */
msg = (HelloWorldData_Msg*)samples[0];
printf("--- [Subscriber(%d)] Received: { %d, %s }\n", id,
msg->userID, msg->message);
MPT_ASSERT_STR_EQ(msg->message, text,
"Messages do not match: \"%s\" vs \"%s\"\n",
msg->message, text);
recv_cnt++;
} else {
ddsrt_cond_waitfor(&g_cond, &g_mutex, DDS_INFINITY);
}
}
ddsrt_mutex_unlock(&g_mutex);
/* Free the data location. */
HelloWorldData_Msg_free (samples[0], DDS_FREE_ALL);
rc = dds_delete (participant);
MPT_ASSERT_EQ(rc, DDS_RETCODE_OK, "Teardown failed\n");
dds_delete_listener(listener);
dds_delete_qos(qos);
printf("--- [Subscriber(%d)] Done\n", id);
}

View file

@ -0,0 +1,52 @@
/*
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
/** @file
*
* @brief DDS C Communication Status API
*
* This header file defines the public API of the Communication Status in the
* Eclipse Cyclone DDS C language binding.
*/
#ifndef MPT_BASIC_PROCS_HELLO_H
#define MPT_BASIC_PROCS_HELLO_H
#include <stdio.h>
#include <string.h>
#include "dds/dds.h"
#include "mpt/mpt.h"
#if defined (__cplusplus)
extern "C" {
#endif
void hello_init(void);
void hello_fini(void);
MPT_ProcessEntry(hello_publisher,
MPT_Args(dds_domainid_t domainid,
const char *topic_name,
int sub_cnt,
const char *text));
MPT_ProcessEntry(hello_subscriber,
MPT_Args(dds_domainid_t domainid,
const char *topic_name,
int sample_cnt,
const char *text));
#if defined (__cplusplus)
}
#endif
#endif /* MPT_BASIC_PROCS_HELLO_H */

View file

@ -0,0 +1,9 @@
module HelloWorldData
{
struct Msg
{
long userID;
string message;
};
#pragma keylist Msg userID
};

View file

@ -0,0 +1,25 @@
#
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
include(${MPT_CMAKE})
message(STATUS "MPT selftest enabled: some test will fail deliberately")
set(sources
"asserts.c"
"environments.c"
"fixtures.c"
"ipc.c"
"resources.c"
"usage.c")
add_mpt_executable(mpt_self ${sources})

View file

@ -0,0 +1,711 @@
#include <stdio.h>
#include <assert.h>
#include "mpt/mpt.h"
#include "dds/ddsrt/time.h"
static int dummy;
/************************************************************
* Test MPT_ASSERT
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT, MPT_Args(const char *exp, int cond))
{
assert(exp);
MPT_ASSERT(cond, "MPT_ASSERT(%d), 1st, expect: %s", cond, exp);
MPT_ASSERT(cond, "MPT_ASSERT(%d), 2nd, expect: %s", cond, exp);
}
MPT_TestProcess(MPT_ASSERT, pass, id, proc_MPT_ASSERT, MPT_ArgValues("PASS", 1));
MPT_TestProcess(MPT_ASSERT, fail, id, proc_MPT_ASSERT, MPT_ArgValues("FAIL", 0));
MPT_Test(MPT_ASSERT, pass, .xfail=false);
MPT_Test(MPT_ASSERT, fail, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FAIL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FAIL, MPT_NoArgs())
{
MPT_ASSERT_FAIL("MPT_ASSERT_FAIL(), 1st, expect a fail, always");
MPT_ASSERT_FAIL("MPT_ASSERT_FAIL(), 2nd, expect a fail, always");
}
MPT_TestProcess(MPT_ASSERT_FAIL, call, id, proc_MPT_ASSERT_FAIL, MPT_NoArgValues());
MPT_Test(MPT_ASSERT_FAIL, call, .xfail=true);
/************************************************************
* Test MPT_ASSERT_EQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_EQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_EQ(val1, val2, "MPT_ASSERT_EQ(%d, %d), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_EQ(val1, val2, "MPT_ASSERT_EQ(%d, %d), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_EQ_int, eq, id, proc_MPT_ASSERT_EQ_int, MPT_ArgValues("PASS", 1, 1));
MPT_TestProcess(MPT_ASSERT_EQ_int, lt, id, proc_MPT_ASSERT_EQ_int, MPT_ArgValues("FAIL", 1, 2));
MPT_TestProcess(MPT_ASSERT_EQ_int, gt, id, proc_MPT_ASSERT_EQ_int, MPT_ArgValues("FAIL", 3, 2));
MPT_Test(MPT_ASSERT_EQ_int, eq, .xfail=false);
MPT_Test(MPT_ASSERT_EQ_int, lt, .xfail=true);
MPT_Test(MPT_ASSERT_EQ_int, gt, .xfail=true);
MPT_ProcessEntry(proc_MPT_ASSERT_EQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_EQ(val1, val2, "MPT_ASSERT_EQ(%f, %f), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_EQ(val1, val2, "MPT_ASSERT_EQ(%f, %f), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_EQ_double, eq, id, proc_MPT_ASSERT_EQ_double, MPT_ArgValues("PASS", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_EQ_double, lt, id, proc_MPT_ASSERT_EQ_double, MPT_ArgValues("FAIL", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_EQ_double, gt, id, proc_MPT_ASSERT_EQ_double, MPT_ArgValues("FAIL", 1.3, 1.2));
MPT_Test(MPT_ASSERT_EQ_double, eq, .xfail=false);
MPT_Test(MPT_ASSERT_EQ_double, lt, .xfail=true);
MPT_Test(MPT_ASSERT_EQ_double, gt, .xfail=true);
/************************************************************
* Test MPT_ASSERT_NEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_NEQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_NEQ(val1, val2, "MPT_ASSERT_NEQ(%d, %d), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_NEQ(val1, val2, "MPT_ASSERT_NEQ(%d, %d), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_NEQ_int, eq, id, proc_MPT_ASSERT_NEQ_int, MPT_ArgValues("FAIL", 1, 1));
MPT_TestProcess(MPT_ASSERT_NEQ_int, lt, id, proc_MPT_ASSERT_NEQ_int, MPT_ArgValues("PASS", 1, 2));
MPT_TestProcess(MPT_ASSERT_NEQ_int, gt, id, proc_MPT_ASSERT_NEQ_int, MPT_ArgValues("PASS", 3, 2));
MPT_Test(MPT_ASSERT_NEQ_int, eq, .xfail=true);
MPT_Test(MPT_ASSERT_NEQ_int, lt, .xfail=false);
MPT_Test(MPT_ASSERT_NEQ_int, gt, .xfail=false);
MPT_ProcessEntry(proc_MPT_ASSERT_NEQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_NEQ(val1, val2, "MPT_ASSERT_NEQ(%f, %f), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_NEQ(val1, val2, "MPT_ASSERT_NEQ(%f, %f), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_NEQ_double, eq, id, proc_MPT_ASSERT_NEQ_double, MPT_ArgValues("FAIL", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_NEQ_double, lt, id, proc_MPT_ASSERT_NEQ_double, MPT_ArgValues("PASS", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_NEQ_double, gt, id, proc_MPT_ASSERT_NEQ_double, MPT_ArgValues("PASS", 1.3, 1.2));
MPT_Test(MPT_ASSERT_NEQ_double, eq, .xfail=true);
MPT_Test(MPT_ASSERT_NEQ_double, lt, .xfail=false);
MPT_Test(MPT_ASSERT_NEQ_double, gt, .xfail=false);
/************************************************************
* Test MPT_ASSERT_LEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_LEQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_LEQ(val1, val2, "MPT_ASSERT_LEQ(%d, %d), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_LEQ(val1, val2, "MPT_ASSERT_LEQ(%d, %d), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_LEQ_int, eq, id, proc_MPT_ASSERT_LEQ_int, MPT_ArgValues("PASS", 1, 1));
MPT_TestProcess(MPT_ASSERT_LEQ_int, lt, id, proc_MPT_ASSERT_LEQ_int, MPT_ArgValues("PASS", 1, 2));
MPT_TestProcess(MPT_ASSERT_LEQ_int, gt, id, proc_MPT_ASSERT_LEQ_int, MPT_ArgValues("FAIL", 3, 2));
MPT_Test(MPT_ASSERT_LEQ_int, eq, .xfail=false);
MPT_Test(MPT_ASSERT_LEQ_int, lt, .xfail=false);
MPT_Test(MPT_ASSERT_LEQ_int, gt, .xfail=true);
MPT_ProcessEntry(proc_MPT_ASSERT_LEQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_LEQ(val1, val2, "MPT_ASSERT_LEQ(%f, %f), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_LEQ(val1, val2, "MPT_ASSERT_LEQ(%f, %f), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_LEQ_double, eq, id, proc_MPT_ASSERT_LEQ_double, MPT_ArgValues("PASS", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_LEQ_double, lt, id, proc_MPT_ASSERT_LEQ_double, MPT_ArgValues("PASS", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_LEQ_double, gt, id, proc_MPT_ASSERT_LEQ_double, MPT_ArgValues("FAIL", 1.3, 1.2));
MPT_Test(MPT_ASSERT_LEQ_double, eq, .xfail=false);
MPT_Test(MPT_ASSERT_LEQ_double, lt, .xfail=false);
MPT_Test(MPT_ASSERT_LEQ_double, gt, .xfail=true);
/************************************************************
* Test MPT_ASSERT_GEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_GEQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_GEQ(val1, val2, "MPT_ASSERT_GEQ(%d, %d), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_GEQ(val1, val2, "MPT_ASSERT_GEQ(%d, %d), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_GEQ_int, eq, id, proc_MPT_ASSERT_GEQ_int, MPT_ArgValues("PASS", 1, 1));
MPT_TestProcess(MPT_ASSERT_GEQ_int, lt, id, proc_MPT_ASSERT_GEQ_int, MPT_ArgValues("FAIL", 1, 2));
MPT_TestProcess(MPT_ASSERT_GEQ_int, gt, id, proc_MPT_ASSERT_GEQ_int, MPT_ArgValues("PASS", 3, 2));
MPT_Test(MPT_ASSERT_GEQ_int, eq, .xfail=false);
MPT_Test(MPT_ASSERT_GEQ_int, lt, .xfail=true);
MPT_Test(MPT_ASSERT_GEQ_int, gt, .xfail=false);
MPT_ProcessEntry(proc_MPT_ASSERT_GEQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_GEQ(val1, val2, "MPT_ASSERT_GEQ(%f, %f), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_GEQ(val1, val2, "MPT_ASSERT_GEQ(%f, %f), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_GEQ_double, eq, id, proc_MPT_ASSERT_GEQ_double, MPT_ArgValues("PASS", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_GEQ_double, lt, id, proc_MPT_ASSERT_GEQ_double, MPT_ArgValues("FAIL", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_GEQ_double, gt, id, proc_MPT_ASSERT_GEQ_double, MPT_ArgValues("PASS", 1.3, 1.2));
MPT_Test(MPT_ASSERT_GEQ_double, eq, .xfail=false);
MPT_Test(MPT_ASSERT_GEQ_double, lt, .xfail=true);
MPT_Test(MPT_ASSERT_GEQ_double, gt, .xfail=false);
/************************************************************
* Test MPT_ASSERT_LT
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_LT_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_LT(val1, val2, "MPT_ASSERT_LT(%d, %d), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_LT(val1, val2, "MPT_ASSERT_LT(%d, %d), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_LT_int, eq, id, proc_MPT_ASSERT_LT_int, MPT_ArgValues("FAIL", 1, 1));
MPT_TestProcess(MPT_ASSERT_LT_int, lt, id, proc_MPT_ASSERT_LT_int, MPT_ArgValues("PASS", 1, 2));
MPT_TestProcess(MPT_ASSERT_LT_int, gt, id, proc_MPT_ASSERT_LT_int, MPT_ArgValues("FAIL", 3, 2));
MPT_Test(MPT_ASSERT_LT_int, eq, .xfail=true);
MPT_Test(MPT_ASSERT_LT_int, lt, .xfail=false);
MPT_Test(MPT_ASSERT_LT_int, gt, .xfail=true);
MPT_ProcessEntry(proc_MPT_ASSERT_LT_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_LT(val1, val2, "MPT_ASSERT_LT(%f, %f), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_LT(val1, val2, "MPT_ASSERT_LT(%f, %f), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_LT_double, eq, id, proc_MPT_ASSERT_LT_double, MPT_ArgValues("FAIL", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_LT_double, lt, id, proc_MPT_ASSERT_LT_double, MPT_ArgValues("PASS", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_LT_double, gt, id, proc_MPT_ASSERT_LT_double, MPT_ArgValues("FAIL", 1.3, 1.2));
MPT_Test(MPT_ASSERT_LT_double, eq, .xfail=true);
MPT_Test(MPT_ASSERT_LT_double, lt, .xfail=false);
MPT_Test(MPT_ASSERT_LT_double, gt, .xfail=true);
/************************************************************
* Test MPT_ASSERT_GT
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_GT_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_GT(val1, val2, "MPT_ASSERT_GT(%d, %d), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_GT(val1, val2, "MPT_ASSERT_GT(%d, %d), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_GT_int, eq, id, proc_MPT_ASSERT_GT_int, MPT_ArgValues("FAIL", 1, 1));
MPT_TestProcess(MPT_ASSERT_GT_int, lt, id, proc_MPT_ASSERT_GT_int, MPT_ArgValues("FAIL", 1, 2));
MPT_TestProcess(MPT_ASSERT_GT_int, gt, id, proc_MPT_ASSERT_GT_int, MPT_ArgValues("PASS", 3, 2));
MPT_Test(MPT_ASSERT_GT_int, eq, .xfail=true);
MPT_Test(MPT_ASSERT_GT_int, lt, .xfail=true);
MPT_Test(MPT_ASSERT_GT_int, gt, .xfail=false);
MPT_ProcessEntry(proc_MPT_ASSERT_GT_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_GT(val1, val2, "MPT_ASSERT_GT(%f, %f), 1st expect: %s", val1, val2, exp);
MPT_ASSERT_GT(val1, val2, "MPT_ASSERT_GT(%f, %f), 2nd expect: %s", val1, val2, exp);
}
MPT_TestProcess(MPT_ASSERT_GT_double, eq, id, proc_MPT_ASSERT_GT_double, MPT_ArgValues("FAIL", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_GT_double, lt, id, proc_MPT_ASSERT_GT_double, MPT_ArgValues("FAIL", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_GT_double, gt, id, proc_MPT_ASSERT_GT_double, MPT_ArgValues("PASS", 1.3, 1.2));
MPT_Test(MPT_ASSERT_GT_double, eq, .xfail=true);
MPT_Test(MPT_ASSERT_GT_double, lt, .xfail=true);
MPT_Test(MPT_ASSERT_GT_double, gt, .xfail=false);
/************************************************************
* Test MPT_ASSERT_NULL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_NULL, MPT_Args(const char *exp, const void* ptr))
{
assert(exp);
MPT_ASSERT_NULL(ptr, "MPT_ASSERT_NULL(%p), expect: %s", ptr, exp);
}
MPT_TestProcess(MPT_ASSERT_NULL, addr, id, proc_MPT_ASSERT_NULL, MPT_ArgValues("FAIL", &dummy));
MPT_TestProcess(MPT_ASSERT_NULL, null, id, proc_MPT_ASSERT_NULL, MPT_ArgValues("PASS", NULL));
MPT_Test(MPT_ASSERT_NULL, addr, .xfail=true);
MPT_Test(MPT_ASSERT_NULL, null, .xfail=false);
/************************************************************
* Test MPT_ASSERT_NOT_NULL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_NOT_NULL, MPT_Args(const char *exp, const void* ptr))
{
assert(exp);
MPT_ASSERT_NOT_NULL(ptr, "MPT_ASSERT_NOT_NULL(%p), expect: %s", ptr, exp);
}
MPT_TestProcess(MPT_ASSERT_NOT_NULL, addr, id, proc_MPT_ASSERT_NOT_NULL, MPT_ArgValues("PASS", &dummy));
MPT_TestProcess(MPT_ASSERT_NOT_NULL, null, id, proc_MPT_ASSERT_NOT_NULL, MPT_ArgValues("FAIL", NULL));
MPT_Test(MPT_ASSERT_NOT_NULL, addr, .xfail=false);
MPT_Test(MPT_ASSERT_NOT_NULL, null, .xfail=true);
/************************************************************
* Test MPT_ASSERT_STR_EQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_STR_EQ, MPT_Args(const char *exp, const char* val1, const char* val2))
{
assert(exp);
MPT_ASSERT_STR_EQ(val1, val2, "MPT_ASSERT_STR_EQ(%s, %s), expect: %s",
val1 ? val1 : "<null>",
val2 ? val2 : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_STR_EQ, eq, id, proc_MPT_ASSERT_STR_EQ, MPT_ArgValues("PASS", "foo", "foo"));
MPT_TestProcess(MPT_ASSERT_STR_EQ, neq, id, proc_MPT_ASSERT_STR_EQ, MPT_ArgValues("FAIL", "foo", "bar"));
MPT_TestProcess(MPT_ASSERT_STR_EQ, null1, id, proc_MPT_ASSERT_STR_EQ, MPT_ArgValues("FAIL", NULL, "foo"));
MPT_TestProcess(MPT_ASSERT_STR_EQ, null2, id, proc_MPT_ASSERT_STR_EQ, MPT_ArgValues("FAIL", "foo", NULL));
MPT_Test(MPT_ASSERT_STR_EQ, eq, .xfail=false);
MPT_Test(MPT_ASSERT_STR_EQ, neq, .xfail=true);
MPT_Test(MPT_ASSERT_STR_EQ, null1, .xfail=true);
MPT_Test(MPT_ASSERT_STR_EQ, null2, .xfail=true);
/************************************************************
* Test MPT_ASSERT_STR_NEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_STR_NEQ, MPT_Args(const char *exp, const char* val1, const char* val2))
{
assert(exp);
MPT_ASSERT_STR_NEQ(val1, val2, "MPT_ASSERT_STR_NEQ(%s, %s), expect: %s",
val1 ? val1 : "<null>",
val2 ? val2 : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_STR_NEQ, eq, id, proc_MPT_ASSERT_STR_NEQ, MPT_ArgValues("FAIL", "foo", "foo"));
MPT_TestProcess(MPT_ASSERT_STR_NEQ, neq, id, proc_MPT_ASSERT_STR_NEQ, MPT_ArgValues("PASS", "foo", "bar"));
MPT_TestProcess(MPT_ASSERT_STR_NEQ, null1, id, proc_MPT_ASSERT_STR_NEQ, MPT_ArgValues("FAIL", NULL, "foo"));
MPT_TestProcess(MPT_ASSERT_STR_NEQ, null2, id, proc_MPT_ASSERT_STR_NEQ, MPT_ArgValues("FAIL", "foo", NULL));
MPT_Test(MPT_ASSERT_STR_NEQ, eq, .xfail=true);
MPT_Test(MPT_ASSERT_STR_NEQ, neq, .xfail=false);
MPT_Test(MPT_ASSERT_STR_NEQ, null1, .xfail=true);
MPT_Test(MPT_ASSERT_STR_NEQ, null2, .xfail=true);
/************************************************************
* Test MPT_ASSERT_STR_EMPTY
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_STR_EMPTY, MPT_Args(const char *exp, const char* val))
{
assert(exp);
MPT_ASSERT_STR_EMPTY(val, "MPT_ASSERT_STR_EMPTY(%s), expect: %s",
val ? val : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_STR_EMPTY, nempty, id, proc_MPT_ASSERT_STR_EMPTY, MPT_ArgValues("FAIL", "foo"));
MPT_TestProcess(MPT_ASSERT_STR_EMPTY, empty, id, proc_MPT_ASSERT_STR_EMPTY, MPT_ArgValues("PASS", ""));
MPT_TestProcess(MPT_ASSERT_STR_EMPTY, null, id, proc_MPT_ASSERT_STR_EMPTY, MPT_ArgValues("FAIL", NULL));
MPT_Test(MPT_ASSERT_STR_EMPTY, nempty, .xfail=true);
MPT_Test(MPT_ASSERT_STR_EMPTY, empty, .xfail=false);
MPT_Test(MPT_ASSERT_STR_EMPTY, null, .xfail=true);
/************************************************************
* Test MPT_ASSERT_STR_NOT_EMPTY
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_STR_NOT_EMPTY, MPT_Args(const char *exp, const char* val))
{
assert(exp);
MPT_ASSERT_STR_NOT_EMPTY(val, "MPT_ASSERT_STR_NOT_EMPTY(%s), expect: %s",
val ? val : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_STR_NOT_EMPTY, nempty, id, proc_MPT_ASSERT_STR_NOT_EMPTY, MPT_ArgValues("PASS", "foo"));
MPT_TestProcess(MPT_ASSERT_STR_NOT_EMPTY, empty, id, proc_MPT_ASSERT_STR_NOT_EMPTY, MPT_ArgValues("FAIL", ""));
MPT_TestProcess(MPT_ASSERT_STR_NOT_EMPTY, null, id, proc_MPT_ASSERT_STR_NOT_EMPTY, MPT_ArgValues("FAIL", NULL));
MPT_Test(MPT_ASSERT_STR_NOT_EMPTY, nempty, .xfail=false);
MPT_Test(MPT_ASSERT_STR_NOT_EMPTY, empty, .xfail=true);
MPT_Test(MPT_ASSERT_STR_NOT_EMPTY, null, .xfail=true);
/*****************************************************************************/
/************************************************************
* Test MPT_ASSERT_FATAL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL, MPT_Args(const char *exp, int cond))
{
assert(exp);
MPT_ASSERT_FATAL(cond, "MPT_ASSERT_FATAL(%d), expect: %s", cond, exp);
MPT_ASSERT(cond, "MPT_ASSERT(%d) after a fatal", cond);
}
MPT_TestProcess(MPT_ASSERT_FATAL, pass, id, proc_MPT_ASSERT_FATAL, MPT_ArgValues("PASS", 1));
MPT_Test(MPT_ASSERT_FATAL, pass, .xfail=false);
MPT_TestProcess(MPT_ASSERT_FATAL, fail, id, proc_MPT_ASSERT_FATAL, MPT_ArgValues("FAIL", 0));
MPT_Test(MPT_ASSERT_FATAL, fail, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_FAIL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_FAIL, MPT_NoArgs())
{
MPT_ASSERT_FATAL_FAIL("MPT_ASSERT_FATAL_FAIL(), expect a fail, always");
MPT_ASSERT_FAIL("MPT_ASSERT_FAIL() after a fatal");
}
MPT_TestProcess(MPT_ASSERT_FATAL_FAIL, fail, id, proc_MPT_ASSERT_FATAL_FAIL, MPT_NoArgValues());
MPT_Test(MPT_ASSERT_FATAL_FAIL, fail, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_EQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_EQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_FATAL_EQ(val1, val2, "MPT_ASSERT_FATAL_EQ(%d, %d), expect: %s", val1, val2, exp);
MPT_ASSERT_EQ(val1, val2, "MPT_ASSERT_EQ(%d, %d) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_EQ_int, eq, id, proc_MPT_ASSERT_FATAL_EQ_int, MPT_ArgValues("PASS", 1, 1));
MPT_TestProcess(MPT_ASSERT_FATAL_EQ_int, lt, id, proc_MPT_ASSERT_FATAL_EQ_int, MPT_ArgValues("FAIL", 1, 2));
MPT_TestProcess(MPT_ASSERT_FATAL_EQ_int, gt, id, proc_MPT_ASSERT_FATAL_EQ_int, MPT_ArgValues("FAIL", 3, 2));
MPT_Test(MPT_ASSERT_FATAL_EQ_int, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_EQ_int, lt, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_EQ_int, gt, .xfail=true);
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_EQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_FATAL_EQ(val1, val2, "MPT_ASSERT_FATAL_EQ(%f, %f), expect: %s", val1, val2, exp);
MPT_ASSERT_EQ(val1, val2, "MPT_ASSERT_EQ(%f, %f) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_EQ_double, eq, id, proc_MPT_ASSERT_FATAL_EQ_double, MPT_ArgValues("PASS", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_FATAL_EQ_double, lt, id, proc_MPT_ASSERT_FATAL_EQ_double, MPT_ArgValues("FAIL", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_FATAL_EQ_double, gt, id, proc_MPT_ASSERT_FATAL_EQ_double, MPT_ArgValues("FAIL", 1.3, 1.2));
MPT_Test(MPT_ASSERT_FATAL_EQ_double, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_EQ_double, lt, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_EQ_double, gt, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_NEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_NEQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_FATAL_NEQ(val1, val2, "MPT_ASSERT_FATAL_NEQ(%d, %d), expect: %s", val1, val2, exp);
MPT_ASSERT_NEQ(val1, val2, "MPT_ASSERT_NEQ(%d, %d) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_NEQ_int, eq, id, proc_MPT_ASSERT_FATAL_NEQ_int, MPT_ArgValues("FAIL", 1, 1));
MPT_TestProcess(MPT_ASSERT_FATAL_NEQ_int, lt, id, proc_MPT_ASSERT_FATAL_NEQ_int, MPT_ArgValues("PASS", 1, 2));
MPT_TestProcess(MPT_ASSERT_FATAL_NEQ_int, gt, id, proc_MPT_ASSERT_FATAL_NEQ_int, MPT_ArgValues("PASS", 3, 2));
MPT_Test(MPT_ASSERT_FATAL_NEQ_int, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_NEQ_int, lt, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_NEQ_int, gt, .xfail=false);
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_NEQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_FATAL_NEQ(val1, val2, "MPT_ASSERT_FATAL_NEQ(%f, %f), expect: %s", val1, val2, exp);
MPT_ASSERT_NEQ(val1, val2, "MPT_ASSERT_NEQ(%f, %f) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_NEQ_double, eq, id, proc_MPT_ASSERT_FATAL_NEQ_double, MPT_ArgValues("FAIL", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_FATAL_NEQ_double, lt, id, proc_MPT_ASSERT_FATAL_NEQ_double, MPT_ArgValues("PASS", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_FATAL_NEQ_double, gt, id, proc_MPT_ASSERT_FATAL_NEQ_double, MPT_ArgValues("PASS", 1.3, 1.2));
MPT_Test(MPT_ASSERT_FATAL_NEQ_double, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_NEQ_double, lt, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_NEQ_double, gt, .xfail=false);
/************************************************************
* Test MPT_ASSERT_FATAL_LEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_LEQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_FATAL_LEQ(val1, val2, "MPT_ASSERT_FATAL_LEQ(%d, %d), expect: %s", val1, val2, exp);
MPT_ASSERT_LEQ(val1, val2, "MPT_ASSERT_LEQ(%d, %d) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_LEQ_int, eq, id, proc_MPT_ASSERT_FATAL_LEQ_int, MPT_ArgValues("PASS", 1, 1));
MPT_TestProcess(MPT_ASSERT_FATAL_LEQ_int, lt, id, proc_MPT_ASSERT_FATAL_LEQ_int, MPT_ArgValues("PASS", 1, 2));
MPT_TestProcess(MPT_ASSERT_FATAL_LEQ_int, gt, id, proc_MPT_ASSERT_FATAL_LEQ_int, MPT_ArgValues("FAIL", 3, 2));
MPT_Test(MPT_ASSERT_FATAL_LEQ_int, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_LEQ_int, lt, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_LEQ_int, gt, .xfail=true);
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_LEQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_FATAL_LEQ(val1, val2, "MPT_ASSERT_FATAL_LEQ(%f, %f), expect: %s", val1, val2, exp);
MPT_ASSERT_LEQ(val1, val2, "MPT_ASSERT_LEQ(%f, %f) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_LEQ_double, eq, id, proc_MPT_ASSERT_FATAL_LEQ_double, MPT_ArgValues("PASS", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_FATAL_LEQ_double, lt, id, proc_MPT_ASSERT_FATAL_LEQ_double, MPT_ArgValues("PASS", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_FATAL_LEQ_double, gt, id, proc_MPT_ASSERT_FATAL_LEQ_double, MPT_ArgValues("FAIL", 1.3, 1.2));
MPT_Test(MPT_ASSERT_FATAL_LEQ_double, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_LEQ_double, lt, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_LEQ_double, gt, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_GEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_GEQ_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_FATAL_GEQ(val1, val2, "MPT_ASSERT_FATAL_GEQ(%d, %d), expect: %s", val1, val2, exp);
MPT_ASSERT_GEQ(val1, val2, "MPT_ASSERT_GEQ(%d, %d) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_GEQ_int, eq, id, proc_MPT_ASSERT_FATAL_GEQ_int, MPT_ArgValues("PASS", 1, 1));
MPT_TestProcess(MPT_ASSERT_FATAL_GEQ_int, lt, id, proc_MPT_ASSERT_FATAL_GEQ_int, MPT_ArgValues("FAIL", 1, 2));
MPT_TestProcess(MPT_ASSERT_FATAL_GEQ_int, gt, id, proc_MPT_ASSERT_FATAL_GEQ_int, MPT_ArgValues("PASS", 3, 2));
MPT_Test(MPT_ASSERT_FATAL_GEQ_int, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_GEQ_int, lt, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_GEQ_int, gt, .xfail=false);
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_GEQ_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_FATAL_GEQ(val1, val2, "MPT_ASSERT_FATAL_GEQ(%f, %f), expect: %s", val1, val2, exp);
MPT_ASSERT_GEQ(val1, val2, "MPT_ASSERT_GEQ(%f, %f) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_GEQ_double, eq, id, proc_MPT_ASSERT_FATAL_GEQ_double, MPT_ArgValues("PASS", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_FATAL_GEQ_double, lt, id, proc_MPT_ASSERT_FATAL_GEQ_double, MPT_ArgValues("FAIL", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_FATAL_GEQ_double, gt, id, proc_MPT_ASSERT_FATAL_GEQ_double, MPT_ArgValues("PASS", 1.3, 1.2));
MPT_Test(MPT_ASSERT_FATAL_GEQ_double, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_GEQ_double, lt, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_GEQ_double, gt, .xfail=false);
/************************************************************
* Test MPT_ASSERT_FATAL_LT
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_LT_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_FATAL_LT(val1, val2, "MPT_ASSERT_FATAL_LT(%d, %d), expect: %s", val1, val2, exp);
MPT_ASSERT_LT(val1, val2, "MPT_ASSERT_LT(%d, %d) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_LT_int, eq, id, proc_MPT_ASSERT_FATAL_LT_int, MPT_ArgValues("FAIL", 1, 1));
MPT_TestProcess(MPT_ASSERT_FATAL_LT_int, lt, id, proc_MPT_ASSERT_FATAL_LT_int, MPT_ArgValues("PASS", 1, 2));
MPT_TestProcess(MPT_ASSERT_FATAL_LT_int, gt, id, proc_MPT_ASSERT_FATAL_LT_int, MPT_ArgValues("FAIL", 3, 2));
MPT_Test(MPT_ASSERT_FATAL_LT_int, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_LT_int, lt, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_LT_int, gt, .xfail=true);
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_LT_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_FATAL_LT(val1, val2, "MPT_ASSERT_FATAL_LT(%f, %f), expect: %s", val1, val2, exp);
MPT_ASSERT_LT(val1, val2, "MPT_ASSERT_LT(%f, %f) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_LT_double, eq, id, proc_MPT_ASSERT_FATAL_LT_double, MPT_ArgValues("FAIL", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_FATAL_LT_double, lt, id, proc_MPT_ASSERT_FATAL_LT_double, MPT_ArgValues("PASS", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_FATAL_LT_double, gt, id, proc_MPT_ASSERT_FATAL_LT_double, MPT_ArgValues("FAIL", 1.3, 1.2));
MPT_Test(MPT_ASSERT_FATAL_LT_double, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_LT_double, lt, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_LT_double, gt, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_GT
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_GT_int, MPT_Args(const char *exp, int val1, int val2))
{
assert(exp);
MPT_ASSERT_FATAL_GT(val1, val2, "MPT_ASSERT_FATAL_GT(%d, %d), expect: %s", val1, val2, exp);
MPT_ASSERT_GT(val1, val2, "MPT_ASSERT_GT(%d, %d) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_GT_int, eq, id, proc_MPT_ASSERT_FATAL_GT_int, MPT_ArgValues("FAIL", 1, 1));
MPT_TestProcess(MPT_ASSERT_FATAL_GT_int, lt, id, proc_MPT_ASSERT_FATAL_GT_int, MPT_ArgValues("FAIL", 1, 2));
MPT_TestProcess(MPT_ASSERT_FATAL_GT_int, gt, id, proc_MPT_ASSERT_FATAL_GT_int, MPT_ArgValues("PASS", 3, 2));
MPT_Test(MPT_ASSERT_FATAL_GT_int, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_GT_int, lt, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_GT_int, gt, .xfail=false);
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_GT_double, MPT_Args(const char *exp, double val1, double val2))
{
assert(exp);
MPT_ASSERT_FATAL_GT(val1, val2, "MPT_ASSERT_FATAL_GT(%f, %f), expect: %s", val1, val2, exp);
MPT_ASSERT_GT(val1, val2, "MPT_ASSERT_GT(%f, %f) after a fatal", val1, val2);
}
MPT_TestProcess(MPT_ASSERT_FATAL_GT_double, eq, id, proc_MPT_ASSERT_FATAL_GT_double, MPT_ArgValues("FAIL", 1.1, 1.1));
MPT_TestProcess(MPT_ASSERT_FATAL_GT_double, lt, id, proc_MPT_ASSERT_FATAL_GT_double, MPT_ArgValues("FAIL", 1.1, 1.2));
MPT_TestProcess(MPT_ASSERT_FATAL_GT_double, gt, id, proc_MPT_ASSERT_FATAL_GT_double, MPT_ArgValues("PASS", 1.3, 1.2));
MPT_Test(MPT_ASSERT_FATAL_GT_double, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_GT_double, lt, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_GT_double, gt, .xfail=false);
/************************************************************
* Test MPT_ASSERT_FATAL_NULL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_NULL, MPT_Args(const char *exp, const void* ptr))
{
assert(exp);
MPT_ASSERT_FATAL_NULL(ptr, "MPT_ASSERT_FATAL_NULL(%p), expect: %s", ptr, exp);
}
MPT_TestProcess(MPT_ASSERT_FATAL_NULL, addr, id, proc_MPT_ASSERT_FATAL_NULL, MPT_ArgValues("FAIL", &dummy));
MPT_TestProcess(MPT_ASSERT_FATAL_NULL, null, id, proc_MPT_ASSERT_FATAL_NULL, MPT_ArgValues("PASS", NULL));
MPT_Test(MPT_ASSERT_FATAL_NULL, addr, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_NULL, null, .xfail=false);
/************************************************************
* Test MPT_ASSERT_FATAL_NOT_NULL
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_NOT_NULL, MPT_Args(const char *exp, const void* ptr))
{
assert(exp);
MPT_ASSERT_FATAL_NOT_NULL(ptr, "MPT_ASSERT_FATAL_NOT_NULL(%p), expect: %s", ptr, exp);
}
MPT_TestProcess(MPT_ASSERT_FATAL_NOT_NULL, addr, id, proc_MPT_ASSERT_FATAL_NOT_NULL, MPT_ArgValues("PASS", &dummy));
MPT_TestProcess(MPT_ASSERT_FATAL_NOT_NULL, null, id, proc_MPT_ASSERT_FATAL_NOT_NULL, MPT_ArgValues("FAIL", NULL));
MPT_Test(MPT_ASSERT_FATAL_NOT_NULL, addr, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_NOT_NULL, null, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_STR_EQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_STR_EQ, MPT_Args(const char *exp, const char* val1, const char* val2))
{
assert(exp);
MPT_ASSERT_FATAL_STR_EQ(val1, val2, "MPT_ASSERT_FATAL_STR_EQ(%s, %s), expect: %s",
val1 ? val1 : "<null>",
val2 ? val2 : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EQ, eq, id, proc_MPT_ASSERT_FATAL_STR_EQ, MPT_ArgValues("PASS", "foo", "foo"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EQ, neq, id, proc_MPT_ASSERT_FATAL_STR_EQ, MPT_ArgValues("FAIL", "foo", "bar"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EQ, null1, id, proc_MPT_ASSERT_FATAL_STR_EQ, MPT_ArgValues("FAIL", NULL, "foo"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EQ, null2, id, proc_MPT_ASSERT_FATAL_STR_EQ, MPT_ArgValues("FAIL", "foo", NULL));
MPT_Test(MPT_ASSERT_FATAL_STR_EQ, eq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_STR_EQ, neq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_STR_EQ, null1, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_STR_EQ, null2, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_STR_NEQ
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_STR_NEQ, MPT_Args(const char *exp, const char* val1, const char* val2))
{
assert(exp);
MPT_ASSERT_FATAL_STR_NEQ(val1, val2, "MPT_ASSERT_FATAL_STR_NEQ(%s, %s), expect: %s",
val1 ? val1 : "<null>",
val2 ? val2 : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NEQ, eq, id, proc_MPT_ASSERT_FATAL_STR_NEQ, MPT_ArgValues("FAIL", "foo", "foo"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NEQ, neq, id, proc_MPT_ASSERT_FATAL_STR_NEQ, MPT_ArgValues("PASS", "foo", "bar"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NEQ, null1, id, proc_MPT_ASSERT_FATAL_STR_NEQ, MPT_ArgValues("FAIL", NULL, "foo"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NEQ, null2, id, proc_MPT_ASSERT_FATAL_STR_NEQ, MPT_ArgValues("FAIL", "foo", NULL));
MPT_Test(MPT_ASSERT_FATAL_STR_NEQ, eq, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_STR_NEQ, neq, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_STR_NEQ, null1, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_STR_NEQ, null2, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_STR_EMPTY
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_STR_EMPTY, MPT_Args(const char *exp, const char* val))
{
assert(exp);
MPT_ASSERT_FATAL_STR_EMPTY(val, "MPT_ASSERT_FATAL_STR_EMPTY(%s), expect: %s",
val ? val : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EMPTY, nempty, id, proc_MPT_ASSERT_FATAL_STR_EMPTY, MPT_ArgValues("FAIL", "foo"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EMPTY, empty, id, proc_MPT_ASSERT_FATAL_STR_EMPTY, MPT_ArgValues("PASS", ""));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_EMPTY, null, id, proc_MPT_ASSERT_FATAL_STR_EMPTY, MPT_ArgValues("FAIL", NULL));
MPT_Test(MPT_ASSERT_FATAL_STR_EMPTY, nempty, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_STR_EMPTY, empty, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_STR_EMPTY, null, .xfail=true);
/************************************************************
* Test MPT_ASSERT_FATAL_STR_NOT_EMPTY
************************************************************/
MPT_ProcessEntry(proc_MPT_ASSERT_FATAL_STR_NOT_EMPTY, MPT_Args(const char *exp, const char* val))
{
assert(exp);
MPT_ASSERT_FATAL_STR_NOT_EMPTY(val, "MPT_ASSERT_FATAL_STR_NOT_EMPTY(%s), expect: %s",
val ? val : "<null>",
exp);
}
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NOT_EMPTY, nempty, id, proc_MPT_ASSERT_FATAL_STR_NOT_EMPTY, MPT_ArgValues("PASS", "foo"));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NOT_EMPTY, empty, id, proc_MPT_ASSERT_FATAL_STR_NOT_EMPTY, MPT_ArgValues("FAIL", ""));
MPT_TestProcess(MPT_ASSERT_FATAL_STR_NOT_EMPTY, null, id, proc_MPT_ASSERT_FATAL_STR_NOT_EMPTY, MPT_ArgValues("FAIL", NULL));
MPT_Test(MPT_ASSERT_FATAL_STR_NOT_EMPTY, nempty, .xfail=false);
MPT_Test(MPT_ASSERT_FATAL_STR_NOT_EMPTY, empty, .xfail=true);
MPT_Test(MPT_ASSERT_FATAL_STR_NOT_EMPTY, null, .xfail=true);
/*****************************************************************************/
/************************************************************
* Test propagation,
* Check if failure/success is actually propagated to ctest.
************************************************************/
MPT_ProcessEntry(proc_propagation, MPT_Args(const char *exp, int cond, dds_duration_t delay))
{
assert(exp);
if (delay > 0) {
dds_sleepfor(delay);
}
MPT_ASSERT(cond, "MPT_ASSERT(%d), expect: %s", cond, exp);
}
/* This should pass in the ctest results. */
MPT_TestProcess(propagation, pass, id1, proc_propagation, MPT_ArgValues("PASS", 1, 0));
MPT_TestProcess(propagation, pass, id2, proc_propagation, MPT_ArgValues("PASS", 1, 0));
MPT_Test(propagation, pass);
/* This should fail in the ctest results. */
MPT_TestProcess(propagation, fail_1st, id1, proc_propagation, MPT_ArgValues("FAIL", 0, 0));
MPT_TestProcess(propagation, fail_1st, id2, proc_propagation, MPT_ArgValues("PASS", 1, DDS_SECS(1)));
MPT_Test(propagation, fail_1st);
/* This should fail in the ctest results. */
MPT_TestProcess(propagation, fail_2nd, id1, proc_propagation, MPT_ArgValues("PASS", 1, 0));
MPT_TestProcess(propagation, fail_2nd, id2, proc_propagation, MPT_ArgValues("FAIL", 0, DDS_SECS(1)));
MPT_Test(propagation, fail_2nd);

View file

@ -0,0 +1,96 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "mpt/mpt.h"
#include "mpt/resource.h" /* MPT_SOURCE_ROOT_DIR */
#include "dds/ddsrt/environ.h"
/************************************************************
* Process
************************************************************/
MPT_ProcessEntry(proc_environments, MPT_Args(mpt_env_t *exp))
{
assert(exp);
while ((exp->name != NULL) && (exp->value != NULL)) {
/* The environment variable value should match the expected value. */
char *ptr = NULL;
ddsrt_getenv(exp->name, &ptr);
if (ptr) {
MPT_ASSERT((strcmp(exp->value, ptr) == 0), "%s: found \"%s\", expected \"%s\"", exp->name, ptr, exp->value);
} else {
MPT_ASSERT(0, "Expected \"%s\" not found in environment", exp->name);
}
exp++;
}
}
/************************************************************
* Basic environment tests
************************************************************/
static mpt_env_t environ_basic[] = {
{ "MY_ENV_VAR1", "1" },
{ "MY_ENV_VAR2", "2" },
{ NULL, NULL }
};
MPT_TestProcess(environment, proc, id, proc_environments, MPT_ArgValues(environ_basic), .environment=environ_basic);
MPT_Test(environment, proc);
MPT_TestProcess(environment, test, id, proc_environments, MPT_ArgValues(environ_basic));
MPT_Test(environment, test, .environment=environ_basic);
/************************************************************
* Expanding variables environment tests
************************************************************/
static mpt_env_t environ_expand[] = {
{ "1", "b" },
{ "2", "${1}l" },
{ "3", "${2}aat" },
{ "4", "bla${1}${2}a${3}" },
{ NULL, NULL }
};
static mpt_env_t expect_expand[] = {
{ "1", "b" },
{ "2", "bl" },
{ "3", "blaat" },
{ "4", "blabblablaat" },
{ NULL, NULL }
};
MPT_TestProcess(environment, expand_proc, id, proc_environments, MPT_ArgValues(expect_expand), .environment=environ_expand);
MPT_Test(environment, expand_proc);
MPT_TestProcess(environment, expand_test, id, proc_environments, MPT_ArgValues(expect_expand));
MPT_Test(environment, expand_test, .environment=environ_expand);
/************************************************************
* Environment inheritance test
************************************************************/
static mpt_env_t environ_test[] = {
{ "ETC_DIR", MPT_SOURCE_ROOT_DIR"/tests/self/etc" },
{ "OVERRULE", "NO" },
{ NULL, NULL }
};
static mpt_env_t environ_proc[] = {
{ "CYCLONE_URI", "file://${ETC_DIR}/ospl.xml" },
{ "OVERRULE", "YES" },
{ "EXTRA", "proc" },
{ NULL, NULL }
};
static mpt_env_t environ_test_proc[] = {
{ "ETC_DIR", MPT_SOURCE_ROOT_DIR"/tests/self/etc" },
{ "CYCLONE_URI", "file://"MPT_SOURCE_ROOT_DIR"/tests/self/etc/ospl.xml" },
{ "OVERRULE", "YES" },
{ "EXTRA", "proc" },
{ NULL, NULL }
};
MPT_TestProcess(environment, inheritance, id, proc_environments, MPT_ArgValues(environ_test_proc), .environment=environ_proc);
MPT_Test(environment, inheritance, .environment=environ_test);

View file

View file

@ -0,0 +1,75 @@
#include <stdio.h>
#include "mpt/mpt.h"
#include "mpt/resource.h" /* MPT_SOURCE_ROOT_DIR */
#include "dds/ddsrt/time.h"
/************************************************************
* Support functions
************************************************************/
static int g_initcnt = 0;
static void init_inc(void)
{
g_initcnt++;
};
/************************************************************
* Processes
************************************************************/
MPT_ProcessEntry(proc_initcnt, MPT_Args(int init))
{
MPT_ASSERT((g_initcnt == init), "init count: %d vs %d", g_initcnt, init);
}
MPT_ProcessEntry(proc_sleep, MPT_Args(dds_duration_t delay))
{
MPT_ASSERT((delay > 0), "basically just to satisfy the compiler");
dds_sleepfor(delay);
}
/************************************************************
* Init fixture tests
************************************************************/
MPT_TestProcess(init, none, id, proc_initcnt, MPT_ArgValues(0));
MPT_Test(init, none);
MPT_TestProcess(init, null, id, proc_initcnt, MPT_ArgValues(0), .init=NULL);
MPT_Test(init, null, .init=NULL);
MPT_TestProcess(init, proc, id, proc_initcnt, MPT_ArgValues(1), .init=init_inc);
MPT_Test(init, proc);
MPT_TestProcess(init, test, id, proc_initcnt, MPT_ArgValues(1));
MPT_Test(init, test, .init=init_inc);
MPT_TestProcess(init, test_proc, id, proc_initcnt, MPT_ArgValues(2), .init=init_inc);
MPT_Test(init, test_proc, .init=init_inc);
/************************************************************
* Disable fixture tests
************************************************************/
MPT_TestProcess(disabled, _true, id, proc_initcnt, MPT_ArgValues(0));
MPT_Test(disabled, _true, .disabled=true);
MPT_TestProcess(disabled, _false, id, proc_initcnt, MPT_ArgValues(0));
MPT_Test(disabled, _false, .disabled=false);
/************************************************************
* Timeout fixture tests
************************************************************/
/* See if a child process is killed when the parent is killed.
* This can only really be done manually, unfortunately. */
MPT_TestProcess(timeout, child_culling, id, proc_sleep, MPT_ArgValues(DDS_SECS(120)));
MPT_Test(timeout, child_culling, .timeout=1);
MPT_TestProcess(timeout, default_timeout, id, proc_sleep, MPT_ArgValues(DDS_SECS(120)));
MPT_Test(timeout, default_timeout);

29
src/mpt/tests/self/ipc.c Normal file
View file

@ -0,0 +1,29 @@
#include <stdio.h>
#include "mpt/mpt.h"
/************************************************************
* Processes
************************************************************/
MPT_ProcessEntry(proc_ipc_send, MPT_NoArgs())
{
/* This will fail and cause an internal MPT assert as long
* as IPC is not yet implemented. */
MPT_Send("todo: implement");
}
MPT_ProcessEntry(proc_ipc_wait, MPT_NoArgs())
{
/* This will fail and cause an internal MPT assert as long
* as IPC is not yet implemented. */
MPT_Wait("todo: implement");
}
/************************************************************
* Tests
************************************************************/
MPT_TestProcess(ipc, TODO, id1, proc_ipc_send, MPT_NoArgValues());
MPT_TestProcess(ipc, TODO, id2, proc_ipc_wait, MPT_NoArgValues());
MPT_Test(ipc, TODO);

View file

@ -0,0 +1,30 @@
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "mpt/mpt.h"
#include "mpt/resource.h" /* MPT_SOURCE_ROOT_DIR */
/************************************************************
* Processes
************************************************************/
MPT_ProcessEntry(proc_file, MPT_NoArgs())
{
const char *test_file = MPT_SOURCE_ROOT_DIR"/tests/self/etc/file";
#if _WIN32
struct _stat buffer;
int ret = _stat(test_file,&buffer);
#else
struct stat buffer;
int ret = stat(test_file,&buffer);
#endif
MPT_ASSERT((ret == 0), "%s", test_file);
}
/************************************************************
* Test if MPT_SOURCE_ROOT_DIR is a valid location.
************************************************************/
MPT_TestProcess(resources, root_dir, id, proc_file, MPT_NoArgValues());
MPT_Test(resources, root_dir);

186
src/mpt/tests/self/usage.c Normal file
View file

@ -0,0 +1,186 @@
#include <stdio.h>
#include "mpt/mpt.h"
#include "mpt/resource.h" /* MPT_SOURCE_ROOT_DIR */
/******************************************************************************
* First, we need a process entry-point that can be used in tests.
*****************************************************************************/
/* | name | arguments | */
MPT_ProcessEntry(proc_noargs, MPT_NoArgs())
{
// Do stuff
// The test processes will use asserts to indicate success/failures.
MPT_ASSERT(1, "The problem is: %s", "existential crisis");
// No need to return anything, that's handled by the assert calls.
}
/******************************************************************************
* A process entry-point can have arguments.
*****************************************************************************/
/* | name | arguments | */
MPT_ProcessEntry(proc_args, MPT_Args(int domain, const char* text))
{
int expected = 1;
MPT_ASSERT(expected == domain, "proc_args(%d, %s)", domain, text);
}
/******************************************************************************
* Process entry-points can communicate to be able to sync fi.
*****************************************************************************/
MPT_ProcessEntry(proc_recv, MPT_NoArgs())
{
/* This will wait until another process sends the same string. */
MPT_Wait("some state reached");
}
MPT_ProcessEntry(proc_send, MPT_NoArgs())
{
/* If this fails, an internal MPT_ASSERT will be triggered.
* The same is true for MPT_Wait(). */
MPT_Send("some state reached");
}
/******************************************************************************
* Test: suitename_testA
******************************************************************************
* A simple test that starts two processes. Because a test can use the same
* process entry-point to start multiple processes, each process has to have
* its own unique id within the test.
*/
/* | process identification | entry-point | arguments | */
MPT_TestProcess(suitename, testA, id1, proc_noargs, MPT_NoArgValues());
MPT_TestProcess(suitename, testA, id2, proc_noargs, MPT_NoArgValues());
MPT_Test(suitename, testA);
/******************************************************************************
* Test: suitename_testB
******************************************************************************
* Of course, different processes can be started as well.
* Argument values are provided per test process.
*/
MPT_TestProcess(suitename, testB, id1, proc_noargs, MPT_NoArgValues( ));
MPT_TestProcess(suitename, testB, id2, proc_args, MPT_ArgValues(1, "2"));
MPT_TestProcess(suitename, testB, id3, proc_args, MPT_ArgValues(1, "3"));
MPT_Test(suitename, testB);
/******************************************************************************
* Test: suitename_testC
******************************************************************************
* The processes can have different or equal 'system environments'.
*/
mpt_env_t environment_C1[] = {
{ "CYCLONEDDS_URI", "file://config1.xml" },
{ "PERMISSIONS", "file://permissions.p7s" },
{ "GOVERNANCE", "file://governance.p7s" },
{ NULL, NULL }
};
mpt_env_t environment_C2[] = {
{ "CYCLONEDDS_URI", "file://config2.xml" },
{ "PERMISSIONS", "file://permissions.p7s" },
{ "GOVERNANCE", "file://governance.p7s" },
{ NULL, NULL }
};
MPT_TestProcess(suitename, testC, id1, proc_noargs, MPT_NoArgValues(), .environment=environment_C1);
MPT_TestProcess(suitename, testC, id2, proc_noargs, MPT_NoArgValues(), .environment=environment_C1);
MPT_TestProcess(suitename, testC, id3, proc_noargs, MPT_NoArgValues(), .environment=environment_C2);
MPT_Test(suitename, testC);
/******************************************************************************
* Test: suitename_testD
******************************************************************************
* The two environments in the previous example are partly the same.
* It's possible set the environment on test level. The environment variables
* related to the test are set before the ones related to a process. This
* means that a process can overrule variables.
*
* The following test is the same as the previous one.
*/
mpt_env_t environment_D1[] = {
{ "CYCLONEDDS_URI", "file://config1.xml" },
{ "PERMISSIONS", "file://permissions.p7s" },
{ "GOVERNANCE", "file://governance.p7s" },
{ NULL, NULL }
};
mpt_env_t environment_D2[] = {
{ "CYCLONEDDS_URI", "file://config2.xml" },
{ NULL, NULL }
};
MPT_TestProcess(suitename, testD, id1, proc_noargs, MPT_NoArgValues());
MPT_TestProcess(suitename, testD, id2, proc_noargs, MPT_NoArgValues());
MPT_TestProcess(suitename, testD, id3, proc_noargs, MPT_NoArgValues(), .environment=environment_D2);
MPT_Test(suitename, testD, .environment=environment_D1);
/******************************************************************************
* Test: suitename_testE
******************************************************************************
* Environment variables will be expanded.
* Also, the MPT_SOURCE_ROOT_DIR define contains a string to that particular
* directory.
* This can be combined to easily point to files.
*/
mpt_env_t environment_E[] = {
{ "ETC_DIR", MPT_SOURCE_ROOT_DIR"/tests/self/etc" },
{ "CYCLONEDDS_URI", "file://${ETC_DIR}/config.xml" },
{ NULL, NULL }
};
MPT_TestProcess(suitename, testE, id, proc_noargs, MPT_NoArgValues(), .environment=environment_E);
MPT_Test(suitename, testE);
/******************************************************************************
* Test: suitename_testF
******************************************************************************
* The processes and tests can use init/fini fixtures.
* The test init is executed before the process init.
* The process fini is executed before the test fini.
*/
void proc_setup(void) { /* do stuff */ }
void proc_teardown(void) { /* do stuff */ }
void test_setup(void) { /* do stuff */ }
void test_teardown(void) { /* do stuff */ }
MPT_TestProcess(suitename, testF, id1, proc_noargs, MPT_NoArgValues(), .init=proc_setup);
MPT_TestProcess(suitename, testF, id2, proc_noargs, MPT_NoArgValues(), .fini=proc_teardown);
MPT_TestProcess(suitename, testF, id3, proc_noargs, MPT_NoArgValues(), .init=proc_setup, .fini=proc_teardown);
MPT_Test(suitename, testF, .init=test_setup, .fini=test_teardown);
/******************************************************************************
* Test: suitename_testG
******************************************************************************
* The timeout and disable options are handled by test fixtures.
*/
MPT_TestProcess(suitename, testG, id1, proc_noargs, MPT_NoArgValues());
MPT_TestProcess(suitename, testG, id2, proc_noargs, MPT_NoArgValues());
MPT_Test(suitename, testG, .timeout=10, .disabled=true);
/******************************************************************************
* Test: suitename_testH
******************************************************************************
* See the process entries to notice the MPT Send/Wait IPC.
*/
MPT_TestProcess(suitename, testH, id1, proc_recv, MPT_NoArgValues());
MPT_TestProcess(suitename, testH, id2, proc_send, MPT_NoArgValues());
MPT_Test(suitename, testH);