47 std::list<PostSpec*>* postSpecs =
new std::list<PostSpec*>;
57 postSpecs->push_back(spec);
64 std::list<PostSpec*>* postSpecs =
new std::list<PostSpec*>;
76 postSpecs->push_back(spec);
162 std::string str =
utils::StringFormat(
"<trigger name=\"%s\" type=\"%s\" compid=\"%u\" interval=\"%u\" delay=\"%u\" from=\"%u\" to=\"%u\" tag=\"%u\" filtercount=\"%u\" signalcount=\"%u\" postcount=\"%u\" context=\"%s\" triggercontext=\"%s\"",
163 name,
type.toString().c_str(),
componentID,
interval,
delay,
from,
to,
tag,
filterCount,
signalCount,
postCount,
context.toString().c_str(),
triggerContext.toString().c_str());
175 str += rSpec->
toXML();
181 str += sSpec->
toXML();
187 str += qSpec->
toXML();
193 str += pSpec->
toXML();
196 str +=
"</trigger>\n";
221 std::string fxml = f.
toXML();
222 if (fxml.empty()) {
unittest::fail(
"Subscriptions test: FilterSpec toXML empty\n");
return false; }
225 if (!f2.
fromXML(fxml.c_str())) {
unittest::fail(
"Subscriptions test: FilterSpec fromXML failed\n");
return false; }
227 strcmp(f2.
key,
"fkey") != 0 || strcmp(f2.
value,
"fval") != 0) {
228 unittest::fail(
"Subscriptions test: FilterSpec round-trip mismatch\n");
return false;
235 std::string sxml = s.
toXML();
236 if (sxml.empty()) {
unittest::fail(
"Subscriptions test: SignalSpec toXML empty\n");
return false; }
239 if (!s2.
fromXML(sxml.c_str())) {
unittest::fail(
"Subscriptions test: SignalSpec fromXML failed\n");
return false; }
241 unittest::fail(
"Subscriptions test: SignalSpec round-trip mismatch\n");
return false;
257 std::string pxml = p.
toXML();
258 if (pxml.empty() || pxml.find(
"post1") == std::string::npos) {
259 unittest::fail(
"Subscriptions test: PostSpec toXML missing name\n");
return false;
264 if (added != 3) {
unittest::fail(
"Subscriptions test: addContentToMsg count != 3\n");
return false; }
265 if (msg.
getAsString(
"s_key") !=
"s_val") {
unittest::fail(
"Subscriptions test: addContentToMsg string mismatch\n");
return false; }
267 if (!msg.
getInt(
"i_key", iv) || iv != 42) {
unittest::fail(
"Subscriptions test: addContentToMsg int mismatch\n");
return false; }
269 if (!msg.
getDouble(
"f_key", dv) || dv < 1.4 || dv > 1.6) {
unittest::fail(
"Subscriptions test: addContentToMsg float mismatch\n");
return false; }
275 const uint16 nFilter = 1;
276 const uint16 nRetrieve = 1;
277 const uint16 nQuery = 1;
278 const uint16 nPost = 2;
279 const uint16 nSignal = 2;
288 char* buf = (
char*)malloc(total);
289 if (!buf) {
unittest::fail(
"Subscriptions test: malloc failed\n");
return false; }
290 memset(buf, 0, total);
328 if (trig->
getRetrieveSpec((uint32)0) != ret) {
unittest::fail(
"Subscriptions test: getRetrieveSpec(0) wrong ptr\n"); free(buf);
return false; }
329 if (trig->
getQuerySpec((uint32)0) != qry) {
unittest::fail(
"Subscriptions test: getQuerySpec(0) wrong ptr\n"); free(buf);
return false; }
330 if (trig->
getPostSpec((uint32)0) != &posts[0]) {
unittest::fail(
"Subscriptions test: getPostSpec(0) wrong ptr\n"); free(buf);
return false; }
331 if (trig->
getPostSpec((uint32)1) != &posts[1]) {
unittest::fail(
"Subscriptions test: getPostSpec(1) wrong ptr\n"); free(buf);
return false; }
332 if (trig->
getSignalSpec((uint32)0) != &sigs[0]) {
unittest::fail(
"Subscriptions test: getSignalSpec(0) wrong ptr\n"); free(buf);
return false; }
333 if (trig->
getSignalSpec((uint32)1) != &sigs[1]) {
unittest::fail(
"Subscriptions test: getSignalSpec(1) wrong ptr\n"); free(buf);
return false; }
336 if (trig->
getRetrieveSpec(nRetrieve) != NULL) {
unittest::fail(
"Subscriptions test: getRetrieveSpec out-of-range not NULL\n"); free(buf);
return false; }
337 if (trig->
getQuerySpec(nQuery) != NULL) {
unittest::fail(
"Subscriptions test: getQuerySpec out-of-range not NULL\n"); free(buf);
return false; }
338 if (trig->
getPostSpec(nPost) != NULL) {
unittest::fail(
"Subscriptions test: getPostSpec out-of-range not NULL\n"); free(buf);
return false; }
339 if (trig->
getSignalSpec(nSignal) != NULL) {
unittest::fail(
"Subscriptions test: getSignalSpec out-of-range not NULL\n"); free(buf);
return false; }
343 if (trig->
getRetrieveSpec(
"nope") != NULL) {
unittest::fail(
"Subscriptions test: getRetrieveSpec(bad name) not NULL\n"); free(buf);
return false; }
344 if (trig->
getQuerySpec(
"qry1") != qry) {
unittest::fail(
"Subscriptions test: getQuerySpec(name) miss\n"); free(buf);
return false; }
345 if (trig->
getQuerySpec(
"nope") != NULL) {
unittest::fail(
"Subscriptions test: getQuerySpec(bad name) not NULL\n"); free(buf);
return false; }
346 if (trig->
getPostSpec(
"postB") != &posts[1]) {
unittest::fail(
"Subscriptions test: getPostSpec(name) miss\n"); free(buf);
return false; }
347 if (trig->
getPostSpec(
"nope") != NULL) {
unittest::fail(
"Subscriptions test: getPostSpec(bad name) not NULL\n"); free(buf);
return false; }
348 if (trig->
getSignalSpec(
"sigA") != &sigs[0]) {
unittest::fail(
"Subscriptions test: getSignalSpec(name) miss\n"); free(buf);
return false; }
349 if (trig->
getSignalSpec(
"nope") != NULL) {
unittest::fail(
"Subscriptions test: getSignalSpec(bad name) not NULL\n"); free(buf);
return false; }
351 if (trig->
getPostSpec(
"POSTA") != &posts[0]) {
unittest::fail(
"Subscriptions test: getPostSpec name not case-insensitive\n"); free(buf);
return false; }
357 if (trig->
getPostSpec(t12) != &posts[0]) {
unittest::fail(
"Subscriptions test: getPostSpec(type 1.2) miss\n"); free(buf);
return false; }
358 if (trig->
getPostSpec(t13) != &posts[1]) {
unittest::fail(
"Subscriptions test: getPostSpec(type 1.3) miss\n"); free(buf);
return false; }
359 if (trig->
getPostSpec(t99) != NULL) {
unittest::fail(
"Subscriptions test: getPostSpec(type 9.9) not NULL\n"); free(buf);
return false; }
361 if (trig->
getSignalSpec(t45) != &sigs[0]) {
unittest::fail(
"Subscriptions test: getSignalSpec(type 4.5) miss\n"); free(buf);
return false; }
364 std::list<PostSpec*>* all = trig->
getPostSpecs((
const char*)NULL);
365 if (!all || all->size() != nPost) {
unittest::fail(
"Subscriptions test: getPostSpecs(NULL) wrong count\n");
delete all; free(buf);
return false; }
368 std::list<PostSpec*>* named = trig->
getPostSpecs(
"postA");
369 if (!named || named->size() != 1 || named->front() != &posts[0]) {
370 unittest::fail(
"Subscriptions test: getPostSpecs(name) wrong\n");
delete named; free(buf);
return false;
377 std::list<PostSpec*>* byType = trig->
getPostSpecs(wild);
378 if (!byType || byType->size() != 2) {
379 unittest::fail(
"Subscriptions test: getPostSpecs(wildcard type) wrong count\n");
delete byType; free(buf);
return false;
384 std::string xml = trig->
toXML();
385 if (xml.empty() || xml.find(
"mytrigger") == std::string::npos) {
386 unittest::fail(
"Subscriptions test: TriggerSpec toXML missing name\n"); free(buf);
return false;
388 if (xml.find(
"postA") == std::string::npos || xml.find(
"sigA") == std::string::npos) {
389 unittest::fail(
"Subscriptions test: TriggerSpec toXML missing sub-specs\n"); free(buf);
return false;
394 const uint32 iterations = 200000;
396 volatile uint64 sink = 0;
397 for (uint32 i = 0; i < iterations; i++) {
403 unittest::metric(
"name_lookup", (
double)(iterations * 2) / us * 1e6,
"ops/s",
true);
404 unittest::detail(
"trigger name lookups: %u in %.0f us\n", iterations * 2, us);
415 "Trigger/filter/retrieve/query/post/signal spec lookup and XML",
"core");
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Subscription, trigger, retrieval, query, post and signal specifications — the declarative wiring of a...
#define TRIGGERFILTER_EQUALS
String equality between entry value and filter value.
#define TRIGGERFILTER_HASKEY
Fire only if the message has the named key.
Small, dependency-free unit test harness used by all CMSDK object tests.
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool getDouble(const char *key, double &value)
getDouble(const char* key, double& value)
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
std::string getAsString(const char *key)
getAsString(const char* key)
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
std::string StringFormat(const char *format,...)
printf into a std::string.
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
static struct PsyType CTRL_TEST
void Register_Subscriptions_Tests()
Hierarchical message type identifier — the key used for publish/subscribe matching in Psyclone.
bool fromString(const char *text)
Parse dotted decimal notation (e.g.
bool setLevel(int i, uint16 value)
Set an individual hierarchy level.
One filter condition attached to a TriggerSpec: a test on a user entry of the candidate message.
char key[MAXKEYNAMELEN+1]
Name of the user entry to test.
char value[MAXVALUENAMELEN+1]
Comparison operand as text (parsed numerically for the numeric operations).
std::string toXML()
Serialise as a <filter .../> XML element.
bool fromXML(const char *xml)
Parse from an XML string containing a <filter .../> element.
uint8 filter
Filter operation, one of the TRIGGERFILTER_... codes.
uint64 time
Time operand in microseconds (used by TRIGGERFILTER_MAXAGE).
Specification of a message to post when a trigger fires (or a crank completes).
PsyType type
Type of the posted message.
uint32 addContentToMsg(DataMessage *msg)
Unpack the inline content entries into msg as typed user entries.
char name[MAXKEYNAMELEN+1]
Name of this post spec.
uint32 contentCount
Number of inline content entries packed into content.
char content[MAXCONTENTLEN+1]
Packed content entries: "||T||key||value" repeated (T = S, I or F).
uint32 to
Addressee component id (0 = publish to subscribers).
std::string toXML()
Serialise as a <post ...>...</post> XML element including content children.
Specification of a query against a catalog/service/feed, attached to a TriggerSpec.
char name[MAXKEYNAMELEN+1]
Name of this query, used to reference its results in the crank.
std::string toXML()
Serialise as a <query .../> XML element.
Specification of a whiteboard retrieval attached to a TriggerSpec.
char name[MAXKEYNAMELEN+1]
Name of this retrieve, used to reference its results in the crank.
std::string toXML()
Serialise as a <retrieve .../> XML element.
Specification of a signal (a typed notification without payload) emitted when a trigger fires.
PsyType type
Type of the signal to emit.
bool fromXML(const char *xml)
Parse from an XML string containing a <signal .../> element.
std::string toXML()
Serialise as a <signal .../> XML element.
char name[MAXKEYNAMELEN+1]
Name of this signal spec.
A complete trigger definition: what fires a component's crank and what happens then.
uint16 signalCount
Number of SignalSpecs in the tail.
uint16 filterCount
Number of FilterSpecs in the tail.
uint32 id
Unique id of this trigger.
uint32 tag
Only messages with this tag fire the trigger (0 = any).
PsyContext context
Context in which the trigger is active.
RetrieveSpec * getRetrieveSpec(uint32 n)
uint32 from
Only messages sent by this component id fire the trigger (0 = any).
PostSpec * getPostSpec(const char *name)
uint32 interval
Periodic trigger interval in ms (0 = message-triggered only).
bool reset(uint32 s=0)
Zero the header and stamp size/cid.
uint32 delay
Delay in ms between trigger match and crank execution.
uint16 queryCount
Number of QuerySpecs in the tail.
std::list< PostSpec * > * getPostSpecs(const char *name)
PsyContext triggerContext
Context the triggering message must match.
char name[MAXKEYNAMELEN+1]
Trigger name.
QuerySpec * getQuerySpec(uint32 n)
PsyType type
Message type that fires the trigger (wildcards allowed).
std::string toXML()
Serialise the whole trigger (with all attached specs) as XML.
uint16 postCount
Number of PostSpecs in the tail.
uint16 retrieveCount
Number of RetrieveSpecs in the tail.
static bool UnitTest()
Run the built-in self test.
uint32 componentID
Id of the component owning the trigger.
uint32 to
Only messages addressed to this component id fire the trigger (0 = any).
SignalSpec * getSignalSpec(const char *name)