Merge pull request #152 from martinbremmer/mptproto3
Multi Process Testing framework
This commit is contained in:
commit
bf79e12e10
44 changed files with 4458 additions and 69 deletions
86
docs/dev/mpt_req.md
Normal file
86
docs/dev/mpt_req.md
Normal 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]
|
||||
|
86
docs/dev/multi_process_testing.md
Normal file
86
docs/dev/multi_process_testing.md
Normal 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">
|
||||
|
BIN
docs/dev/pictures/mpt_flow.png
Normal file
BIN
docs/dev/pictures/mpt_flow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
BIN
docs/dev/pictures/mpt_tree.png
Normal file
BIN
docs/dev/pictures/mpt_tree.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
309
src/ddsrt/src/process/posix/process.c
Normal file
309
src/ddsrt/src/process/posix/process.c
Normal 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;
|
||||
}
|
447
src/ddsrt/src/process/windows/process.c
Normal file
447
src/ddsrt/src/process/windows/process.c
Normal 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;
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
284
src/ddsrt/tests/process.c
Normal 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);
|
||||
}
|
108
src/ddsrt/tests/process_app.c
Normal file
108
src/ddsrt/tests/process_app.c
Normal 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;
|
||||
}
|
42
src/ddsrt/tests/process_test.h.in
Normal file
42
src/ddsrt/tests/process_test.h.in
Normal 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
23
src/mpt/CMakeLists.txt
Normal 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)
|
||||
|
17
src/mpt/mpt/CMakeLists.txt
Normal file
17
src/mpt/mpt/CMakeLists.txt
Normal 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
173
src/mpt/mpt/cmake/MPT.cmake
Normal 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()
|
||||
|
157
src/mpt/mpt/include/mpt/mpt.h
Normal file
157
src/mpt/mpt/include/mpt/mpt.h
Normal 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 */
|
153
src/mpt/mpt/include/mpt/private/mpt.h
Normal file
153
src/mpt/mpt/include/mpt/private/mpt.h
Normal 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 */
|
7
src/mpt/mpt/include/mpt/resource.h.in
Normal file
7
src/mpt/mpt/include/mpt/resource.h.in
Normal 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
549
src/mpt/mpt/src/main.c.in
Normal 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;
|
||||
}
|
||||
|
19
src/mpt/tests/CMakeLists.txt
Normal file
19
src/mpt/tests/CMakeLists.txt
Normal 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)
|
||||
|
23
src/mpt/tests/basic/CMakeLists.txt
Normal file
23
src/mpt/tests/basic/CMakeLists.txt
Normal 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)
|
||||
|
30
src/mpt/tests/basic/etc/config_any.xml
Normal file
30
src/mpt/tests/basic/etc/config_any.xml
Normal 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>
|
30
src/mpt/tests/basic/etc/config_specific.xml
Normal file
30
src/mpt/tests/basic/etc/config_specific.xml
Normal 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>
|
63
src/mpt/tests/basic/helloworld.c
Normal file
63
src/mpt/tests/basic/helloworld.c
Normal 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
|
||||
|
||||
|
||||
|
50
src/mpt/tests/basic/multi.c
Normal file
50
src/mpt/tests/basic/multi.c
Normal 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
|
239
src/mpt/tests/basic/procs/hello.c
Normal file
239
src/mpt/tests/basic/procs/hello.c
Normal 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);
|
||||
}
|
52
src/mpt/tests/basic/procs/hello.h
Normal file
52
src/mpt/tests/basic/procs/hello.h
Normal 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 */
|
9
src/mpt/tests/basic/procs/helloworlddata.idl
Normal file
9
src/mpt/tests/basic/procs/helloworlddata.idl
Normal file
|
@ -0,0 +1,9 @@
|
|||
module HelloWorldData
|
||||
{
|
||||
struct Msg
|
||||
{
|
||||
long userID;
|
||||
string message;
|
||||
};
|
||||
#pragma keylist Msg userID
|
||||
};
|
25
src/mpt/tests/self/CMakeLists.txt
Normal file
25
src/mpt/tests/self/CMakeLists.txt
Normal 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})
|
||||
|
711
src/mpt/tests/self/asserts.c
Normal file
711
src/mpt/tests/self/asserts.c
Normal 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);
|
||||
|
96
src/mpt/tests/self/environments.c
Normal file
96
src/mpt/tests/self/environments.c
Normal 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);
|
0
src/mpt/tests/self/etc/file
Normal file
0
src/mpt/tests/self/etc/file
Normal file
75
src/mpt/tests/self/fixtures.c
Normal file
75
src/mpt/tests/self/fixtures.c
Normal 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
29
src/mpt/tests/self/ipc.c
Normal 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);
|
30
src/mpt/tests/self/resources.c
Normal file
30
src/mpt/tests/self/resources.c
Normal 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
186
src/mpt/tests/self/usage.c
Normal 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);
|
Loading…
Add table
Add a link
Reference in a new issue