diff --git a/composer.json b/composer.json index 389de390f..89df1d226 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.8", + "version": "0.9.9", "authors": [ { "name": "Open Assessment Technologies S.A.", diff --git a/qtism/runtime/common/State.php b/qtism/runtime/common/State.php index e80b5ae16..12408fec5 100644 --- a/qtism/runtime/common/State.php +++ b/qtism/runtime/common/State.php @@ -158,8 +158,9 @@ public function offsetGet($offset) { * Reset all test-level outcome variables to their defaults. * * @param boolean $preserveBuiltIn Whether the built-in outcome variable 'completionStatus'. + * @param array $preserve An array of identifiers reflecting variables that must not be reset but preserved. */ - public function resetOutcomeVariables($preserveBuiltIn = true) { + public function resetOutcomeVariables($preserveBuiltIn = true, array $preserve = array()) { $data = &$this->getDataPlaceHolder(); foreach (array_keys($data) as $k) { @@ -167,7 +168,7 @@ public function resetOutcomeVariables($preserveBuiltIn = true) { if ($preserveBuiltIn === true && $k === 'completionStatus') { continue; } - else { + else if (in_array($k, $preserve) === false) { $data[$k]->applyDefaultValue(); } } diff --git a/qtism/runtime/tests/AssessmentTestSession.php b/qtism/runtime/tests/AssessmentTestSession.php index 72f15f2af..81f683ef2 100644 --- a/qtism/runtime/tests/AssessmentTestSession.php +++ b/qtism/runtime/tests/AssessmentTestSession.php @@ -149,6 +149,13 @@ class AssessmentTestSession extends State { */ private $sessionManager; + /** + * An array of identifiers representing outcome variable that must not be applied their default value at outcome processing time. + * + * @var array + */ + private $preservedOutcomeVariables; + /** * Create a new AssessmentTestSession object. * @@ -167,6 +174,7 @@ public function __construct(AssessmentTest $assessmentTest, AbstractSessionManag $this->setPendingResponseStore(new PendingResponseStore()); $durationStore = new DurationStore(); $this->setDurationStore($durationStore); + $this->setPreservedOutcomeVariables(array()); // Take the outcomeDeclaration objects of the global scope. // Instantiate them with their defaults. @@ -414,6 +422,24 @@ public function setSessionManager(AbstractSessionManager $sessionManager) { public function getSessionManager() { return $this->sessionManager; } + + /** + * Set the identifiers of the outcome variables that must not be applied their default value at outcome processing time. + * + * @param array $identifiers + */ + public function setPreservedOutcomeVariables(array $identifiers) { + $this->preservedOutcomeVariables = $identifiers; + } + + /** + * Get the identifiers of the outcome variables that must not be applied their default value at outcome processing time. + * + * @return array + */ + public function getPreservedOutcomeVariables() { + return $this->preservedOutcomeVariables; + } /** * Get a weight by using a prefixed identifier e.g. 'Q01.weight1' @@ -1718,7 +1744,7 @@ protected function outcomeProcessing() { // As per QTI Spec: // The values of the test's outcome variables are always reset to their defaults prior // to carrying out the instructions described by the outcomeRules. - $this->resetOutcomeVariables(); + $this->resetOutcomeVariables(true, $this->getPreservedOutcomeVariables()); $outcomeProcessing = $this->getAssessmentTest()->getOutcomeProcessing(); diff --git a/test/qtism/runtime/tests/AssessmentTestSessionTest.php b/test/qtism/runtime/tests/AssessmentTestSessionTest.php index 6901b5686..daae87cac 100644 --- a/test/qtism/runtime/tests/AssessmentTestSessionTest.php +++ b/test/qtism/runtime/tests/AssessmentTestSessionTest.php @@ -1409,4 +1409,37 @@ public function testEmptySection() { // and the session is then closed. $this->assertEquals(AssessmentTestSessionState::CLOSED, $session->getState()); } + + public function testPreserveOutcomes() { + // Aims at testing that even a section of the test is empty, + // it is simply ignored at runtime. + $doc = new XmlCompactDocument(); + $doc->load(self::samplesDir() . 'custom/runtime/preserve_test_outcomes.xml'); + $manager = new SessionManager(); + + $session = $manager->createAssessmentTestSession($doc->getDocumentComponent()); + $session->setPreservedOutcomeVariables(array('PRESERVED')); + $session->beginTestSession(); + + $this->assertEquals('I will be preserved!', $session['PRESERVED']->getValue()); + $session['PRESERVED'] = new String('I am still preserved!'); + + $this->assertEquals(0, $session['NOTPRESERVED']->getValue()); + + $this->assertEquals('Q01', $session->getCurrentAssessmentItemRef()->getIdentifier()); + $session->beginAttempt(); + $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', BaseType::IDENTIFIER, Cardinality::SINGLE, new Identifier('ChoiceA'))))); + $session->moveNext(); + + $this->assertEquals('I am still preserved!', $session['PRESERVED']->getValue()); + $this->assertEquals(1, $session['NOTPRESERVED']->getValue()); + + $this->assertEquals('Q02', $session->getCurrentAssessmentItemRef()->getIdentifier()); + $session->beginAttempt(); + $session->endAttempt(new State(array(new ResponseVariable('RESPONSE', BaseType::IDENTIFIER, Cardinality::SINGLE, new Identifier('ChoiceB'))))); + $session->moveNext(); + + $this->assertEquals('I am still preserved!', $session['PRESERVED']->getValue()); + $this->assertEquals(1, $session['NOTPRESERVED']->getValue()); + } } \ No newline at end of file