diff --git a/composer.json b/composer.json index a0b354182..47cdafea6 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "qtism/qtism", "description": "OAT QTI Software Module Library", "type": "library", - "version": "0.9.12", + "version": "0.9.13", "authors": [ { "name": "Open Assessment Technologies S.A.", diff --git a/qtism/runtime/tests/AssessmentTestSession.php b/qtism/runtime/tests/AssessmentTestSession.php index c3b4aaf28..ae6378ef4 100644 --- a/qtism/runtime/tests/AssessmentTestSession.php +++ b/qtism/runtime/tests/AssessmentTestSession.php @@ -1585,9 +1585,11 @@ protected function nextRouteItem($ignoreBranchings = false, $ignorePreConditions $this->endTestSession(); } else if ($target === 'EXIT_TESTPART') { + $route->next(); $this->moveNextTestPart(); } else if ($target === 'EXIT_SECTION') { + $route->next(); $this->moveNextAssessmentSection(); } else { @@ -1653,42 +1655,40 @@ public function moveNextTestPart() { $route = $this->getRoute(); $from = $route->current(); - $route->next(); - while ($route->valid() === true && $route->current()->getTestPart() === $from->getTestPart()) { - $this->nextRouteItem(); - } - - if ($this->isRunning() === true) { - $this->interactWithItemSession(); - } + while ($route->valid() === true && $route->current()->getTestPart() === $from->getTestPart()) { + $this->nextRouteItem(); + } + + if ($this->isRunning() === true) { + $this->interactWithItemSession(); + } } - + /** * Set the position in the Route at the very next assessmentSection in the route sequence. - * + * * * If there is no assessmentSection left in the flow, the test session ends gracefully. * * If there are still pending responses, they are processed. - * + * * @throws AssessmentTestSessionException If the test is not running. */ public function moveNextAssessmentSection() { - + if ($this->isRunning() === false) { $msg = "Cannot move to the next assessmentSection while the state of the test session is INITIAL or CLOSED."; throw new AssessmentTestSessionException($msg, AssessmentTestSessionException::STATE_VIOLATION); } - + $route = $this->getRoute(); $from = $route->current(); - $route->next(); - while ($route->valid() === true && $route->current()->getAssessmentSection() === $from->getAssessmentSection()) { - $this->nextRouteItem(); - } - - if ($this->isRunning() === true) { - $this->interactWithItemSession(); - } + while ($route->valid() === true && $route->current()->getAssessmentSection() === $from->getAssessmentSection()) { + $this->nextRouteItem(); + } + + if ($this->isRunning() === true) { + $this->interactWithItemSession(); + } } /** @@ -2526,4 +2526,4 @@ protected function buildCurrentItemSessionIdentifier() { protected function timeLimitsInForce($excludeItem = false) { return count($this->getCurrentRouteItem()->getTimeLimits($excludeItem)) !== 0; } -} \ No newline at end of file +} diff --git a/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php b/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php index 679e90986..e6344cc60 100644 --- a/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php +++ b/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php @@ -334,4 +334,35 @@ public function testMultipleOccurences() { $this->assertEquals(2, $session['Q01.2.duration']->getSeconds(true)); $this->assertEquals(0, $session['Q01.3.duration']->getSeconds(true)); } - } \ No newline at end of file + + public function testLastItemTimeout() { + $session = self::instantiate(self::samplesDir() . 'custom/runtime/timings/last_item_timeout.xml'); + $session->beginTestSession(); + + $session->beginAttempt(); + sleep(2); + $session->moveNext(); + $this->assertEquals(AssessmentTestSessionState::CLOSED, $session->getState()); + } + + public function testLastItemSectionTimeout() { + $session = self::instantiate(self::samplesDir() . 'custom/runtime/timings/last_item_section_timeout.xml'); + $session->beginTestSession(); + + $session->beginAttempt(); + sleep(2); + $session->moveNextAssessmentSection(); + $this->assertEquals(AssessmentTestSessionState::CLOSED, $session->getState()); + } + + public function testLastItemTestPartTimeout() { + $session = self::instantiate(self::samplesDir() . 'custom/runtime/timings/last_item_testpart_timeout.xml'); + $session->beginTestSession(); + + $session->beginAttempt(); + sleep(2); + $session->moveNextTestPart(); + $this->assertEquals(AssessmentTestSessionState::CLOSED, $session->getState()); + } + } + diff --git a/test/samples/custom/runtime/timings/last_item_section_timeout.xml b/test/samples/custom/runtime/timings/last_item_section_timeout.xml new file mode 100644 index 000000000..d8a89155e --- /dev/null +++ b/test/samples/custom/runtime/timings/last_item_section_timeout.xml @@ -0,0 +1,24 @@ + + + + + + + + + ChoiceB + + + + + 0.0 + + + + + + + diff --git a/test/samples/custom/runtime/timings/last_item_testpart_timeout.xml b/test/samples/custom/runtime/timings/last_item_testpart_timeout.xml new file mode 100644 index 000000000..7bf4f8ce4 --- /dev/null +++ b/test/samples/custom/runtime/timings/last_item_testpart_timeout.xml @@ -0,0 +1,24 @@ + + + + + + + + + ChoiceB + + + + + 0.0 + + + + + + + diff --git a/test/samples/custom/runtime/timings/last_item_timeout.xml b/test/samples/custom/runtime/timings/last_item_timeout.xml new file mode 100644 index 000000000..a21f9021e --- /dev/null +++ b/test/samples/custom/runtime/timings/last_item_timeout.xml @@ -0,0 +1,24 @@ + + + + + + + + + ChoiceB + + + + + 0.0 + + + + + + +