Lumiera  0.pre.03
»edit your freedom«
event-log.cpp
Go to the documentation of this file.
1 /*
2  EventLog - test facility to verify the occurrence of expected events
3 
4  Copyright (C)
5  2018, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
37 #include "lib/error.hpp"
38 #include "lib/test/event-log.hpp"
39 
40 
41 
42 namespace lib {
43 namespace test{
44  namespace error = lumiera::error;
45 
46  using util::stringify;
47  using util::contains;
48  using util::isnil;
49  using lib::Symbol;
50  using std::string;
51 
52 
53  namespace { /* == elementary matchers == */
54 
55  using Filter = decltype( buildSearchFilter (std::declval<Log const&>()) );
56 
58  using RExSeq = std::vector<std::regex>;
59 
60 
61  inline auto
62  find (string match)
63  {
64  return [=](Entry const& entry)
65  {
66  return contains (string(entry), match);
67  };
68  }
69 
70  inline auto
71  findRegExp (string regExpDef)
72  {
73  std::regex regExp(regExpDef);
74  return [=](Entry const& entry)
75  {
76  return std::regex_search(string(entry), regExp);
77  };
78  }
79 
80  inline auto
81  findEvent (string match)
82  {
83  return [=](Entry const& entry)
84  {
85  return ( entry.getType() == "event"
86  or entry.getType() == "error"
87  or entry.getType() == "create"
88  or entry.getType() == "destroy"
89  or entry.getType() == "logJoin"
90  )
91  and not isnil(entry.scope())
92  and contains (*entry.scope(), match);
93  };
94  }
95 
96  inline auto
97  findEvent (string classifier, string match)
98  {
99  return [=](Entry const& entry)
100  {
101  return ( entry.getType() == classifier
102  or (entry.hasAttribute("ID") and contains (entry.get("ID"), classifier))
103  )
104  and not isnil(entry.scope())
105  and contains (*entry.scope(), match);
106  };
107  }
108 
109  inline auto
110  findCall (string match)
111  {
112  return [=](Entry const& entry)
113  {
114  return entry.getType() == "call"
115  and contains (entry.get("fun"), match);
116  };
117  }
118 
119 
128  inline auto
129  matchArguments (ArgSeq&& argSeq)
130  {
131  return [=](Entry const& entry)
132  {
133  auto scope = entry.scope();
134  for (auto const& match : argSeq)
135  if (isnil (scope) or not contains(*scope, match))
136  return false;
137  else
138  ++scope;
139 
140  return isnil(scope); // must be exhausted by now
141  }; // otherwise the sizes do not match...
142  }
143 
145  inline auto
146  matchArgument (size_t idx, string match)
147  {
148  return [=](Entry const& entry)
149  {
150  return idx < entry.childSize()
151  and contains (entry.child(idx), match);
152  };
153  }
154 
155 
165  inline auto
166  matchArgsRegExp (RExSeq&& regExpSeq)
167  {
168  return [=](Entry const& entry)
169  {
170  auto scope = entry.scope();
171  for (auto const& rex : regExpSeq)
172  {
173  if (isnil (scope)) return false;
174  while (scope and std::regex_search(*scope, rex))
175  ++scope;
176  }
177 
178  return isnil(scope); // must be exhausted by now
179  }; // otherwise we didn't get full coverage...
180  }
181 
182 
184  inline auto
185  matchType (string typeID)
186  {
187  return [=](Entry const& entry)
188  {
189  return contains (entry.getType(), typeID);
190  };
191  }
192 
193 
195  inline auto
196  ensureAttribute (string key)
197  {
198  return [=](Entry const& entry)
199  {
200  return entry.hasAttribute(key);
201  };
202  }
203 
204 
206  inline auto
207  matchAttribute (string key, string valueMatch)
208  {
209  return [=](Entry const& entry)
210  {
211  return entry.hasAttribute(key)
212  and contains (entry.get(key), valueMatch);
213  };
214  }
215 
216 
217 
218  /* === configure the underlying search engine === */
219 
220  enum Direction {
221  FORWARD, BACKWARD, CURRENT
222  };
223 
224  template<typename COND>
225  inline void
226  attachNextSerchStep (Filter& solution, COND&& searchCond, Direction direction)
227  {
228  if (CURRENT == direction)
229  solution.search (forward<COND> (searchCond));
230  else
231  solution.addStep ([predicate{forward<COND> (searchCond)}, direction]
232  (auto& filter)
233  {
234  filter.reverse (BACKWARD == direction);
235  filter.disableFilter(); // deactivate any filtering temporarily
236  ++filter; // move one step in the required direction
237  filter.setNewFilter (predicate);
238  });
239  }
240 
241  template<typename COND>
242  inline void
243  refineSerach (Filter& solution, COND&& additionalCond)
244  {
245  solution.addStep ([predicate{forward<COND> (additionalCond)}]
246  (auto& filter)
247  {
248  filter.andFilter (predicate);
249  });
250  }
251 
252  }//(End) Elementary Matchers and handling of the search engine
253 
254 
255 
256 
257 
258  EventMatch::EventMatch (Log const& srcSeq)
259  : solution_{buildSearchFilter (srcSeq)}
260  , lastMatch_{"HEAD "+ solution_->get("this")}
261  , look_for_match_{true}
262  , violation_{}
263  { }
264 
265 
266 
271  bool
273  {
274  return not isnil (solution_);
275  }
276 
277 
287  void
288  EventMatch::evaluateQuery (string matchSpec, Literal rel)
289  {
290  if (look_for_match_ and not isnil (violation_)) return;
291  // already failed, no further check necessary
292 
293  if (foundSolution()) // NOTE this pulls the filter
294  {
295  lastMatch_ = matchSpec+" @ "+string(*solution_)
296  + (isnil(lastMatch_)? ""
297  : "\n.."+rel+" "+lastMatch_);
298  if (not look_for_match_)
299  violation_ = "FOUND at least "+lastMatch_;
300  }
301  else
302  {
303  if (look_for_match_)
304  violation_ = "FAILED to "+matchSpec
305  + "\n.."+rel
306  + " "+lastMatch_;
307  else
308  violation_ = "";
309  }
310  }
311 
312 
313 
320  EventMatch&
321  EventMatch::locate (string match)
322  {
323  attachNextSerchStep (solution_, find(match), CURRENT);
324  evaluateQuery ("match(\""+match+"\")");
325  return *this;
326  }
327 
331  EventMatch&
332  EventMatch::locateMatch (string regExp)
333  {
334  attachNextSerchStep (solution_, findRegExp(regExp), CURRENT);
335  evaluateQuery ("find-RegExp(\""+regExp+"\")");
336  return *this;
337  }
338 
344  EventMatch&
345  EventMatch::locateEvent (string match)
346  {
347  attachNextSerchStep (solution_, findEvent(match), CURRENT);
348  evaluateQuery ("match-event(\""+match+"\")");
349  return *this;
350  }
351 
352  EventMatch&
353  EventMatch::locateEvent (string classifier, string match)
354  {
355  attachNextSerchStep (solution_, findEvent(classifier,match), CURRENT);
356  evaluateQuery ("match-event(ID=\""+classifier+"\", \""+match+"\")");
357  return *this;
358  }
359 
364  EventMatch&
365  EventMatch::locateCall (string match)
366  {
367  attachNextSerchStep (solution_, findCall(match), CURRENT);
368  evaluateQuery ("match-call(\""+match+"\")");
369  return *this;
370  }
371 
372 
373 
374 
383  EventMatch&
384  EventMatch::before (string match)
385  {
386  attachNextSerchStep (solution_, find(match), FORWARD);
387  evaluateQuery ("match(\""+match+"\")");
388  return *this;
389  }
390 
394  EventMatch&
395  EventMatch::beforeMatch (string regExp)
396  {
397  attachNextSerchStep (solution_, findRegExp(regExp), FORWARD);
398  evaluateQuery ("find-RegExp(\""+regExp+"\")");
399  return *this;
400  }
401 
416  EventMatch&
417  EventMatch::beforeEvent (string match)
418  {
419  attachNextSerchStep (solution_, findEvent(match), FORWARD);
420  evaluateQuery ("match-event(\""+match+"\")");
421  return *this;
422  }
423 
424  EventMatch&
425  EventMatch::beforeEvent (string classifier, string match)
426  {
427  attachNextSerchStep (solution_, findEvent(classifier,match), FORWARD);
428  evaluateQuery ("match-event(ID=\""+classifier+"\", \""+match+"\")");
429  return *this;
430  }
431 
437  EventMatch&
438  EventMatch::beforeCall (string match)
439  {
440  attachNextSerchStep (solution_, findCall(match), FORWARD);
441  evaluateQuery ("match-call(\""+match+"\")");
442  return *this;
443  }
444 
445 
446 
447 
453  EventMatch&
454  EventMatch::after (string match)
455  {
456  attachNextSerchStep (solution_, find(match), BACKWARD);
457  evaluateQuery ("match(\""+match+"\")", "before");
458  return *this;
459  }
460 
461  EventMatch&
462  EventMatch::afterMatch (string regExp)
463  {
464  attachNextSerchStep (solution_, findRegExp(regExp), BACKWARD);
465  evaluateQuery ("find-RegExp(\""+regExp+"\")", "before");
466  return *this;
467  }
468 
469  EventMatch&
470  EventMatch::afterEvent (string match)
471  {
472  attachNextSerchStep (solution_, findEvent(match), BACKWARD);
473  evaluateQuery ("match-event(\""+match+"\")", "before");
474  return *this;
475  }
476 
477  EventMatch&
478  EventMatch::afterEvent (string classifier, string match)
479  {
480  attachNextSerchStep (solution_, findEvent(classifier,match), BACKWARD);
481  evaluateQuery ("match-event(ID=\""+classifier+"\", \""+match+"\")", "before");
482  return *this;
483  }
484 
488  EventMatch&
489  EventMatch::afterCall (string match)
490  {
491  attachNextSerchStep (solution_, findCall(match), BACKWARD);
492  evaluateQuery ("match-call(\""+match+"\")", "before");
493  return *this;
494  }
495 
496 
497 
501  void
502  EventMatch::refineSerach_matchArgument (size_t idx, string match)
503  {
504  refineSerach (solution_, matchArgument(idx, match));
505  evaluateQuery ("match-argument(["+util::toString(idx)+"]="+match+")");
506  }
507 
511  void
513  {
514  string argList(util::join(argSeq));
515  refineSerach (solution_, matchArguments(move(argSeq)));
516  evaluateQuery ("match-arguments("+argList+")");
517  }
518 
526  void
527  EventMatch::refineSerach_matchArgsRegExp (RExSeq&& regExpSeq, string rendered_regExps)
528  {
529  refineSerach (solution_, matchArgsRegExp (move (regExpSeq)));
530  evaluateQuery ("match-args-RegExp("+rendered_regExps+")");
531  }
532 
533 
534 
538  EventMatch&
539  EventMatch::type (string typeID)
540  {
541  refineSerach (solution_, matchType(typeID));
542  evaluateQuery ("match-type("+typeID+")");
543  return *this;
544  }
545 
549  EventMatch&
551  {
552  refineSerach (solution_, ensureAttribute(key));
553  evaluateQuery ("ensure-attribute("+key+")");
554  return *this;
555  }
556 
560  EventMatch&
561  EventMatch::attrib (string key, string valueMatch)
562  {
563  refineSerach (solution_, matchAttribute(key,valueMatch));
564  evaluateQuery ("match-attribute("+key+"=\""+valueMatch+"\")");
565  return *this;
566  }
567 
571  EventMatch&
572  EventMatch::id (string classifier)
573  {
574  refineSerach (solution_, matchAttribute("ID",classifier));
575  evaluateQuery ("match-ID(\""+classifier+"\")");
576  return *this;
577  }
578 
582  EventMatch&
583  EventMatch::on (string targetID)
584  {
585  refineSerach (solution_, matchAttribute("this",targetID));
586  evaluateQuery ("match-this(\""+targetID+"\")");
587  return *this;
588  }
589 
590  EventMatch&
591  EventMatch::on (const char* targetID)
592  {
593  refineSerach (solution_, matchAttribute("this",targetID));
594  evaluateQuery ("match-this(\""+string(targetID)+"\")");
595  return *this;
596  }
597 
598 
599 
600 
601 
602  /* ==== EventLog implementation ==== */
603 
604  EventLog::EventLog (string logID)
605  : log_(new Log)
606  {
607  log({"type=EventLogHeader", "this="+logID});
608  }
609 
610 
624  EventLog&
626  {
627  Log& target = *otherLog.log_;
628  target.reserve (target.size() + log_->size() + 1);
629  target.emplace_back (log_->front());
630  auto p = log_->begin();
631  while (++p != log_->end()) // move our log's content into the other log
632  target.emplace_back (std::move(*p));
633  this->log_->resize(1);
634  this->log({"type=joined", otherLog.getID()}); // leave a tag to indicate
635  otherLog.log({"type=logJoin", this->getID()}); // where the `joinInto` took place,
636  this->log_ = otherLog.log_; // connect this to the other storage
637  return *this;
638  }
639 
640 
641 
642  EventLog&
644  {
645  string originalLogID = this->getID();
646  return this->clear (originalLogID);
647  }
648 
657  EventLog&
658  EventLog::clear (string alteredLogID)
659  {
660  log_.reset (new Log);
661  log({"type=EventLogHeader", "this="+alteredLogID});
662  return *this;
663  }
664 
665  EventLog&
666  EventLog::clear (const char* alteredLogID)
667  {
668  return clear (string{alteredLogID});
669  }
670 
671 
672 
673  /* ==== Logging API ==== */
674 
675  EventLog&
676  EventLog::event (string text)
677  {
678  log ("event", ArgSeq{}, ArgSeq{text}); // we use this ctor variant to ensure
679  return *this; // that text is not misinterpreted as attribute,
680  } // which might happen when text contains a '='
681 
682  EventLog&
683  EventLog::event (string classifier, string text)
684  {
685  log ("event", ArgSeq{"ID="+classifier}, ArgSeq{text});
686  return *this;
687  }
688 
689  EventLog&
690  EventLog::call (string target, string function)
691  {
692  return call(target, function, ArgSeq());
693  }
694 
695  EventLog&
696  EventLog::call (string target, string function, ArgSeq&& args)
697  {
698  log ("call", ArgSeq{"fun="+function, "this="+target}, std::forward<ArgSeq>(args));
699  return *this;
700  }
701 
702  EventLog&
703  EventLog::call (const char* target, const char* function, ArgSeq&& args)
704  {
705  return call (string(target), string(function), std::forward<ArgSeq>(args));
706  }
707 
708 
709  EventLog&
710  EventLog::warn (string text)
711  {
712  log({"type=warn", text});
713  return *this;
714  }
715 
716  EventLog&
717  EventLog::error (string text)
718  {
719  log({"type=error", text});
720  return *this;
721  }
722 
723  EventLog&
724  EventLog::fatal (string text)
725  {
726  log({"type=fatal", text});
727  return *this;
728  }
729 
730 
731 
732  EventLog&
733  EventLog::create (string text)
734  {
735  log({"type=create", text});
736  return *this;
737  }
738 
739  EventLog&
740  EventLog::destroy (string text)
741  {
742  log({"type=destroy", text});
743  return *this;
744  }
745 
746 
747 
748 
749  /* ==== Query/Verification API ==== */
750 
751  EventMatch
752  EventLog::verify (string match) const
753  {
754  EventMatch matcher(*log_);
755  matcher.locate (match); // new matcher starts linear search from first log element
756  return matcher;
757  }
758 
759 
760  EventMatch
761  EventLog::verifyMatch (string regExp) const
762  {
763  EventMatch matcher(*log_);
764  matcher.locateMatch (regExp);
765  return matcher;
766  }
767 
768 
769  EventMatch
770  EventLog::verifyEvent (string match) const
771  {
772  EventMatch matcher(*log_);
773  matcher.locateEvent (match);
774  return matcher;
775  }
776 
777 
778  EventMatch
779  EventLog::verifyEvent (string classifier, string match) const
780  {
781  EventMatch matcher(*log_);
782  matcher.locateEvent (classifier, match);
783  return matcher;
784  }
785 
786 
787  EventMatch
788  EventLog::verifyCall (string match) const
789  {
790  EventMatch matcher(*log_);
791  matcher.locateCall (match);
792  return matcher;
793  }
794 
795 
804  EventMatch
805  EventLog::ensureNot (string match) const
806  {
807  EventMatch matcher(*log_);
808  matcher.look_for_match_ = false; // flip logic; fail if match succeeds
809  matcher.locate (match);
810  return matcher;
811  }
812 
813 
814 
815 }} // namespace lib::test
void refineSerach_matchArgument(size_t idx, string match)
Definition: event-log.cpp:502
EventMatch & type(string typeID)
refine filter to additionally require a matching log entry type
Definition: event-log.cpp:539
EventLog & event(string text)
log some text as event
Definition: event-log.cpp:676
EventMatch & locateCall(string match)
basic search for some specific function invocation
Definition: event-log.cpp:365
auto ensureAttribute(string key)
refinement filter to ensure a specific attribute is present on the log entry
Definition: event-log.cpp:196
string violation_
record when the underlying query has failed
Definition: event-log.hpp:138
Filter solution_
match predicate evaluator
Definition: event-log.hpp:129
EventLog & warn(string text)
Log a warning entry.
Definition: event-log.cpp:710
Support for verifying the occurrence of events from unit tests.
bool filter(Placement< DummyMO > const &candidate)
a filter predicate to pick some objects from a resultset.
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Definition: run.hpp:40
EventMatch & beforeCall(string match)
find a match for some function invocation after the current point of reference
Definition: event-log.cpp:438
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
auto stringify(IT &&src)
convert to string as transforming step in a pipeline
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:275
EventMatch & beforeMatch(string regExp)
find a match with the given regular expression
Definition: event-log.cpp:395
EventLog & create(string text)
Log the creation of an object.
Definition: event-log.cpp:733
EventMatch & after(string match)
find a match (substring match) of the given text in an EventLog entry before the current position...
Definition: event-log.cpp:454
EventMatch & locateMatch(string regExp)
basic search like locate() but with the given regular expression
Definition: event-log.cpp:332
EventMatch verifyEvent(string match) const
start a query to match for some event.
Definition: event-log.cpp:770
EventMatch & afterCall(string match)
find a function invocation backwards, before the current point of reference
Definition: event-log.cpp:489
Implementation namespace for support and library code.
auto matchArgsRegExp(RExSeq &&regExpSeq)
refinement filter, to cover all arguments by regular expression(s)
Definition: event-log.cpp:166
void evaluateQuery(string matchSpec, Literal rel="after")
this is actually called after each refinement of the filter and matching conditions.
Definition: event-log.cpp:288
EventMatch & attrib(string key, string valueMatch)
refine filter to additionally match on a specific attribute
Definition: event-log.cpp:561
EventMatch(Log const &srcSeq)
Definition: event-log.cpp:258
auto matchArgument(size_t idx, string match)
refinement filter to match a specific positional argument
Definition: event-log.cpp:146
Token or Atom with distinct identity.
Definition: symbol.hpp:117
EventMatch verifyCall(string match) const
start a query to match especially a function call
Definition: event-log.cpp:788
EventMatch ensureNot(string match) const
start a query to ensure the given expression does not match.
Definition: event-log.cpp:805
auto matchAttribute(string key, string valueMatch)
refinement filter to ensure a specific attribute is present on the log entry
Definition: event-log.cpp:207
bool foundSolution()
core of the evaluation machinery: apply a filter predicate and then pull through the log to find a ac...
Definition: event-log.cpp:272
EventMatch & beforeEvent(string match)
find a match for an "event" after the current point of reference
Definition: event-log.cpp:417
EventLog & clear()
purge log contents while retaining just the original Header-ID
Definition: event-log.cpp:643
auto matchArguments(ArgSeq &&argSeq)
this filter functor is for refinement of an existing filter
Definition: event-log.cpp:129
EventLog & call(string target, string function)
Log occurrence of a function call with no arguments.
Definition: event-log.cpp:690
EventLog & joinInto(EventLog &otherLog)
Merge this log into another log, forming a combined log.
Definition: event-log.cpp:625
bool look_for_match_
support for positive and negative queries.
Definition: event-log.hpp:135
EventMatch & id(string classifier)
refine filter to additionally match on the ID attribute
Definition: event-log.cpp:572
void refineSerach_matchArguments(ArgSeq &&argSeq)
Definition: event-log.cpp:512
Lumiera error handling (C++ interface).
EventMatch & locate(string match)
basic search function: continue linear lookup over the elements of the EventLog to find a match (subs...
Definition: event-log.cpp:321
EventLog & fatal(string text)
Log a fatal failure.
Definition: event-log.cpp:724
EventMatch verify(string match) const
start a query to match for some substring.
Definition: event-log.cpp:752
void refineSerach_matchArgsRegExp(RExSeq &&regExpSeq, string rendered_regExps)
Definition: event-log.cpp:527
EventLog & destroy(string text)
Log the destruction of an object.
Definition: event-log.cpp:740
EventMatch verifyMatch(string regExp) const
start a query to match with a regular expression
Definition: event-log.cpp:761
EventMatch & on(string targetID)
refine filter to additionally match the &#39;this&#39; attribute
Definition: event-log.cpp:583
auto matchType(string typeID)
refinement filter to match on the given typeID
Definition: event-log.cpp:185
EventMatch & key(string key)
refine filter to additionally require the presence an attribute
Definition: event-log.cpp:550
string lastMatch_
record last match for diagnostics
Definition: event-log.hpp:132
EventMatch & before(string match)
find a match (substring match) of the given text in an EventLog entry after the current position ...
Definition: event-log.cpp:384
string getID() const
Definition: event-log.hpp:317
bool contains(SEQ const &cont, typename SEQ::const_reference val)
shortcut for brute-force containment test in any sequential container
Definition: util.hpp:255
EventMatch & locateEvent(string match)
basic search for a matching "event"
Definition: event-log.cpp:345
EventLog & error(string text)
Log an error note.
Definition: event-log.cpp:717