/* * 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 #include #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 +
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 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