Merge branch '7.0' into PSI-7.0
This commit is contained in:
@@ -49,17 +49,84 @@ DBCORE_API int db_post_events (
|
||||
void *pRecord, void *pField, unsigned caEventMask );
|
||||
|
||||
typedef void EXTRALABORFUNC (void *extralabor_arg);
|
||||
/** @brief Allocate event reception context
|
||||
* @return NULL on error
|
||||
*
|
||||
* On success, call db_start_events(), and then eventually db_close_events().
|
||||
*
|
||||
* @pre Call after initHookAfterInitDatabase and before initHookBeforeFree.
|
||||
*/
|
||||
DBCORE_API dbEventCtx db_init_events (void);
|
||||
/** @brief Start listener thread
|
||||
* @param ctx Context
|
||||
* @param taskname Thread name
|
||||
* @param init_func If not NULL, call from the newly created listener thread
|
||||
* @param init_func_arg Argument to init_func
|
||||
* @param osiPriority Thread priority. See epicsThreadOpts::priority
|
||||
* @return DB_EVENT_OK Success. DB_EVENT_ERROR, failed to create new thread.
|
||||
*
|
||||
* Basic lifecycle:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* dbEventCtx ctxt = db_init_events();
|
||||
* assert(ctxt);
|
||||
* int ret = db_start_events(ctxt, "mymodule", NULL, NULL, 0);
|
||||
* assert(ret==DB_EVENT_OK);
|
||||
* ... create and close subscriptions
|
||||
* db_close_events(ctxt);
|
||||
* @endcode
|
||||
*
|
||||
* Flow control initially disabled, so events can be delivered.
|
||||
*/
|
||||
DBCORE_API int db_start_events (
|
||||
dbEventCtx ctx, const char *taskname, void (*init_func)(void *),
|
||||
void *init_func_arg, unsigned osiPriority );
|
||||
/** @brief Stop and deallocate event reception context
|
||||
* @param ctx Context
|
||||
*
|
||||
* @pre Call after initHookAfterInitDatabase and before initHookBeforeFree.
|
||||
* @pre All dbEventSubscription must first be deallocated by db_cancel_event().
|
||||
* @post Joins event listener thread.
|
||||
*/
|
||||
DBCORE_API void db_close_events (dbEventCtx ctx);
|
||||
/** @brief Enable flow control, pause event delivery
|
||||
* @param ctx Context
|
||||
*/
|
||||
DBCORE_API void db_event_flow_ctrl_mode_on (dbEventCtx ctx);
|
||||
/** @brief Disable flow control, resume event delivery
|
||||
* @param ctx Context
|
||||
*/
|
||||
DBCORE_API void db_event_flow_ctrl_mode_off (dbEventCtx ctx);
|
||||
/** @brief Setup/Clear extra labor callback
|
||||
* @param ctx Context
|
||||
* @param func Extra labor callback, may be NULL to clear previously set callback.
|
||||
* @param arg Argument to func
|
||||
* @return DB_EVENT_OK, always succeeds.
|
||||
*
|
||||
* Does not queue callback. See db_post_extra_labor().
|
||||
*/
|
||||
DBCORE_API int db_add_extra_labor_event (
|
||||
dbEventCtx ctx, EXTRALABORFUNC *func, void *arg);
|
||||
/** @brief Wait for extra labor callback.
|
||||
*
|
||||
* @pre Do not call from event listener thread.
|
||||
* @post extra labor queued flag is unchanged
|
||||
*
|
||||
* To ensure completion, call should arrange that db_post_extra_labor() will
|
||||
* not be called again.
|
||||
*/
|
||||
DBCORE_API void db_flush_extra_labor_event (dbEventCtx);
|
||||
/** @brief Queue extra labor callback event
|
||||
* @param ctx Context
|
||||
* @return DB_EVENT_OK, always succeeds.
|
||||
*
|
||||
* Sets an internal queued flag, which still be set from a previous call.
|
||||
*/
|
||||
DBCORE_API int db_post_extra_labor (dbEventCtx ctx);
|
||||
/** @brief Change event listener thread priority
|
||||
* @param ctx Context
|
||||
* @param epicsPriority Thread priority. See epicsThreadOpts::priority
|
||||
*/
|
||||
DBCORE_API void db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority );
|
||||
|
||||
#ifdef EPICS_PRIVATE_API
|
||||
@@ -67,19 +134,106 @@ DBCORE_API void db_cleanup_events(void);
|
||||
DBCORE_API void db_init_event_freelists (void);
|
||||
#endif
|
||||
|
||||
/** @brief Subscription event callback
|
||||
* @param user_arg private argument passed to db_add_event()
|
||||
* @param chan dbChannel passed to db_add_event()
|
||||
* @param eventsRemaining Approximate number of events remaining in queued.
|
||||
* Not exact. Use is discouraged.
|
||||
* @param pfl db_field_log of this event.
|
||||
*
|
||||
* An event callback is expected to call dbChannelGetField() or take equivalent action.
|
||||
* eg. explicitly lock the record and call dbChannelGet().
|
||||
*
|
||||
* Callee must _not_ delete the db_field_log.
|
||||
*
|
||||
* @since 7.0.5, @code pfl->mask @endcode may be used to detect which condition(s)
|
||||
* triggered this event.
|
||||
*/
|
||||
typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan,
|
||||
int eventsRemaining, struct db_field_log *pfl);
|
||||
|
||||
/** @brief Create subscription to channel
|
||||
* @param ctx Context
|
||||
* @param chan Channel
|
||||
* @param user_sub Event callback
|
||||
* @param user_arg Private argument
|
||||
* @param select Bit mask of DBE_VALUE and others. See caeventmask.h
|
||||
* @return NULL on error. On success, later call db_cancel_event()
|
||||
*
|
||||
* Creates a new subscription to the specified dbChannel.
|
||||
* Callbacks will be delivered on the listener thread of the provided Context.
|
||||
*
|
||||
* Creation does not queue any events.
|
||||
* Follow with a call to db_post_single_event() to queue an initial event.
|
||||
*
|
||||
* Subscription is initially disabled. Call db_event_enable();
|
||||
*
|
||||
* Basic lifecycle:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* static
|
||||
* void mycb(void *priv, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl) {
|
||||
* (void)eventsRemaining; // use not recommended
|
||||
*
|
||||
* // dbChannelGetField() locks record.
|
||||
* // May read value and meta-data from event (db_field_log).
|
||||
* long ret = dbChannelGetField(chan, ..., pfl);
|
||||
* }
|
||||
* void someaction() {
|
||||
* dbEventCtx ctx = ...; // previously created
|
||||
* dbChannel *chan = ...;
|
||||
* void *priv = ...;
|
||||
*
|
||||
* dbEventSubscription sub = db_add_event(ctx, chan, mycb, priv, DBE_VALUE|DBE_ALARM);
|
||||
* assert(sub);
|
||||
* db_cancel_event(sub);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
DBCORE_API dbEventSubscription db_add_event (
|
||||
dbEventCtx ctx, struct dbChannel *chan,
|
||||
EVENTFUNC *user_sub, void *user_arg, unsigned select);
|
||||
/** @brief Deallocate subscription
|
||||
* @param es Subscription. Must not be NULL.
|
||||
*
|
||||
* Synchronizes with Event Context worker thread to wait for a concurrent callback
|
||||
* to complete.
|
||||
*/
|
||||
DBCORE_API void db_cancel_event (dbEventSubscription es);
|
||||
/** @brief Immediately attempt to queue an event with the present value
|
||||
* @param es Subscription
|
||||
*
|
||||
* Locks record and runs pre-chain of any server-side filters.
|
||||
* Such a filter may drop the new event (a well designed filter should not drop the first event).
|
||||
*/
|
||||
DBCORE_API void db_post_single_event (dbEventSubscription es);
|
||||
/** @brief Enable subscription callback delivery
|
||||
* @param es Subscription
|
||||
*/
|
||||
DBCORE_API void db_event_enable (dbEventSubscription es);
|
||||
/** @brief Disable subscription callback delivery
|
||||
* @param es Subscription
|
||||
*
|
||||
* Does __not__ synchronize with listener thread, pending callbacks may be delivered.
|
||||
* Use extra-labor mechanism and db_flush_extra_labor_event() to synchronize.
|
||||
*/
|
||||
DBCORE_API void db_event_disable (dbEventSubscription es);
|
||||
|
||||
/** @brief Allocate subscription update event.
|
||||
* @param pevent Subscription
|
||||
* @return NULL on allocation failure.
|
||||
*/
|
||||
DBCORE_API struct db_field_log* db_create_event_log (struct evSubscrip *pevent);
|
||||
/** @brief Allocate "read" event.
|
||||
* @param pevent Subscription
|
||||
* @return NULL on allocation failure.
|
||||
*
|
||||
* Used by PVA or CA "GET" operations when polling the current value of a Channel.
|
||||
*/
|
||||
DBCORE_API struct db_field_log* db_create_read_log (struct dbChannel *chan);
|
||||
/** @brief db_delete_field_log
|
||||
* @param pfl event structure. May be NULL (no-op).
|
||||
*/
|
||||
DBCORE_API void db_delete_field_log (struct db_field_log *pfl);
|
||||
DBCORE_API int db_available_logs(void);
|
||||
|
||||
@@ -90,4 +244,40 @@ DBCORE_API int db_available_logs(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @file dbEvent.h
|
||||
*
|
||||
* Internal publish/subscribe mechanism of process database.
|
||||
* Direct usage is discouraged in favor of derived interfaces,
|
||||
* principally local and remote PVA/CA.
|
||||
*
|
||||
* @since 7.0.8.1 New usage is recommended to define the USE_TYPED_DBEVENT C macro to select
|
||||
* typed arguments of some calls as opposed to void pointers.
|
||||
*
|
||||
* @section dbeventobjects Objects
|
||||
*
|
||||
* - Event context (dbEventCtx)
|
||||
* - Event subscription (dbEventSubscription)
|
||||
* - Channel (dbChannel)
|
||||
* - Event / field log (db_field_log)
|
||||
*
|
||||
* @section dbeventlifecycle Lifecycle
|
||||
*
|
||||
* Usage is tied to the lifetime of the process database.
|
||||
* Either through @ref inithooks or @ref dbunittest .
|
||||
* db_init_events() must not be called before initHookAfterInitDatabase or testIocInitOk().
|
||||
* db_close_events() must be called after initHookBeforeFree or testIocShutdownOk().
|
||||
*
|
||||
* @note testMonitorCreate() and friends are provided to easy handling of
|
||||
* subscriptions in unit tests.
|
||||
*
|
||||
* Subscriptions associated with an Event Context must be deallocated before
|
||||
* that Context is deallocated.
|
||||
*
|
||||
* @section dbeventthread Concurrency
|
||||
*
|
||||
* Each Event Context has an associated worker thread on which subscription callbacks are invoked.
|
||||
* Some API functions implicitly synchronize with that worker thread as noted.
|
||||
* The "extra labor" functions may be used to explicitly synchronize with this thread.
|
||||
*/
|
||||
|
||||
#endif /*INCLdbEventh*/
|
||||
|
||||
@@ -6,13 +6,6 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/** @file dbUnitTest.h
|
||||
* @brief Helpers for unittests of process database
|
||||
* @author Michael Davidsaver, Ralph Lange
|
||||
*
|
||||
* @see @ref dbunittest
|
||||
*/
|
||||
|
||||
#ifndef EPICSUNITTESTDB_H
|
||||
#define EPICSUNITTESTDB_H
|
||||
|
||||
@@ -204,15 +197,19 @@ DBCORE_API void testGlobalUnlock(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @page dbunittest Unit testing of record processing
|
||||
/** @file dbUnitTest.h
|
||||
* @brief Helpers for unittests of process database
|
||||
* @author Michael Davidsaver, Ralph Lange
|
||||
*
|
||||
* @see @ref epicsUnitTest.h
|
||||
* @section dbunittest Unit testing of record processing
|
||||
*
|
||||
* @see @ref unittest
|
||||
*
|
||||
* @section dbtestskel Test skeleton
|
||||
*
|
||||
* For the impatient, the skeleton of a test:
|
||||
*
|
||||
* @code
|
||||
* @code{.c}
|
||||
* #include <dbUnitTest.h>
|
||||
* #include <testMain.h>
|
||||
*
|
||||
@@ -236,7 +233,7 @@ DBCORE_API void testGlobalUnlock(void);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @code
|
||||
* @code{make}
|
||||
* TOP = ..
|
||||
* include $(TOP)/configure/CONFIG
|
||||
*
|
||||
@@ -254,6 +251,27 @@ DBCORE_API void testGlobalUnlock(void);
|
||||
* include $(TOP)/configure/RULES
|
||||
* @endcode
|
||||
*
|
||||
* Discussion:
|
||||
*
|
||||
* Some tests require the context of an IOC to be run. This conflicts with the
|
||||
* idea of running multiple tests within a test harness, as iocInit() is only
|
||||
* allowed to be called once, and some parts of the full IOC (e.g. the rsrv CA
|
||||
* server) can not be shut down cleanly. The function iocBuildIsolated() allows
|
||||
* to start an IOC without its Channel Access parts, so that it can be shutdown
|
||||
* quite cleanly using iocShutdown(). This feature is only intended to be used
|
||||
* from test programs, do not use it on production IOCs. After building the
|
||||
* IOC using iocBuildIsolated() or iocBuild(), it has to be started by calling
|
||||
* iocRun().
|
||||
*
|
||||
* The part from iocBuildIsolated() to iocShutdown() can be repeated to
|
||||
* execute multiple tests within one executable or harness.
|
||||
*
|
||||
* To make it easier to create a single test program that can be built for
|
||||
* both the embedded and workstation operating system harnesses, the header file
|
||||
* testMain.h provides a convenience macro MAIN() that adjusts the name of the
|
||||
* test program according to the platform it is running on: main() on
|
||||
* workstations and a regular function name on embedded systems.
|
||||
*
|
||||
* @section dbtestactions Actions
|
||||
*
|
||||
* Several helper functions are provided to interact with a running database.
|
||||
@@ -276,7 +294,7 @@ DBCORE_API void testGlobalUnlock(void);
|
||||
*
|
||||
* @see enum dbfType in dbFldTypes.h
|
||||
*
|
||||
* @code
|
||||
* @code{.c}
|
||||
* testdbPutFieldOk("pvname", DBF_ULONG, (unsigned int)5);
|
||||
* testdbPutFieldOk("pvname", DBF_FLOAT, (double)4.1);
|
||||
* testdbPutFieldOk("pvname", DBF_STRING, "hello world");
|
||||
@@ -324,7 +342,7 @@ DBCORE_API void testGlobalUnlock(void);
|
||||
* When possible, the best way to avoid this race would be to join the worker
|
||||
* before destroying the event.
|
||||
*
|
||||
* @code
|
||||
* @code{.c}
|
||||
* epicsEventId evt;
|
||||
* void thread1() {
|
||||
* epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
|
||||
@@ -346,7 +364,7 @@ DBCORE_API void testGlobalUnlock(void);
|
||||
* that epicsEventMustSignal() has returned before destroying the event.
|
||||
* testGlobalLock() and testGlobalUnlock() provide access to such a mutex.
|
||||
*
|
||||
* @code
|
||||
* @code{.c}
|
||||
* epicsEventId evt;
|
||||
* void thread1() {
|
||||
* evt = epicsEventMustCreate(...);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* (C) 2014 David Lettier.
|
||||
* http://www.lettier.com/
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* use of UDP funktions and implement timeout, HPJ 15.12.2025
|
||||
\*************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
@@ -20,7 +22,6 @@
|
||||
|
||||
int epicsNtpGetTime(char *ntpIp, struct timespec *now)
|
||||
{
|
||||
|
||||
int sockfd, n; // Socket file descriptor and the n return result from writing/reading from the socket.
|
||||
int portno = 123; // NTP UDP port number.
|
||||
|
||||
@@ -30,34 +31,31 @@ int epicsNtpGetTime(char *ntpIp, struct timespec *now)
|
||||
// Set the first byte's bits to 00,011,011 for li = 0, vn = 3, and mode = 3. The rest will be left set to zero.
|
||||
*( ( char * ) &packet + 0 ) = 0x1b; // Represents 27 in base 10 or 00011011 in base 2.
|
||||
|
||||
// Create a UDP socket, convert the host-name to an IP address, set the port number,
|
||||
// connect to the server, send the packet, and then read in the return packet.
|
||||
struct sockaddr_in serv_addr; // Server address data structure.
|
||||
|
||||
sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); // Create a UDP socket.
|
||||
/* Create an UDP socket, use IP address of given nameserver, set the port number
|
||||
struct sockaddr_in serv_addr; // Server address data structure.
|
||||
add timout of 2 seconds
|
||||
*/
|
||||
struct sockaddr_in serv_addr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = inet_addr(ntpIp),
|
||||
.sin_port = htons( portno ),
|
||||
};
|
||||
|
||||
sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
|
||||
if ( sockfd < 0 ) {
|
||||
perror( "epicsNtpGetTime creating socket" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Zero out the server address structure.
|
||||
memset( ( char* ) &serv_addr, 0, sizeof( serv_addr ) );
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = inet_addr(ntpIp);
|
||||
|
||||
serv_addr.sin_port = htons( portno );
|
||||
|
||||
// Call up the server using its IP address and port number.
|
||||
if ( connect( sockfd, ( struct sockaddr * ) &serv_addr, sizeof( serv_addr) ) < 0 ) {
|
||||
perror( "epicsNtpGetTime connecting socket" );
|
||||
close( sockfd );
|
||||
/* Set receive timeout to 2 seconds */
|
||||
struct timeval tv = { .tv_sec = 2, .tv_usec = 0 };
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
|
||||
perror("setsockopt");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Send it the NTP packet it wants. If n == -1, it failed.
|
||||
n = write( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
|
||||
n = sendto(sockfd, ( char* ) &packet, sizeof( ntp_packet ), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
|
||||
if ( n < 0 ) {
|
||||
perror( "epicsNtpGetTime sending NTP request" );
|
||||
close( sockfd );
|
||||
@@ -65,13 +63,18 @@ int epicsNtpGetTime(char *ntpIp, struct timespec *now)
|
||||
}
|
||||
|
||||
// Wait and receive the packet back from the server. If n == -1, it failed.
|
||||
n = read( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
|
||||
if ( n < 0 ) {
|
||||
perror( "epicsNtpGetTime reading NTP reply" );
|
||||
n = recvfrom(sockfd, ( char* ) &packet, sizeof( ntp_packet ), 0, NULL, NULL);
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
printf("Timeout - no response\n");
|
||||
} else {
|
||||
perror( "epicsNtpGetTime reading NTP reply" );
|
||||
}
|
||||
close( sockfd );
|
||||
return -1;
|
||||
} else {
|
||||
printf("Received %zd bytes\n", n);
|
||||
}
|
||||
|
||||
close( sockfd );
|
||||
|
||||
// These two fields contain the time-stamp seconds as the packet left the NTP server.
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/** @page inithooks IOC lifecycle callback hooks.
|
||||
/** @file initHooks.h
|
||||
*
|
||||
* @section inithooks IOC lifecycle callback hooks.
|
||||
*
|
||||
* initHookRegister() allows external code to be called at certains points (initHookState)
|
||||
* during IOC startup and shutdown.
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
* \brief Unit test routines
|
||||
* \author Andrew Johnson
|
||||
*
|
||||
* @section unittest Unit test routines
|
||||
*
|
||||
* The unit test routines make it easy for a test program to generate output
|
||||
* that is compatible with the Test Anything Protocol and can thus be used with
|
||||
* Perl's automated Test::Harness as well as generating human-readable output.
|
||||
@@ -72,78 +74,40 @@
|
||||
* mechanism to generate its result outputs (from an epicsAtExit() callback
|
||||
* routine).
|
||||
*
|
||||
* ### IOC Testing
|
||||
* @see @ref dbunittest
|
||||
*
|
||||
* Some tests require the context of an IOC to be run. This conflicts with the
|
||||
* idea of running multiple tests within a test harness, as iocInit() is only
|
||||
* allowed to be called once, and some parts of the full IOC (e.g. the rsrv CA
|
||||
* server) can not be shut down cleanly. The function iocBuildIsolated() allows
|
||||
* to start an IOC without its Channel Access parts, so that it can be shutdown
|
||||
* quite cleanly using iocShutdown(). This feature is only intended to be used
|
||||
* from test programs, do not use it on production IOCs. After building the
|
||||
* IOC using iocBuildIsolated() or iocBuild(), it has to be started by calling
|
||||
* iocRun(). The suggested call sequence in a test program that needs to run the
|
||||
* IOC without Channel Access is:
|
||||
\code
|
||||
#include "iocInit.h"
|
||||
|
||||
MAIN(iocTest)
|
||||
{
|
||||
testPlan(0);
|
||||
testdbPrepare();
|
||||
testdbReadDatabase("<dbdname>.dbd", 0, 0);
|
||||
<dbdname>_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("some.db", 0, 0);
|
||||
... test code before iocInit(). eg. dbGetString() ...
|
||||
testIocInitOk();
|
||||
... test code with IOC running. eg. dbGet()
|
||||
testIocShutdownOk();
|
||||
testdbCleanup();
|
||||
return testDone();
|
||||
}
|
||||
\endcode
|
||||
|
||||
* The part from iocBuildIsolated() to iocShutdown() can be repeated to
|
||||
* execute multiple tests within one executable or harness.
|
||||
*
|
||||
* To make it easier to create a single test program that can be built for
|
||||
* both the embedded and workstation operating system harnesses, the header file
|
||||
* testMain.h provides a convenience macro MAIN() that adjusts the name of the
|
||||
* test program according to the platform it is running on: main() on
|
||||
* workstations and a regular function name on embedded systems.
|
||||
*
|
||||
* ### Example
|
||||
* @section unittestexample Example
|
||||
*
|
||||
* The following is a simple example of a test program using the epicsUnitTest
|
||||
* routines:
|
||||
\code
|
||||
#include <math.h>
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
MAIN(mathTest)
|
||||
{
|
||||
testPlan(3);
|
||||
testOk(sin(0.0) == 0.0, "Sine starts");
|
||||
testOk(cos(0.0) == 1.0, "Cosine continues");
|
||||
if (!testOk1(M_PI == 4.0*atan(1.0)))
|
||||
testDiag("4 * atan(1) = %g", 4.0 * atan(1.0));
|
||||
return testDone();
|
||||
}
|
||||
\endcode
|
||||
|
||||
* \code{.c}
|
||||
* #include <math.h>
|
||||
* #include "epicsUnitTest.h"
|
||||
* #include "testMain.h"
|
||||
*
|
||||
* MAIN(mathTest)
|
||||
* {
|
||||
* testPlan(3);
|
||||
* testOk(sin(0.0) == 0.0, "Sine starts");
|
||||
* testOk(cos(0.0) == 1.0, "Cosine continues");
|
||||
* if (!testOk1(M_PI == 4.0*atan(1.0)))
|
||||
* testDiag("4 * atan(1) = %g", 4.0 * atan(1.0));
|
||||
* return testDone();
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* The output from running the above program looks like this:
|
||||
\code
|
||||
1..3
|
||||
ok 1 - Sine starts
|
||||
ok 2 - Cosine continues
|
||||
ok 3 - M_PI == 4.0*atan(1.0)
|
||||
|
||||
Results
|
||||
=======
|
||||
Tests: 3
|
||||
Passed: 3 = 100%
|
||||
\endcode
|
||||
* \code
|
||||
* 1..3
|
||||
* ok 1 - Sine starts
|
||||
* ok 2 - Cosine continues
|
||||
* ok 3 - M_PI == 4.0*atan(1.0)
|
||||
*
|
||||
* Results
|
||||
* =======
|
||||
* Tests: 3
|
||||
* Passed: 3 = 100%
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#ifndef INC_epicsUnitTest_H
|
||||
|
||||
Reference in New Issue
Block a user