diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp
index f3da7df8f..35d818d54 100644
--- a/src/lib/iter-tree-explorer.hpp
+++ b/src/lib/iter-tree-explorer.hpp
@@ -644,7 +644,8 @@ namespace lib {
* of this Expander (decorator) is to integrate those "nested child iterators" seamlessly
* into the overall iteration process; once a child iterator is exhausted, it will be
* popped and iteration continues with the previous child iterator or finally with
- * the source iterator wrapped by this decorator.
+ * the source iterator wrapped by this decorator. The source pipeline is only pulled
+ * once the expanded children are exhausted.
* @remark since we allow a lot of leeway regarding the actual form and definition of the
* _expansion functor_, there is a lot of minute technical details, mostly confined
* within the _FunTraits traits. For the same reason, we need to prepare two different
@@ -689,14 +690,14 @@ namespace lib {
expandChildren()
{
REQUIRE (this->checkPoint(), "attempt to expand an empty explorer");
+ REQUIRE (invariant());
ResIter expanded{ hasChildren()? expandChild_(*expansions_)
: expandRoot_(*this)};
- incrementCurrent(); // consume current head element (but don't clean-up)
if (not isnil(expanded))
- expansions_.push (move(expanded));
+ expansions_.push (move(expanded)); // note: source of expansion retained
else
- dropExhaustedChildren(); // clean-up only here to preserve the logical depth
+ iterNext(); // expansion unsuccessful, thus consume source immediately
ENSURE (invariant());
}
@@ -773,6 +774,12 @@ namespace lib {
protected:
+ bool
+ hasChildren() const
+ {
+ return 0 < depth();
+ }
+
/** @internal accessor for downstream layers to allow close collaboration */
ResIter&
accessCurrentChildIter()
@@ -785,13 +792,10 @@ namespace lib {
dropExhaustedChildren()
{
while (not invariant())
- ++expansions_;
- }
-
- bool
- hasChildren() const
- {
- return 0 < depth();
+ {
+ ++expansions_; // pop expansion stack (to reinstate invariant)
+ incrementCurrent(); // consume source of innermost expansion
+ }
}
};
diff --git a/tests/core/steam/engine/job-planning-setup-test.cpp b/tests/core/steam/engine/job-planning-setup-test.cpp
index da6274084..b6e4e9c28 100644
--- a/tests/core/steam/engine/job-planning-setup-test.cpp
+++ b/tests/core/steam/engine/job-planning-setup-test.cpp
@@ -213,14 +213,24 @@ namespace test {
}
- /** @test build and verify the exploration function to discover job prerequisites */
+ /** @test build and verify the exploration function to discover job prerequisites
+ * - use a setup where the master ExitNode requires a prerequisite ExitNode to be pulled
+ * - mark the pipeline-IDs, so that both nodes can be distinguished in the resulting Jobs
+ * - the `expandPrerequisites()` builder function uses JobTicket::getPrerequisites()
+ * - and this »expander« function is unfolded recursively such that first the source
+ * appears in the iterator, and as next step the child prerequisites, possibly to
+ * be unfolded further recursively
+ * - by design of the iterator pipeline, it is always possible to access the `PipeFrameTick`
+ * - this corresponds to the top-level JobTicket, which will produce the final frame
+ * - putting all these information together, proper working can be visualised.
+ */
void
exploreJobTickets()
{
MockDispatcher dispatcher{MakeRec() // define a single segment for the complete time axis
.attrib("mark", 11) // the »master job« for each frame has pipeline-ID ≔ 11
.scope(MakeRec()
- .attrib("mark",22) // add a »prerequisite job« with pipeline-ID ≔ 22
+ .attrib("mark",22) // add a »prerequisite job« marked with pipeline-ID ≔ 22
.genNode())
.genNode()};
diff --git a/tests/library/iter-tree-explorer-test.cpp b/tests/library/iter-tree-explorer-test.cpp
index 65f95de16..28043bd81 100644
--- a/tests/library/iter-tree-explorer-test.cpp
+++ b/tests/library/iter-tree-explorer-test.cpp
@@ -1148,7 +1148,7 @@ namespace test{
/** @test ability to wrap and handle IterSource based iteration.
- * Contrary to the preceding test case, here the point is to _base the whole pipleine_
+ * Contrary to the preceding test case, here the point is to _base the whole pipeline_
* on a data source accessible through the IterSource (VTable based) interface. The notable
* point with this technique is the ability to use some _extended sub interface of IterSource_
* and to rely on this interface to implement some functor bound into the TreeExplorer pipeline.
@@ -1168,19 +1168,19 @@ namespace test{
virtual PrivateSource* expandChildren() const =0;
};
- class VerySpecivicIter
+ class VerySpecificIter
: public WrappedLumieraIter
+ FAIL___expectation___________
+
+ expect:J(11|200ms)-J(22|200ms)-J(11|240ms)-J(22|240ms)-J(11|280ms)-J(22|280ms)
+
+ actual:J(11|200ms)-J(22|240ms)-J(11|240ms)-J(22|280ms)-J(11|280ms)-J(22|320ms)
+
+ Lösung: Quelle erst weiter iterieren,
+
+ wenn wirklich notwendig
+
+ Bedeutung der Invariante
+
+ technisch:
+
+ Und zwar wegen der Objektorientierung: die gesamte Logik ist verkapselt, und nur wenige Operationen sind für abgeleitete Klassen zugänglich; im Besonderen ist incrementCurrent() private und das ist der einzige Weg um an die Iteration des Quell-Iterators zu kommen, aber genau dort wird auch dropExhaustedChildren aufgerufen()
+
+ insofern kann das Verwerfen des Quell-Objekts verzögert werden,
+
+ solange bis die daraus resultierenden Kinder aufgebraucht sind;
+
+ da wir ohnehin einen Stack benötigen, läßt sich dieser Status
+
+ auch verschachtelt aufrechterhalten
+
+ bisher stand hier das dropExhaustedChildren(), aber das fällt in disem Fall leer durch, wenn wir uns bereits wieder auf Basis-Level befinden — was bisher kein Problem war, denn es war bereits das Quell-Element konsumiert worden. Da wir aber diesen Fall klar zu fassen bekommen direkt in expandChildren() (weil eben die Expansion sich leer ergibt), können wir explizit in diesem Fall das Konsumieren der Quelle direkt machen. Ist im Ergebnis sogar logischer.
+
+ ...ich hatte es seinerzeit wohl bemerkt, aber für nicht relevant bzw sogar „eigentlich logisch“ gehalten... jedenfalls war es im Testfall verify_IterSource() dokumentiert, da dort auch die Source-Pipeline (hinter dem IterSource-Interface) sichtbar ist und mit beobachtet wird. Dieser Fall (Zeile 1229) hat nun angesprochen und wurde entsprechend korrigiert
+
+
+
+
+
+
+