1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #include <iostream>
19 
20 #include "itkTemporalProcessObject.h"
21 #include "itkTemporalDataObject.h"
22 
23 /** Set up dummy implementations of TemporalProcessObject and
24  * TemporalDataObject for testing
25  */
26 namespace itk
27 {
28 namespace TemporalProcessObjectTest
29 {
30 
31 using SizeValueType = ::itk::SizeValueType;
32 using OffsetValueType = ::itk::OffsetValueType;
33 /** \class CallRecord
34  * Record of a start or end of a GenerateDataCall from a
35  * DummyTemporalProcessObject instance
36  */
37 class CallRecord
38 {
39 public:
40   enum RecordTypeEnum {START_CALL, END_CALL, MAX_RECORD_TYPE};
41   enum MethodTypeEnum {GENERATE_DATA, STREAMING_GENERATE_DATA, MAX_METHOD_TYPE};
42 
43   /** Constructor that takes necessary info */
CallRecord(SizeValueType callerId,SizeValueType recordType,SizeValueType methodType)44   CallRecord(SizeValueType callerId, SizeValueType recordType, SizeValueType methodType)
45   {
46     if (recordType >= MAX_RECORD_TYPE || methodType >= MAX_METHOD_TYPE)
47       {
48       throw;
49       }
50     m_CallerId = callerId;
51     m_RecordType = recordType;
52     m_MethodType = methodType;
53   }
54 
55   /** Access members */
GetCallerId() const56   SizeValueType GetCallerId() const
57   {
58     return m_CallerId;
59   }
GetRecordType() const60   SizeValueType GetRecordType() const
61   {
62     return m_RecordType;
63   }
GetMethodType() const64   SizeValueType GetMethodType() const
65   {
66     return m_MethodType;
67   }
68 
69   /** Print out nicely */
Print()70   void Print()
71   {
72     std::cout << "ID: " << m_CallerId << " -> ";
73     if (m_MethodType == GENERATE_DATA)
74       {
75       std::cout << "GenerateData - ";
76       }
77     else if(m_MethodType == STREAMING_GENERATE_DATA)
78       {
79       std::cout << "TemporalStreamingGenerateData - ";
80       }
81 
82     if (m_RecordType == START_CALL)
83       {
84       std::cout << " START";
85       }
86     else if(m_RecordType == END_CALL)
87       {
88       std::cout << " END";
89       }
90     std::cout << std::endl;
91   }
92 
93   /** Comparison operators */
operator ==(const CallRecord & other) const94   bool operator==(const CallRecord& other) const
95   {
96     return (m_CallerId == other.GetCallerId() &&
97             m_RecordType == other.GetRecordType() &&
98             m_MethodType == other.GetMethodType() );
99   }
100 
operator !=(const CallRecord & other) const101   bool operator!=(const CallRecord& other) const
102   {
103     return !(*this == other);
104   }
105 
106 protected:
107   SizeValueType m_CallerId;
108   SizeValueType m_RecordType;
109   SizeValueType m_MethodType;
110 };
111 
112 /**
113  * Static list of CallRecord items representing the stack trace of
114  * calls to GenerateData and TemporalStreamingGenerateData
115  */
116 std::vector<CallRecord> m_CallStack;
117 
118 /** \class DummyTemporalDataObject
119  * Create TemporaDataObject subclass that does nothing, but overrides some
120  * methods to provide debug output
121  */
122 class DummyTemporalDataObject : public TemporalDataObject
123 {
124 public:
125 
126   /** type alias */
127   using Self = DummyTemporalDataObject;
128   using Superclass = TemporalDataObject;
129   using Pointer = SmartPointer< Self >;
130   using ConstPointer = SmartPointer< const Self >;
131 
132   /** Class macros */
133   itkNewMacro(Self);
134   itkTypeMacro(DummyTemporalDataObject, TemporalDataObject);
135 
136   /** Override update for debug output */
Update()137   void Update() override
138   {
139     //std::cout << "Calling Update from temporal data object" << std::endl;
140     Superclass::Update();
141   }
142 
143   /** Override UpdateOutputInformation for debug output */
UpdateOutputInformation()144   void UpdateOutputInformation() override
145   {
146     //std::cout << "Calling UpdateOutputInformation from temporal data object"
147     // << std::endl;
148     Superclass::UpdateOutputInformation();
149   }
150 
151   /** Override PropagateRequestedRegion for debug output */
PropagateRequestedRegion()152   void PropagateRequestedRegion() throw (itk::InvalidRequestedRegionError) override
153   {
154     Superclass::PropagateRequestedRegion();
155   }
156 
157   /** Override UpdateOutputData for debug output */
UpdateOutputData()158   void UpdateOutputData() override
159   {
160     std::cout << "      UpdateOutputData from temporal data object" << std::endl;
161 
162     //DEBUG
163     std::cout << "Buffered region outside: " << this->RequestedRegionIsOutsideOfTheBufferedRegion()
164               << std::endl;
165     Superclass::UpdateOutputData();
166   }
167 
168   /** Fill buffer with X new frames */
SetBufferToXNewFrames(SizeValueType x)169   void SetBufferToXNewFrames(SizeValueType  x)
170   {
171     // Set the internal number of buffers
172     m_DataObjectBuffer->SetNumberOfBuffers(x);
173 
174     for (SizeValueType i = 0; i < x; ++i)
175       {
176       // Create a new DataObject
177       DataObject::Pointer obj = dynamic_cast<DataObject*>(DataObject::New().GetPointer() );
178 
179       // Append to the end of the buffer
180       m_DataObjectBuffer->MoveHeadForward();
181       m_DataObjectBuffer->SetBufferContents(0, obj);
182       }
183 
184     // Set buffered region info
185     m_BufferedTemporalRegion.SetFrameStart(0);
186     m_BufferedTemporalRegion.SetFrameDuration(x);
187   }
188 
189   /** Append the supplied data object */
SetObjectAtFrame(SizeValueType frameNumber,DataObject * obj)190   void SetObjectAtFrame(SizeValueType frameNumber, DataObject* obj)
191   {
192     m_DataObjectBuffer->SetBufferContents(frameNumber, obj);
193   }
194 
195   /** Get a bufferd frame */
GetFrame(SizeValueType frameNumber)196   DataObject::Pointer GetFrame(SizeValueType frameNumber)
197   {
198     // if nothing buffered, just fail
199     if (m_BufferedTemporalRegion.GetFrameDuration() == 0)
200       {
201       return nullptr;
202       }
203 
204     // make sure we have the desired frame buffered
205     SizeValueType bufStart = m_BufferedTemporalRegion.GetFrameStart();
206     SizeValueType bufEnd = bufStart + m_BufferedTemporalRegion.GetFrameDuration() - 1;
207     if (frameNumber < bufStart || frameNumber > bufEnd)
208       {
209       return nullptr;
210       }
211 
212     // If we can, fetch the desired frame
213     OffsetValueType frameOffset = frameNumber - bufEnd;  // Should be negative
214     return m_DataObjectBuffer->GetBufferContents(frameOffset);
215   }
216 
217 };
218 
219 /** \class DummyTemporalProcessObject
220  * Create TemporalProcessObject subclass that does nothing, but implements
221  * New() and TemporalStreamingGenerateData()
222  */
223 class DummyTemporalProcessObject : public TemporalProcessObject
224 {
225 public:
226   /** type alias */
227   using Self = DummyTemporalProcessObject;
228   using Superclass = TemporalProcessObject;
229   using Pointer = SmartPointer< Self >;
230   using ConstPointer = SmartPointer< const Self >;
231 
232   /** Class macros */
233   itkNewMacro(Self);
234   itkTypeMacro(DummyTemporalProcessObject, TemporalProcessObject);
235 
236   /*-REQUIRED IMPLEMENTATIONS------------------------------------------------*/
237 
238   /** TemporalStreamingGenerateData */
TemporalStreamingGenerateData()239   void TemporalStreamingGenerateData() override
240   {
241     // Create a START entry in the stack trace
242     m_CallStack.emplace_back(m_IdNumber,
243                                      CallRecord::START_CALL, CallRecord::STREAMING_GENERATE_DATA );
244 
245     // Report
246     SizeValueType outputStart = this->GetOutput()->GetRequestedTemporalRegion().GetFrameStart();
247     std::cout << "**(ID = " << m_IdNumber << ") - TemporalStreamingGenerateData" << std::endl;
248     std::cout << "  -> output requested from: " << outputStart << " to "
249               << this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration() +
250     outputStart - 1
251               << std::endl;
252 
253     SizeValueType inputStart = this->GetInput()->GetRequestedTemporalRegion().GetFrameStart();
254     SizeValueType inputEnd = inputStart +
255       this->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() - 1;
256     std::cout << "  -> input requested from " << inputStart << " to " << inputEnd << std::endl;
257     std::cout << "  -> input buffered from "
258               << this->GetInput()->GetBufferedTemporalRegion().GetFrameStart() << " to "
259               << this->GetInput()->GetBufferedTemporalRegion().GetFrameStart() +
260     this->GetInput()->GetBufferedTemporalRegion().GetFrameDuration() - 1
261               << std::endl;
262 
263     // Get the list of unbuffered frames
264     TemporalRegion unbufferedRegion = this->GetOutput()->GetUnbufferedRequestedTemporalRegion();
265     std::cout << unbufferedRegion << std::endl;
266 
267     // Make sure that the requested output duration matches the unit output
268     // duration
269     SizeValueType numFramesOut =
270       this->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration();
271     if (numFramesOut != m_UnitOutputNumberOfFrames)
272       {
273       itkExceptionMacro("Requested non-unit number of output frames");
274       }
275 
276     // Just pass frames from the input through to the output and add debug info
277     for (SizeValueType i = outputStart; i < outputStart + numFramesOut; ++i)
278       {
279       DataObject::Pointer newObj = dynamic_cast<DataObject*>(DataObject::New().GetPointer() );
280 
281       // Set the output
282       this->GetOutput()->SetObjectAtFrame(i, newObj);
283       }
284 
285     // Set the buffered region to match the requested region
286     //this->GetOutput()->SetBufferedTemporalRegion(this->GetOutput()->GetRequestedTemporalRegion());
287 
288     // Create an END entry in the stack trace
289     m_CallStack.emplace_back(m_IdNumber,
290                                      CallRecord::END_CALL, CallRecord::STREAMING_GENERATE_DATA );
291 
292   }
293 
294   /** Allow the UnitInputNumberOfFrames to be set */
SetUnitInputNumberOfFrames(const SizeValueType numberOfFrames)295   void SetUnitInputNumberOfFrames( const SizeValueType numberOfFrames ) override
296     {
297     itkDebugMacro("setting UnitInputNumberOfFrames to " << numberOfFrames);
298     if ( this->m_UnitInputNumberOfFrames != numberOfFrames )
299       {
300       this->m_UnitInputNumberOfFrames = numberOfFrames;
301       this->Modified();
302       }
303     }
304 
305   /** Allow the UnitOutputNumberOfFrames to be set */
SetUnitOutputNumberOfFrames(const SizeValueType numberOfFrames)306   void SetUnitOutputNumberOfFrames( const SizeValueType numberOfFrames ) override
307     {
308     itkDebugMacro("setting UnitOutputNumberOfFrames to " << numberOfFrames);
309     if ( this->m_UnitOutputNumberOfFrames != numberOfFrames )
310       {
311       this->m_UnitOutputNumberOfFrames = numberOfFrames;
312       this->Modified();
313       }
314     }
315 
316   /** GetOutput will return the output on port 0 */
GetOutput()317   DummyTemporalDataObject::Pointer GetOutput()
318   {
319     return dynamic_cast<DummyTemporalDataObject*>(this->TemporalProcessObject::GetOutput(0) );
320   }
321 
322   /** SetInput will set the 0th input */
323   using Superclass::SetInput;
SetInput(TemporalDataObject * tdo)324   void SetInput(TemporalDataObject* tdo)
325   {
326     this->ProcessObject::SetNthInput(0, tdo);
327   }
328 
329   /** GetInput gets the 0th input as a DummyTemporalDataObject */
GetInput()330   DummyTemporalDataObject::Pointer GetInput()
331   {
332     return dynamic_cast<DummyTemporalDataObject*>(this->TemporalProcessObject::GetInput(0) );
333   }
334 
335   /** Get/Set IdNumber */
336   itkSetMacro(IdNumber, SizeValueType);
337   itkGetMacro(IdNumber, SizeValueType);
338 
339   /** Provide access to m_FrameSkipPerOutput */
SetFrameSkipPerOutput(const OffsetValueType frameSkip)340   void SetFrameSkipPerOutput ( const OffsetValueType frameSkip ) override
341     {
342     itkDebugMacro("setting FrameSkipPerOutput to " << frameSkip);
343     if ( this->m_FrameSkipPerOutput != frameSkip )
344       {
345       this->m_FrameSkipPerOutput = frameSkip;
346       this->Modified();
347       }
348     }
349 
350   itkGetMacro(FrameSkipPerOutput, OffsetValueType);
351 
352   /** Provide access to m_InputStencilCurrentFrameIndex */
SetInputStencilCurrentFrameIndex(const SizeValueType inputStencil)353   void SetInputStencilCurrentFrameIndex ( const SizeValueType inputStencil ) override
354     {
355     itkDebugMacro("setting InputStencilCurrentFrameIndex to " << inputStencil);
356     if ( this->m_InputStencilCurrentFrameIndex != inputStencil )
357       {
358       this->m_InputStencilCurrentFrameIndex = inputStencil;
359       this->Modified();
360       }
361     }
GetInputStencilCurrentFrameIndex()362   SizeValueType GetInputStencilCurrentFrameIndex() override
363     {
364     return this->m_InputStencilCurrentFrameIndex;
365     }
366 
367   /*-DEBUG OVERRIDES---------------------------------------------------------*/
368 
369   /** Override Update for debug output */
Update()370   void Update() override
371   {
372     std::cout << "(ID = " << m_IdNumber << ") - Update" << std::endl;
373     Superclass::Update();
374   }
375 
376   /** Override UpdateOutputData for debug output */
UpdateOutputData(DataObject * dobj)377   void UpdateOutputData(DataObject* dobj) override
378   {
379     std::cout << "(ID = " << m_IdNumber << ") - UpdateOutputData" << std::endl;
380     Superclass::UpdateOutputData(dobj);
381   }
382 
383   /** Override GenerateData for debug output */
GenerateData()384   void GenerateData() override
385   {
386     // Create a START entry in the stack trace
387     m_CallStack.emplace_back(m_IdNumber,
388                                      CallRecord::START_CALL, CallRecord::GENERATE_DATA );
389 
390     std::cout << "*(ID = " << m_IdNumber << ") - GenerateData" << std::endl;
391     Superclass::GenerateData();
392 
393     // Create an END entry in the stack trace
394     m_CallStack.emplace_back(m_IdNumber,
395                                      CallRecord::END_CALL, CallRecord::GENERATE_DATA );
396 
397   }
398 
399   /** Override EnlargeOutputRequestedTemporalRegion for debug output */
EnlargeOutputRequestedTemporalRegion(TemporalDataObject * output)400   void EnlargeOutputRequestedTemporalRegion(TemporalDataObject* output) override
401   {
402     std::cout << "(ID = " << m_IdNumber << ") - EnlargeOutputRequestedTemporalRegion" << std::endl;
403     Superclass::EnlargeOutputRequestedTemporalRegion(output);
404   }
405 
406   /** Override GenerateInputRequestedTemporalRegion for debug output */
GenerateInputRequestedTemporalRegion()407   void GenerateInputRequestedTemporalRegion() override
408   {
409     std::cout << "(ID = " << m_IdNumber << ") - GenerateInputRequestedTemporalRegion" << std::endl;
410     Superclass::GenerateInputRequestedTemporalRegion();
411   }
412 
413 protected:
414 
415   /** Constructor */
DummyTemporalProcessObject()416   DummyTemporalProcessObject()
417 
418   {
419     DummyTemporalDataObject::Pointer po = DummyTemporalDataObject::New();
420 
421     this->SetNthOutput(0, po.GetPointer() );
422   }
423 
424 private:
425 
426   /** ID number used for debugging */
427   SizeValueType m_IdNumber{0};
428 
429 };
430 
431 } // end namespace TemporalProcessObjectTest
432 } // end namespace itk
433 
434 /**
435  * Test functionality of itkTemporalProcessObject
436  */
itkTemporalProcessObjectTest(int,char * [])437 int itkTemporalProcessObjectTest( int ,
438                                   char* [] )
439 {
440 
441   using SizeValueType = ::itk::SizeValueType;
442   using OffsetValueType = ::itk::OffsetValueType;
443   //////
444   // Set up pipeline
445   //////
446 
447   // Create 3 new DummyTemporalProcessObjects
448   using TPOType = itk::TemporalProcessObjectTest::DummyTemporalProcessObject;
449   TPOType::Pointer tpo1 = TPOType::New();
450   tpo1->SetIdNumber(1);
451   TPOType::Pointer tpo2 = TPOType::New();
452   tpo2->SetIdNumber(2);
453   TPOType::Pointer tpo3 = TPOType::New();
454   tpo3->SetIdNumber(3);
455 
456   // Set up the Process Objects in a pipeline
457   tpo2->SetInput(tpo1->GetOutput() );
458   tpo3->SetInput(tpo2->GetOutput() );
459 
460   // Set up the Unit input/output numbers of frames
461   tpo1->SetUnitInputNumberOfFrames(3);
462   tpo1->SetUnitOutputNumberOfFrames(1);
463   tpo2->SetUnitInputNumberOfFrames(3);
464   tpo2->SetUnitOutputNumberOfFrames(3);
465   tpo3->SetUnitInputNumberOfFrames(2);
466   tpo3->SetUnitOutputNumberOfFrames(1);
467   tpo3->SetFrameSkipPerOutput(2);
468   tpo2->GetOutput()->SetNumberOfBuffers(6);
469 
470   // Set up frame stencils
471   tpo1->SetInputStencilCurrentFrameIndex(1); // "current frame" centered in
472                                              // group of 3
473   tpo2->SetInputStencilCurrentFrameIndex(0); // "current frame" at start of
474                                              // group of 3
475   tpo3->SetInputStencilCurrentFrameIndex(1); // "current frame" at end of group
476                                              // of 2
477 
478   // Create a new TemporalDataObject to pass through the pipeline
479   using TDOType = itk::TemporalProcessObjectTest::DummyTemporalDataObject;
480   TDOType::Pointer tdo = TDOType::New();
481   tpo1->SetInput(tdo);
482 
483   // Set up regions for TemporalDataObject
484   itk::TemporalRegion largestRegion;
485   itk::TemporalRegion requestedRegion;
486   itk::TemporalRegion bufferedRegion;
487   largestRegion.SetFrameStart(0);
488   largestRegion.SetFrameDuration(20);
489   tdo->SetLargestPossibleTemporalRegion(largestRegion);
490   requestedRegion.SetFrameStart(0);
491   requestedRegion.SetFrameDuration(1);
492   tdo->SetRequestedTemporalRegion(requestedRegion);
493   bufferedRegion.SetFrameStart(0);
494   bufferedRegion.SetFrameDuration(0);
495   tdo->SetBufferedTemporalRegion(bufferedRegion);
496 
497   // Fill the TemporalDataObject input with frames for the entire region
498   tdo->SetBufferToXNewFrames(largestRegion.GetFrameDuration() );
499 
500   //////
501   // Test results of LargestTemporalRegion computation
502   //////
503 
504   // Update to get largest possible temporal region information
505   tpo3->UpdateOutputInformation();
506 
507   // Check largest possible temporal region after propagation
508   if (tpo1->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameDuration() != 18)
509     {
510     std::cerr << "tpo1 largest possible region duration not correct" << std::endl;
511     return EXIT_FAILURE;
512     }
513   if (tpo1->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameStart() != 1)
514     {
515     std::cerr << "tpo1 largest possible region start not correct" << std::endl;
516     return EXIT_FAILURE;
517     }
518   if (tpo2->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameDuration() != 48)
519     {
520     std::cerr << "tpo2 largest possible region duration not correct" << std::endl;
521     return EXIT_FAILURE;
522     }
523   if (tpo2->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameStart() != 1)
524     {
525     std::cerr << "tpo2 largest possible region start not correct" << std::endl;
526     return EXIT_FAILURE;
527     }
528   itk::TemporalRegion endLargestPossibleRegion =
529     tpo3->GetOutput()->GetLargestPossibleTemporalRegion();
530   if (endLargestPossibleRegion.GetFrameDuration() != 24)
531     {
532     std::cerr << "tpo3 largest possible region duration not correct" << std::endl;
533     return EXIT_FAILURE;
534     }
535   if (endLargestPossibleRegion.GetFrameStart() != 2)
536     {
537     std::cerr << "tpo3 largest possible region start not correct" << std::endl;
538     return EXIT_FAILURE;
539     }
540 
541   //////
542   // Test results of requested region propagation
543   //////
544 
545   // Set up requested region for the end of the pipeline
546   itk::TemporalRegion finalRequest;
547   finalRequest.SetFrameStart(endLargestPossibleRegion.GetFrameStart() );
548   finalRequest.SetFrameDuration(1);
549   itk::TemporalProcessObjectTest::DummyTemporalDataObject* finalOutput = tpo3->GetOutput();
550   finalOutput->SetRequestedTemporalRegion(finalRequest);
551 
552   // Update to propagate the requested temporal region
553   finalOutput->PropagateRequestedRegion();
554 
555   // Check requested region up the pipeline
556 
557   // for tpo3, the requested input region should be size 3 because tpo2 can
558   // only output in groups of 3
559   if (tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() != 3)
560     {
561     std::cout << tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() << std::endl;
562     std::cerr << "tpo3 requested region duration not correct" << std::endl;
563     return EXIT_FAILURE;
564     }
565   if (tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameStart() != 3)
566     {
567     std::cerr << tpo3->GetInput()->GetRequestedTemporalRegion().GetFrameStart() << std::endl;
568     std::cerr << "tpo3 requested region start not correct" << std::endl;
569     return EXIT_FAILURE;
570     }
571 
572   // tpo2 is 3->3, so an initial request of 2 gets enlarged to 3 which results
573   // in propagating a request for 3 to tpo1
574   if (tpo2->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() != 3)
575     {
576     std::cerr << "tpo2 requested region duration not correct" << std::endl;
577     return EXIT_FAILURE;
578     }
579   if (tpo2->GetInput()->GetRequestedTemporalRegion().GetFrameStart() != 3)
580     {
581     std::cerr << tpo2->GetInput()->GetRequestedTemporalRegion().GetFrameStart() << std::endl;
582     std::cerr << "tpo2 requested region start not correct" << std::endl;
583     return EXIT_FAILURE;
584     }
585 
586   // tpo1 is 3->1 and skips 1 frame for each output, so a request for 3
587   // requires 5 as input
588   if (tpo1->GetInput()->GetRequestedTemporalRegion().GetFrameDuration() != 5)
589     {
590     std::cerr << "tpo1 requested region duration not correct" << std::endl;
591     return EXIT_FAILURE;
592     }
593   if (tpo1->GetInput()->GetRequestedTemporalRegion().GetFrameStart() != 2)
594     {
595     std::cerr << tpo1->GetInput()->GetRequestedTemporalRegion().GetFrameStart() << std::endl;
596     std::cerr << "tpo1 requested region start not correct" << std::endl;
597     return EXIT_FAILURE;
598     }
599 
600   //////
601   // Test Generation of data
602   //////
603 
604   // Call update to execute the entire pipeline and track the call stack
605   itk::TemporalProcessObjectTest::m_CallStack.clear();
606   tpo3->Update();
607 
608   // Print out duration of buffered output region
609   itk::TemporalProcessObjectTest::DummyTemporalDataObject::Pointer outputObject = tpo3->GetOutput();
610   OffsetValueType                                                    outputStart =
611     outputObject->GetBufferedTemporalRegion().GetFrameStart();
612   SizeValueType outputDuration =
613     outputObject->GetBufferedTemporalRegion().GetFrameDuration();
614   std::cout << "Buffered Output Region: "
615             << outputStart << "->" << outputStart + outputDuration - 1 << std::endl;
616 
617   // Create a list of CallRecord items representing the correct
618   // stack trace
619   using RecordType = itk::TemporalProcessObjectTest::CallRecord;
620   std::vector<RecordType> correctCallStack;
621 
622   // GenDat - START - obj 3
623   correctCallStack.emplace_back(3, RecordType::START_CALL, RecordType::GENERATE_DATA );
624 
625   // GenDat - START - obj 2
626   correctCallStack.emplace_back(2, RecordType::START_CALL, RecordType::GENERATE_DATA );
627 
628   // GenDat - START - obj 1
629   correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::GENERATE_DATA );
630 
631   // TempStreamGenDat - START - obj 1
632   correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
633 
634   // TempStreamGenDat - END - obj 1
635   correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
636 
637   // TempStreamGenDat - START - obj 1
638   correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
639 
640   // TempStreamGenDat - END - obj 1
641   correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
642 
643   // TempStreamGenDat - START - obj 1
644   correctCallStack.emplace_back(1, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
645 
646   // TempStreamGenDat - END - obj 1
647   correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
648 
649   // GenDat - END - obj 1
650   correctCallStack.emplace_back(1, RecordType::END_CALL, RecordType::GENERATE_DATA );
651 
652   // TempStreamGenDat - START - obj 2
653   correctCallStack.emplace_back(2, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
654 
655   // TempStreamGenDat - END - obj 2
656   correctCallStack.emplace_back(2, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
657 
658   // GenDat - END - obj 2
659   correctCallStack.emplace_back(2, RecordType::END_CALL, RecordType::GENERATE_DATA );
660 
661   // TempStreamGenDat - START - obj 3
662   correctCallStack.emplace_back(3, RecordType::START_CALL, RecordType::STREAMING_GENERATE_DATA );
663 
664   // TempStreamGenDat - END - obj 3
665   correctCallStack.emplace_back(3, RecordType::END_CALL, RecordType::STREAMING_GENERATE_DATA );
666 
667   // GenDat - END - obj 3
668   correctCallStack.emplace_back(3, RecordType::END_CALL, RecordType::GENERATE_DATA );
669 
670   // Check that correct number of calls made
671   if (itk::TemporalProcessObjectTest::m_CallStack.size() != correctCallStack.size() )
672     {
673     std::cerr << "Incorrect number of items in call stack" << std::endl;
674     return EXIT_FAILURE;
675     }
676 
677   // Check that call lists match
678   std::cout << std::endl;
679   for (SizeValueType i = 0; i < itk::TemporalProcessObjectTest::m_CallStack.size(); ++i)
680     {
681     std::cout << "Got: ";
682     itk::TemporalProcessObjectTest::m_CallStack[i].Print();
683     std::cout << "Expected: ";
684     correctCallStack[i].Print();
685     std::cout << std::endl;
686 
687     if (itk::TemporalProcessObjectTest::m_CallStack[i] != correctCallStack[i])
688       {
689       std::cerr << "Call stacks don't match" << std::endl;
690       return EXIT_FAILURE;
691       }
692     }
693 
694   //////
695   // Test Generation of next output frame -- Since tpo3 skips two frames of
696   // input for every frame of output and tpo2 can only generate 3 outputs at a
697   // time, tpo2 must generate 6,7,8 (none of which are already buffered), so
698   // the entire pipeline runs again (so the call stack should be the same).
699   //////
700 
701   // Set the requested region to the next output frame
702   finalRequest.SetFrameStart(finalRequest.GetFrameStart() + 1);
703   finalOutput = tpo3->GetOutput();
704   finalOutput->SetRequestedTemporalRegion(finalRequest);
705 
706   // Call update to execute the entire pipeline and track the call stack
707   itk::TemporalProcessObjectTest::m_CallStack.clear();
708   tpo3->Update();
709 
710   // Check that correct number of calls made
711   if (itk::TemporalProcessObjectTest::m_CallStack.size() != correctCallStack.size() )
712     {
713     std::cerr << "Incorrect number of items in call stack. Got: "
714               << itk::TemporalProcessObjectTest::m_CallStack.size() << " Expected: "
715               << correctCallStack.size() << std::endl;
716     return EXIT_FAILURE;
717     }
718 
719   // Check that call lists match
720   std::cout << std::endl;
721   for (SizeValueType i = 0; i < itk::TemporalProcessObjectTest::m_CallStack.size(); ++i)
722     {
723     std::cout << "Got: ";
724     itk::TemporalProcessObjectTest::m_CallStack[i].Print();
725     std::cout << "Expected: ";
726     correctCallStack[i].Print();
727     std::cout << std::endl;
728 
729     if (itk::TemporalProcessObjectTest::m_CallStack[i] != correctCallStack[i])
730       {
731       std::cerr << "Call stacks don't match" << std::endl;
732       return EXIT_FAILURE;
733       }
734     }
735 
736   //////
737   // Call Update again and make sure that nothing happens except one call to
738   // GenerateData at the bottom which doesn't end up needing to do anything
739   //////
740   itk::TemporalProcessObjectTest::m_CallStack.clear();
741   tpo3->Update();
742 
743   correctCallStack.clear();
744 
745   // GenDat - START - obj 3
746   correctCallStack.emplace_back(3, RecordType::START_CALL, RecordType::GENERATE_DATA );
747 
748   // GenDat - END - obj 3
749   correctCallStack.emplace_back(3, RecordType::END_CALL, RecordType::GENERATE_DATA );
750 
751   // Check that correct number of calls made
752   if (itk::TemporalProcessObjectTest::m_CallStack.size() != correctCallStack.size() )
753     {
754     std::cerr << "Incorrect number of items in call stack. Got: "
755               << itk::TemporalProcessObjectTest::m_CallStack.size() << " Expected: "
756               << correctCallStack.size() << std::endl;
757     return EXIT_FAILURE;
758     }
759 
760   // Check that call lists match
761   std::cout << std::endl;
762   for (SizeValueType i = 0; i < itk::TemporalProcessObjectTest::m_CallStack.size(); ++i)
763     {
764     std::cout << "Got: ";
765     itk::TemporalProcessObjectTest::m_CallStack[i].Print();
766     std::cout << "Expected: ";
767     correctCallStack[i].Print();
768     std::cout << std::endl;
769 
770     if (itk::TemporalProcessObjectTest::m_CallStack[i] != correctCallStack[i])
771       {
772       std::cerr << "Call stacks don't match" << std::endl;
773       return EXIT_FAILURE;
774       }
775     }
776 
777   //////
778   // Test that the requested temporal region for the output of a temporal
779   // process object gets set to the largest possible temporal region if no
780   // temporal region has been set
781   //////
782 
783   // Reset tpo1 and the requsted temporal region of tdo
784   tpo1 = TPOType::New();
785   itk::TemporalRegion emptyRegion;
786   tdo->SetRequestedTemporalRegion(emptyRegion);
787   tpo1->SetInput(tdo);
788   tpo1->UpdateOutputInformation();
789 
790   // Make sure the requested temporal region of tpo1's output is empty
791   if (tpo1->GetOutput()->GetRequestedTemporalRegion() != emptyRegion)
792     {
793     std::cerr << "tpo1's output's requested temporal region not empty before propagate" << std::endl;
794     return EXIT_FAILURE;
795     }
796 
797   tpo1->PropagateRequestedRegion(tpo1->GetOutput() );
798   if (tpo1->GetOutput()->GetRequestedTemporalRegion() !=
799       tpo1->GetOutput()->GetLargestPossibleTemporalRegion() ||
800       tpo1->GetOutput()->GetRequestedTemporalRegion() == emptyRegion)
801     {
802     std::cerr << "tpo1's output's requested temporal region not set correctly after propagate" << std::endl;
803     return EXIT_FAILURE;
804     }
805 
806   // Test that if largest possible temporal region has infinte duration,
807   // request gets set to duration 1
808   tpo1 = TPOType::New();
809   largestRegion = tdo->GetLargestPossibleTemporalRegion();
810   largestRegion.SetFrameDuration(ITK_INFINITE_FRAME_DURATION);
811   tdo->SetLargestPossibleTemporalRegion(largestRegion);
812   tpo1->SetInput(tdo);
813   tpo1->UpdateOutputInformation();
814   tpo1->PropagateRequestedRegion(tpo1->GetOutput() );
815   if (tpo1->GetOutput()->GetLargestPossibleTemporalRegion().GetFrameDuration() !=
816       ITK_INFINITE_FRAME_DURATION ||
817       tpo1->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration() != 1)
818     {
819     std::cerr << "tpo1's output's temporal regions not properly set for infinite input" << std::endl;
820     std::cerr << "Requested region duration: "
821               << tpo1->GetOutput()->GetRequestedTemporalRegion().GetFrameDuration() << std::endl;
822     return EXIT_FAILURE;
823     }
824 
825   //////
826   // Return successfully
827   //////
828   return EXIT_SUCCESS;
829 }
830