cyclonedds/src/core/ddsc/tests/test_oneliner.h

327 lines
14 KiB
C
Raw Normal View History

/*
* Copyright(c) 2020 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 _TEST_ONELINER_H_
#define _TEST_ONELINER_H_
#include <stdint.h>
#include <setjmp.h>
#include "dds/dds.h"
#include "dds/ddsrt/sync.h"
/** @brief run a "test" consisting of a sequence of simplish operations
*
* This operation takes a test description, really a program in a bizarre syntax, and
* executes it. Any failures, be it because of error codes coming out of the Cyclone
* calls or expected values being wrong cause it to fail the test via CU_ASSERT_FATAL.
* While it is doing this, it outputs the test steps to stdout including some actual
* values. An invalid program is mostly reported by calling abort(). It is geared towards
* checking for listener invocations and the effects on statuses.
*
* Entities in play:
*
* - participants: P P' P''
* - subscribers: R R' R''
* - publishers: W W' W''
* - readers: r s t r' s' t' r'' s'' t''
* - writers: w x y w' x' y' w'' x'' y''
*
* The unprimed ones exist in domain 0, the primed ones in domain 1 (but configured such
* that it talks to domain 0), and the double-primed ones in domain 2 (again configured such
* that it talks to domain 0) so that network-related listener invocations can be checked
* as well.
*
* The first mention of an entity creates it as well as its ancestors. Implicitly created
* ancestors always have standard QoS and have no listeners. There is one topic that is
* created implicitly when the participant is created.
*
* Standard QoS is: default + reliable (100ms), by-source-timestamp, keep-all. The QoS of
* a reader/writer can be overridden at the first mention of it (i.e., when it is created)
* by appending a list of QoS overrides between parentheses.
*
* A program consists of a sequence of operations separated by whitespace, ';' or '/'
* (there is no meaning to the separators, they exist to allow visual grouping):
*
* PROGRAM ::= (OP (\s+|;)*)*
*
* OP ::= (LISTENER)* ENTITY-NAME[(QOS[,QOS[,QOS...]])]
*
* If entity ENTITY-NAME does not exist:
* creates the entity with the given listeners installed
* QOS can be used to override the standard QoS
* else
* changes the entity's listeners to the specified ones
* (see above for the valid ENTITY-NAMEs)
*
* | -ENTITY-NAME
*
* Deletes the specified entity
*
* | WRITE-LIKE[fail] ENTITY-NAME K[@DT]
* | WRITE-LIKE[fail] ENTITY-NAME (K,X,Y)[@DT]
*
* Writes/disposes/unregisters (K,0,0) (first form) or (K,X,Y). If
* "fail" is appended, the expectation is that it fails with a
* timeout, if @DT is appended, the timestamp is the start time of
* the test + <dt>s rather than the current time; DT is a
* floating-point number
*
* | READ-LIKE ENTITY-NAME
* | READ-LIKE(A,B) ENTITY-NAME
* | READ-LIKE{[S1[,S2[,S3...]][,...]} ENTITY-NAME
*
* Reads/takes at most 10 samples. The second form counts the
* number of valid and invalid samples seen and checks them against
* A and B.
*
* In the third form, the exact result set is given by the sample
* Si, which is a comma-separated list of samples:
*
* [STATE]K[ENTITY-NAME][@DT]
* [STATE](K,X,Y)[ENTITY-NAME][@DT]
*
* The first form is an invalid sample with only the (integer) key
* value K, the second form also specifies the two (integer)
* attribute fields.
*
* STATE specifies allowed sample (f - not-read (fresh), s - read
* (stale)), instance (a - alive, u - no-writers (unregistered) d -
* disposed) and view states (n - new, o - old). If no sample state
* is specified, all sample states are allowed, &c.
*
* ENTITY-NAME is the name of the publishing writer expected in the
* publication_handle. Not specifying a writer means any writer is
* ok. DT is the timestamp in the same manner as the write-like
* operations. Not specifying a timestamp means any timestamp is
* ok.
*
* If the expected set ends up with "..." there may be other samples
* in the result as well.
*
* | ?LISTENER[(ARGS)] ENTITY-NAME
*
* Waits until the specified listener has been invoked on <entity
* name> using a flag set by the listener function, resets the flag
* and verifies that neither the entity status bit nor the "change"
* fields in the various statuses were set.
*
* ARGS is used to check the status argument most recently passed to
* the listener:
*
* da(A) verifies that it has been invoked A times
* dor(A) see da
* it(A,B) verifies count and change match A and B, policy
* matches RELIABILITY
* lc(A,B,C,D,E) verifies that alive and not-alive counts match A
* and B, that alive and not-alive changes match C and D
* and that the last handle matches E if an entity name
* (ignored if E = "*")
* ll (A,B) verifies count and change match A and B
* odm (A,B) verifies count and change match A and B, last handle
* is ignored
* oiq (A,B,C) verifies that total count and change match A and B
* and that the mismatching QoS is C (using the same
* abbreviations as used for defining QoS on entity
* creation)
* pm (A,B,C,D,E) verifies that total count and change match A and
* B, that current count and change match C and D and
* that the last handle matches E if an entity name
* (ignored if E = "*")
* rdm see odm
* riq see oiq
* sl (A,B) verifies that total count and change match A and B
* sr (A,B,C) verifies total count and change match A and B, and
* that the reason matches C (one of "s" for samples,
* "i" for instances, "spi" for samples per instance)
* sm see pm
*
* | ?!LISTENER
*
* (Not listener) tests that LISTENER has not been invoked since
* last reset
*
* | sleep D
*
* Delay program execution for D s (D is a floating-point number)
*
* | deaf ENTITY-NAME
* | hearing ENTITY-NAME
*
* Makes the domain wherein the specified entity exists deaf,
* respectively restoring hearing. The entity must be either P or
* P' and both must exist. Plays some tricks to speed up lease
* expiry and reconnection (like forcibly deleting a proxy
* participant or triggering the publication of SPDP packets).
*
* WRITE-LIKE ::= wr write
* | wrdisp write-dispose
* | disp dispose
* | unreg unregister
*
* READ-LIKE ::= read dds_read (so any state)
* | take dds_take (so any state)
*
* LISTENER ::= da data available (acts on a reader)
* | dor data on readers (acts on a subcsriber)
* | it incompatible topic (acts on a topic)
* | lc liveliness changed (acts on a reader)
* | ll liveliness lost (acts on a writer)
* | odm offered deadline missed (acts on a writer)
* | oiq offered incompatible QoS (acts on a writer)
* | pm publication matched (acts on a writer)
* | rdm requested deadline missed (acts on a reader)
* | riq requested incompatible QoS (acts on a reader)
* | sl sample lost (acts on a reader)
* | sr sample rejected (acts on a reader)
* | sm subscription matched (acts on a reader)
*
* QOS ::= ad={y|n} auto-dispose unregistered instances
* | d={v|tl|t|p} durability
* | dl={inf|DT} deadline (infinite or DT seconds)
* | ds=DT/H/RL durability service: cleanup delay, history,
* resource limits
* | do={r|s} by-reception or by-source destination order
* | h={N|all} history keep-last-N or keep-all
* | lb={inf|DT} latency budget
* | ll={a[:DT]|p:DT|w:DT} liveliness (automatic, manual by
* participant, manual by topic)
* | ls={inf|DT} lifespan
* | o={s|x[:N]} ownership shared or exclusive (strength N)
* | p={i|t|g} presentation: instance, coherent-topic or
* coherent-group
* | r={be|r[:DT]} best-effort or reliable (with max blocking time)
* | rl=N[/N[/N]] resource limits (sample, instances, samples per
* instance; "inf" is allowed, ommitted ones are
* unlimited)
* | tp=N transport-priority
* | ud=... user data (with escape sequences and hex/octal
* input allowed)
*
* All entities share the listeners with their global state. Only the latest invocation is visible.
*
* @param[in] ops Program to execute.
*
* @return > 0 success, 0 failure, < 0 invalid input
*/
int test_oneliner (const char *ops);
union oneliner_tokval {
int i;
int64_t d;
char n[32];
};
struct oneliner_lex {
const char *inp;
dds_time_t tref;
int tok;
union oneliner_tokval v;
};
struct oneliner_ctx;
struct oneliner_cb {
struct oneliner_ctx *ctx;
dds_listener_t *list;
uint32_t cb_called[DDS_STATUS_ID_MAX + 1];
dds_entity_t cb_topic, cb_writer, cb_reader, cb_subscriber;
dds_inconsistent_topic_status_t cb_inconsistent_topic_status;
dds_liveliness_changed_status_t cb_liveliness_changed_status;
dds_liveliness_lost_status_t cb_liveliness_lost_status;
dds_offered_deadline_missed_status_t cb_offered_deadline_missed_status;
dds_offered_incompatible_qos_status_t cb_offered_incompatible_qos_status;
dds_publication_matched_status_t cb_publication_matched_status;
dds_requested_deadline_missed_status_t cb_requested_deadline_missed_status;
dds_requested_incompatible_qos_status_t cb_requested_incompatible_qos_status;
dds_sample_lost_status_t cb_sample_lost_status;
dds_sample_rejected_status_t cb_sample_rejected_status;
dds_subscription_matched_status_t cb_subscription_matched_status;
};
struct oneliner_ctx {
struct oneliner_lex l;
dds_entity_t es[3 * 9];
dds_entity_t tps[3];
dds_entity_t doms[3];
dds_instance_handle_t esi[3 * 9];
// built-in topic readers for cross-referencing instance handles
dds_entity_t pubrd[3];
dds_entity_t subrd[3];
// topic name used for data
char topicname[100];
const dds_qos_t *qos;
dds_qos_t *rwqos;
int result;
char msg[256];
jmp_buf jb;
ddsrt_mutex_t g_mutex;
ddsrt_cond_t g_cond;
struct oneliner_cb cb[3];
};
/** @brief Initialize a "oneliner test" context
*
* @param[out] ctx context to initialize
*/
void test_oneliner_init (struct oneliner_ctx *ctx);
/** @brief Run a sequence of operations in an initialized context
*
* If the context indicates a preceding step has failed, this is a
* no-op and the previous result is propagated to the return value.
*
* @param[in,out] ctx context to operate in
* @param[in] ops sequence of operations to execute (@ref test_oneliner)
*
* @return integer indicating success or failure
*
* @retval 1 success
* @retval 0 test failure
* @retval <0 syntax error unexpected error
*/
int test_oneliner_step (struct oneliner_ctx *ctx, const char *ops);
/** @brief Get a pointer to the error message from a "oneliner test"
*
* If a preceding step has failed, this returns a pointer to a message
* containing some information about the failure. If no error
* occurred, the message is meaningless.
*
*
* @param[in] ctx context to retrieve message from
*
* @return pointer to null-terminated string aliasing a string in ctx
*/
const char *test_oneliner_message (const struct oneliner_ctx *ctx);
/** @brief Deinitialize a "oneliner test" context
*
* This releases all resources used by the context.
*
* @param[in,out] ctx context to operate in
*
* @return integer indicating success or failure in any of the
* preceding steps. If no steps were taken, the result is success.
*
* @retval 1 success
* @retval 0 test failure
* @retval <0 syntax error unexpected error
*/
int test_oneliner_fini (struct oneliner_ctx *ctx);
#endif