public function testTemporaryQtiBinaryStorage()
 {
     $doc = new XmlCompactDocument();
     $doc->load(self::samplesDir() . 'custom/runtime/itemsubset.xml');
     $test = $doc->getDocumentComponent();
     $sessionManager = new SessionManager();
     $storage = new TemporaryQtiBinaryStorage($sessionManager, new BinaryAssessmentTestSeeker($doc->getDocumentComponent()));
     $session = $storage->instantiate($test);
     $sessionId = $session->getSessionId();
     // Instantiating the test session does not mean it is persisted. At the moment,
     // it is not persistent yet.
     $this->assertFalse($storage->exists($sessionId));
     $this->assertInstanceOf('qtism\\runtime\\tests\\AssessmentTestSession', $session);
     $this->assertEquals(AssessmentTestSessionState::INITIAL, $session->getState());
     // The candidate begins the test session at 13:00:00.
     $session->setTime(new DateTime('2014-07-14T13:00:00+00:00', new DateTimeZone('UTC')));
     $session->beginTestSession();
     // A little bit of noisy persistence...
     $storage->persist($session);
     // Now the test is persisted, we can try to know whether it exists in storage.
     $this->assertTrue($storage->exists($sessionId));
     // Let's retrive the session from storage.
     $session = $storage->retrieve($test, $sessionId);
     $this->assertEquals(AssessmentTestSessionState::INTERACTING, $session->getState());
     // The test session has begun. We are in linear mode so that all
     // item sessions must be initialized and selected for presentation
     // to the candidate.
     $itemSessionStore = $session->getAssessmentItemSessionStore();
     $itemSessionCount = 0;
     $iterator = new QtiComponentIterator($doc->getDocumentComponent(), array('assessmentItemRef'));
     foreach ($iterator as $itemRef) {
         $refItemSessions = $itemSessionStore->getAssessmentItemSessions($itemRef);
         foreach ($refItemSessions as $refItemSession) {
             $this->assertEquals(AssessmentItemSessionState::INITIAL, $refItemSession->getState());
             $itemSessionCount++;
         }
     }
     $this->assertEquals(9, $itemSessionCount);
     // The outcome variables composing the test-level global scope
     // must be set with their default value if any.
     foreach ($doc->getDocumentComponent()->getOutcomeDeclarations() as $outcomeDeclaration) {
         $this->assertFalse(is_null($session[$outcomeDeclaration->getIdentifier()]));
         $this->assertEquals(0, $session[$outcomeDeclaration->getIdentifier()]->getValue());
     }
     // S01 -> Q01 - Correct response.
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiFloat', $session['Q01.scoring']);
     $this->assertEquals(0.0, $session['Q01.scoring']->getValue());
     $this->assertSame(null, $session['Q01.RESPONSE']);
     // The candidate begins the attempt on Q01 at 13:00:00.
     $session->setTime(new DateTime('2014-07-14T13:00:00+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The canditate spends 1 second on item Q01.
     $session->setTime(new DateTime('2014-07-14T13:00:01+00:00', new DateTimeZone('UTC')));
     $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER, new QtiIdentifier('ChoiceA')))));
     // Are durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $session->moveNext();
     // Because Q01 is not a multi-occurence item in the route, isLastOccurenceUpdate always return false.
     $this->assertFalse($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 0));
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     // After the persist, do we still have the correct scores and durations for Q01?.
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiFloat', $session['Q01.scoring']);
     $this->assertEquals(1.0, $session['Q01.scoring']->getValue());
     $this->assertEquals('ChoiceA', $session['Q01.RESPONSE']->getValue());
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     // S01 -> Q02 - Incorrect response.
     $this->assertEquals('Q02', $session->getCurrentAssessmentItemRef()->getIdentifier());
     $this->assertEquals('S01', $session->getCurrentAssessmentSection()->getIdentifier());
     $this->assertEquals('P01', $session->getCurrentTestPart()->getIdentifier());
     // The candidate begins an attempt on Q02 at 13:00:02.
     $session->setTime(new DateTime('2014-07-14T13:00:02+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends 2 seconds on item Q02.
     $session->setTime(new DateTime('2014-07-14T13:00:04+00:00', new DateTimeZone('UTC')));
     $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', Cardinality::MULTIPLE, BaseType::PAIR, new MultipleContainer(BaseType::PAIR, array(new QtiPair('C', 'M')))))));
     // Whate about scores of Q02?
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiFloat', $session['Q02.SCORE']);
     $this->assertEquals(1.0, $session['Q02.SCORE']->getValue());
     // Are the durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT4S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT4S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT4S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $session->moveNext();
     // S01 -> Q03 - Skip.
     // The candidate begins an attempt on Q03 at 13:00:04.
     $session->setTime(new DateTime('2014-07-14T13:00:04+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends 10 seconds on Q03 and then skip the item.
     $session->setTime(new DateTime('2014-07-14T13:00:14+00:00', new DateTimeZone('UTC')));
     $session->skip();
     // Are the durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     // !!! We move to the next section S02.
     $session->moveNext();
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     // S02 -> Q04 - Correct response.
     $this->assertEquals('Q04', $session->getCurrentAssessmentItemRef()->getIdentifier());
     $this->assertEquals('S02', $session->getCurrentAssessmentSection()->getIdentifier());
     $this->assertEquals('P01', $session->getCurrentTestPart()->getIdentifier());
     $this->assertEquals(AssessmentTestSessionState::INTERACTING, $session->getState());
     // The candidate begins an attempt on Q04 at 13:00:15.
     $session->setTime(new DateTime('2014-07-14T13:00:15+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends 5 seconds on Q04.
     $session->setTime(new DateTime('2014-07-14T13:00:20+00:00', new DateTimeZone('UTC')));
     $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', Cardinality::MULTIPLE, BaseType::DIRECTED_PAIR, new MultipleContainer(BaseType::DIRECTED_PAIR, array(new QtiDirectedPair('W', 'G1'), new QtiDirectedPair('Su', 'G2')))))));
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     // What about score of Q04.
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiFloat', $session['Q04.SCORE']);
     $this->assertEquals(3.0, $session['Q04.SCORE']->getValue());
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     $this->assertTrue($session['Q04.RESPONSE']->equals(new MultipleContainer(BaseType::DIRECTED_PAIR, array(new QtiDirectedPair('W', 'G1'), new QtiDirectedPair('Su', 'G2')))));
     // Are the durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT20S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT20S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT6S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q04.duration']->equals(new QtiDuration('PT5S')));
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     $session->moveNext();
     // S02 -> Q05 - Skip.
     // The candidate begins the attempt on Q05 at 13:00:20.
     $session->setTime(new DateTime('2014-07-14T13:00:20+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends 1 second on Q05.
     $session->setTime(new DateTime('2014-07-14T13:00:21+00:00', new DateTimeZone('UTC')));
     $session->skip();
     // Are the durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT21S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT21S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT7S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q04.duration']->equals(new QtiDuration('PT5S')));
     $this->assertTrue($session['Q05.duration']->equals(new QtiDuration('PT1S')));
     // !!! We move to the next section S03.
     $session->moveNext();
     // Q06 - Skip.
     // The candidate begins the attempt on Q06 at 13:00:24.
     $session->setTime(new DateTime('2014-07-14T13:00:24+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends 2 seconds on Q06.
     $session->setTime(new DateTime('2014-07-14T13:00:26+00:00', new DateTimeZone('UTC')));
     $session->skip();
     // Are the durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT26S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT26S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT12S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT0S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q04.duration']->equals(new QtiDuration('PT5S')));
     $this->assertTrue($session['Q05.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q06.duration']->equals(new QtiDuration('PT2S')));
     // !!! We move to the next section S03.
     $session->moveNext();
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     // S03 -> Q07.1 - Incorrect response (but inside the circle).
     $this->assertFalse($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 0));
     $this->assertEquals('Q07', $session->getCurrentAssessmentItemRef()->getIdentifier());
     $this->assertEquals(0, $session->getCurrentAssessmentItemRefOccurence());
     // The candidate begins an attempt on Q07.1 at 13:00:28.
     $session->setTime(new DateTime('2014-07-14T13:00:28+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends 10 seconds on Q07.1.
     $session->setTime(new DateTime('2014-07-14T13:00:38+00:00', new DateTimeZone('UTC')));
     $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::POINT, new QtiPoint(103, 114)))));
     // Are the durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT38S')));
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT38S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT12S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT12S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q04.duration']->equals(new QtiDuration('PT5S')));
     $this->assertTrue($session['Q05.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q06.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q07.1.duration']->equals(new QtiDuration('PT10S')));
     $session->moveNext();
     // We now test the lastOccurence update for this multi-occurence item.
     $this->assertTrue($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 0));
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     $this->assertTrue($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 0));
     $this->assertFalse($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 1));
     // S03 -> Q07.2 - Incorrect response (outside the circle).
     $this->assertEquals('Q07', $session->getCurrentAssessmentItemRef()->getIdentifier());
     $this->assertEquals(1, $session->getCurrentAssessmentItemRefOccurence());
     // The candidate begins the attempt on Q07.2 at 13:00:38.
     $session->setTime(new DateTime('2014-07-14T13:00:38+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate spends a whole minute on Q07.2.
     $session->setTime(new DateTime('2014-07-14T13:01:38+00:00', new DateTimeZone('UTC')));
     $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::POINT, new QtiPoint(200, 200)))));
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT98S')));
     // NO FEAR!
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT1M38S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT12S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT1M12S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q04.duration']->equals(new QtiDuration('PT5S')));
     $this->assertTrue($session['Q05.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q06.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q07.1.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q07.2.duration']->equals(new QtiDuration('PT1M')));
     $session->moveNext();
     $this->assertFalse($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 0));
     $this->assertTrue($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 1));
     // A little bit of noisy persistence...
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     $this->assertFalse($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 0));
     $this->assertTrue($session->isLastOccurenceUpdate($session->getCurrentAssessmentItemRef(), 1));
     // S03 -> Q07.3 - Correct response (perfectly on the point).
     $this->assertEquals('Q07', $session->getCurrentAssessmentItemRef()->getIdentifier());
     $this->assertEquals(2, $session->getCurrentAssessmentItemRefOccurence());
     // The candidate takes an attempt on Q07.3 at 13:01:39
     $session->setTime(new DateTime('2014-07-14T13:01:39+00:00', new DateTimeZone('UTC')));
     $session->beginAttempt();
     // The candidate takes an hour (yes, an hour) to respond on Q07.3.
     $session->setTime(new DateTime('2014-07-14T14:01:39+00:00', new DateTimeZone('UTC')));
     $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::POINT, new QtiPoint(102, 113)))));
     $session->moveNext();
     // -- End of test, outcome processing performed correctly?
     $storage->persist($session);
     $session = $storage->retrieve($test, $sessionId);
     $this->assertEquals(AssessmentTestSessionState::CLOSED, $session->getState());
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiInteger', $session['NCORRECTS01']);
     $this->assertEquals(1, $session['NCORRECTS01']->getValue());
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiInteger', $session['NCORRECTS02']);
     $this->assertEquals(1, $session['NCORRECTS02']->getValue());
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiInteger', $session['NCORRECTS03']);
     $this->assertEquals(1, $session['NCORRECTS03']->getValue());
     $this->assertEquals(6, $session['NINCORRECT']->getValue());
     $this->assertEquals(6, $session['NRESPONSED']->getValue());
     $this->assertEquals(9, $session['NPRESENTED']->getValue());
     $this->assertEquals(9, $session['NSELECTED']->getValue());
     $this->assertInstanceOf('qtism\\common\\datatypes\\QtiFloat', $session['PERCENT_CORRECT']);
     $this->assertEquals(round(33.33333, 3), round($session['PERCENT_CORRECT']->getValue(), 3));
     // -- End of test, are durations correct?
     $this->assertTrue($session['itemsubset.duration']->equals(new QtiDuration('PT3699S')));
     // NO FEAR!
     $this->assertTrue($session['P01.duration']->equals(new QtiDuration('PT1H1M39S')));
     $this->assertTrue($session['S01.duration']->equals(new QtiDuration('PT14S')));
     $this->assertTrue($session['S02.duration']->equals(new QtiDuration('PT12S')));
     $this->assertTrue($session['S03.duration']->equals(new QtiDuration('PT1H1M13S')));
     $this->assertTrue($session['Q01.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q02.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q03.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q04.duration']->equals(new QtiDuration('PT5S')));
     $this->assertTrue($session['Q05.duration']->equals(new QtiDuration('PT1S')));
     $this->assertTrue($session['Q06.duration']->equals(new QtiDuration('PT2S')));
     $this->assertTrue($session['Q07.1.duration']->equals(new QtiDuration('PT10S')));
     $this->assertTrue($session['Q07.2.duration']->equals(new QtiDuration('PT1M')));
     $this->assertTrue($session['Q07.3.duration']->equals(new QtiDuration('PT1H')));
 }