Activity-Lang: support negative assertions for tests

Testcase (detect function invocation) passes now as expected


Some Library / Framework changes

- rename event-log-test.cpp
- allow the ExpectString also to work with concatenated expectation strings


Remark: there was a warning in the comment in event-log.hpp,
pointing out that negative assertions are shallow.

However, after the rework in 9/2018 (commit: d923138d1)
...this should no longer be true, since we perform proper backtracking,
leading to an exhaustive search.
This commit is contained in:
Fischlurch 2023-08-14 19:22:18 +02:00
parent 25ad461a28
commit e3f1aa4f7c
8 changed files with 136 additions and 31 deletions

View file

@ -35,7 +35,7 @@
** (non-inline) functions and package all the actual implementation
** code into this dedicated translation unit.
**
** @see TestEventLog_test
** @see EventLog_test
** @see IterChainSearch_test
** @see iter-explorer.hpp
**
@ -785,10 +785,9 @@ namespace test{
* @remarks the query expression is built similar to the other queries,
* but the logic of evaluation is flipped: whenever we find any match
* the overall result (when evaluating to `bool`) will be `false`.
* @warning this is not an proper exhaustive negation, since the implementation
* does not proper backtracking with a stack of choices. This becomes
* evident, when you combine `ensureNot()` with a switch in search
* direction, like e.g. using `afterXXX` at some point in the chain.
* @note since the chained search involves backtracking, this should perform
* an exhaustive negation, searching for a possible match until no
* further search options are left.
*/
EventMatch
EventLog::ensureNot (string match) const

View file

@ -69,7 +69,7 @@
** diagnostics. This means: the sequence of matches you see in this diagnostic
** output is not necessarily the last match patch, which lead to the final failure
**
** @see TestEventLog_test
** @see EventLog_test
** @see [usage example](\ref AbstractTangible_test)
**
*/
@ -268,7 +268,7 @@ namespace test{
* [information records](\ref lib::Record) into a possibly shared (vector)
* buffer in heap storage. An extended query DSL allows to write
* assertions to cover the occurrence of events in unit tests.
* @see TestEventLog_test
* @see EventLog_test
*/
class EventLog
{
@ -321,7 +321,7 @@ namespace test{
/** Merge this log into another log, forming a combined log
* @param otherLog target to integrate this log's contents into.
* @return reference to the merged new log instance
* @see TestEventLog_test::verify_logJoining()
* @see EventLog_test::verify_logJoining()
*/
EventLog& joinInto (EventLog& otherLog);

View file

@ -302,6 +302,12 @@ namespace test{
return expected.verify (actual);
}
friend ExpectString
operator+ (std::string&& l, ExpectString&& r)
{
return ExpectString{(l+r).c_str()};
}
bool verify (std::string const& actual) const;
};

View file

@ -20,14 +20,14 @@ return: 0
END
TEST "Helper for event registration and verification" TestEventLog_test <<END
TEST "Helper for event registration and verification" EventLog_test <<END
err-lit: __Log_condition_violated__
err-lit: FAILED to match("γ")
err: ..after HEAD TestEventLog_test.+
err: ..after HEAD EventLog_test.+
err-lit: __Log_condition_violated__
err-lit: FAILED to match("α")
err-lit: ..after match("β") @ Rec(event|{β})
err: ..after HEAD TestEventLog_test.+
err: ..after HEAD EventLog_test.+
err-lit: __Log_condition_violated__
err-lit: FAILED to match("ham")
err-lit: ..before match("beans") @ Rec(EventLogHeader| this = baked beans )

View file

@ -1,5 +1,5 @@
/*
TestEventLog(Test) - helper for event registration and verification
EventLog(Test) - helper for event registration and verification
Copyright (C) Lumiera.org
2015, Hermann Vosseler <Ichthyostega@web.de>
@ -20,8 +20,8 @@
* *****************************************************/
/** @file test-event-log-test.cpp
** unit test \ref TestEventLog_test
/** @file event-log-test.cpp
** unit test \ref EventLog_test
*/
@ -58,7 +58,7 @@ namespace test{
* printed to STDERR
* @see event-log.hpp
*/
class TestEventLog_test : public Test
class EventLog_test : public Test
{
void
run (Arg)
@ -384,7 +384,7 @@ namespace test{
}
};
LAUNCHER (TestEventLog_test, "unit common");
LAUNCHER (EventLog_test, "unit common");
}}} // namespace lib::test::test

View file

@ -29,7 +29,7 @@
#include "activity-detector.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/time/timevalue.hpp"
//#include "lib/format-cout.hpp"
#include "lib/format-cout.hpp"
//#include "lib/util.hpp"
//#include <utility>
@ -94,7 +94,7 @@ namespace test {
/** @test verify the setup and detection of instrumented invocations
* @todo WIP 7/23 ✔ define 🔁 implement
* @todo WIP 7/23 ✔ define ✔ implement
*/
void
verifyMockInvocation()
@ -105,20 +105,36 @@ namespace test {
++detector;
CHECK (1 == detector.currSeq());
CHECK (not detector.verifyInvocation ("funny"));
CHECK (detector.ensureNoInvocation ("funny"));
detector.markSequence();
fun(rnd);
CHECK (2 == detector.currSeq());
CHECK (detector.verifySeqIncrement(2));
fun (rnd);
CHECK (detector.verifyInvocation ("funny"));
CHECK (detector.verifyInvocation ("funny").arg(rnd));
CHECK (detector.verifyInvocation ("funny").seq(2));
CHECK (detector.verifyInvocation ("funny").arg(rnd).seq(2));
CHECK (detector.verifyInvocation ("funny").seq(2).arg(rnd));
CHECK (not detector.verifyInvocation ("bunny"));
CHECK (not detector.verifyInvocation ("funny").arg());
CHECK (not detector.verifyInvocation ("funny").arg(-rnd));
CHECK (not detector.verifyInvocation ("funny").seq(5));
CHECK (not detector.verifyInvocation ("funny").arg(rnd).seq(1));
CHECK (detector.ensureNoInvocation ("bunny"));
CHECK (detector.ensureNoInvocation ("funny").arg());
CHECK (detector.ensureNoInvocation ("funny").arg(-rnd));
CHECK (detector.ensureNoInvocation ("funny").seq(5));
CHECK (detector.ensureNoInvocation ("funny").arg(rnd).seq(1));
detector.markSequence();
fun (rnd+1);
CHECK (detector.verifyInvocation ("funny").seq(2)
.beforeSeqIncrement(3)
.beforeInvocation ("funny").seq(3).arg(rnd+1));
CHECK (detector == "Rec(EventLogHeader| this = ActivityDetector )"
", Rec(event| ID = IncSeq |{1})"
", Rec(event| ID = IncSeq |{2})"
", Rec(call| fun = funny, this = ActivityDetector, Seq = 2 |{"+util::toString(rnd)+"})"
", Rec(event| ID = IncSeq |{3})"
", Rec(call| fun = funny, this = ActivityDetector, Seq = 3 |{"+util::toString(rnd+1)+"})"_expect);
}

View file

@ -132,7 +132,7 @@ namespace test {
namespace {// Event markers
const string MARK_INC{"Inc"};
const string MARK_INC{"IncSeq"};
const string MARK_SEQ{"Seq"};
}
@ -196,6 +196,13 @@ namespace test {
return *this;
}
ActivityMatch&
beforeSeqIncrement (uint seqNr)
{
_Parent::beforeEvent(MARK_INC, util::toString(seqNr));
return *this;
}
private:
template<typename...ARGS>
ActivityMatch&
@ -297,7 +304,6 @@ namespace test {
uint
markSequence()
{
eventLog_.event (MARK_SEQ, invocationSeq_);
return operator++();
}
@ -329,12 +335,25 @@ namespace test {
return ActivityMatch{move (eventLog_.verifyCall(fun))};
}
string
getLog() const
ActivityMatch
ensureNoInvocation (string fun)
{
return "\n_______Event-Log_____________________\n"
return ActivityMatch{move (eventLog_.ensureNot(fun).locateCall(fun))};
}
ActivityMatch
verifySeqIncrement (uint seqNr)
{
return ActivityMatch{move (eventLog_.verifyEvent(MARK_INC, util::toString(seqNr)))};
}
string
showLog() const
{
return "\n____Event-Log___________________________\n"
+ util::join (eventLog_, "\n")
+ "\n───────╼━━━━━━━━╾────────────────────"
+ "\n────╼━━━━━━━━╾──────────────────────────"
;
}

View file

@ -81814,6 +81814,47 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1692022438369" ID="ID_1637851255" MODIFIED="1692022901645" TEXT="negativ-Checks unterst&#xfc;tzen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1692022449108" ID="ID_1801262965" MODIFIED="1692022587083">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
<font face="Monospaced" size="2" color="#35298d">CHECK (not ... )</font>&#160;f&#252;hrt zu irrelevanten Fehlermeldungen im Output
</p>
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
....weil ich seinerzeit Diagnose-Ausgaben per Seiteneffekt eingef&#252;hrt habe &#8212; sowas ist unabdingbar zur Fehlersuche, aber l&#228;stig, wenn es aus einem negierten CHECK resultiert
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1692022627772" ID="ID_13278224" MODIFIED="1692022860849" TEXT="sowas ist offensichtlich plausibel &#x2014; aber auch problematisch">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...allerdings liegt das an der Natur der logischen Negation selber, nicht an der unterst&#252;tzung im Verifkations-Framework; ein Check ist eine Existenz-Aussage, und daher m&#252;&#223;te zur Negation eine All-Aussage gepr&#252;ft werden, durch eine ersch&#246;pfende Suche aller m&#246;glichen alternativen Pr&#252;f-Ketten. Ich hatte F&#228;lle, in denen das durchaus vorhandene Backtracking das nicht leisten konnte
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1692022870883" ID="ID_1195200670" MODIFIED="1692022899343" TEXT="eigentlich einfach, nur repetitiv">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
das Schema daf&#252;r wurde im EventLog bereits geschaffen
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
</node>
<node CREATED="1690899978840" ID="ID_70827658" MODIFIED="1690899987515" TEXT="einen Funktionsaufruf verifizieren">
@ -81895,6 +81936,30 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690900069493" ID="ID_1755197034" MODIFIED="1691967022305" TEXT="verifyInvocation(fun).args(args...).seq(i)">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1692022948289" ID="ID_1815506838" MODIFIED="1692022990708" TEXT="unerwartete Konversion &#x27fc; unsigned">
<icon BUILTIN="broken-line"/>
<node CREATED="1692023011767" ID="ID_1384711805" MODIFIED="1692023028638">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
<font face="Monospaced" size="2" color="#241569">detector.verifyInvocation (&quot;funny&quot;).arg(-rnd)</font>
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1692023048955" ID="ID_1364393445" MODIFIED="1692023323332">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
Log-Ausgabe: <font color="#3d0f59" face="Monospaced" size="2">FAILED to match-arguments(</font><font color="#ed4669" face="Monospaced" size="2"><b>4294965953</b></font><font color="#3d0f59" face="Monospaced" size="2">)</font>
</p>
</body>
</html></richcontent>
<icon BUILTIN="info"/>
</node>
</node>
</node>
</node>
</node>