public function testOnlyScalarPropertiesConstructorAndProperties()
 {
     $component = new ItemSessionControl();
     $ctx = $this->createMarshallingContext();
     $scalarMarshaller = new PhpScalarMarshaller($ctx, $component->getMaxAttempts());
     $scalarMarshaller->marshall();
     $scalarMarshaller->setToMarshall($component->mustShowFeedback());
     $scalarMarshaller->marshall();
     $scalarMarshaller->setToMarshall($component->doesAllowReview());
     $scalarMarshaller->marshall();
     $scalarMarshaller->setToMarshall($component->mustShowSolution());
     $scalarMarshaller->marshall();
     $scalarMarshaller->setToMarshall($component->doesAllowComment());
     $scalarMarshaller->marshall();
     $scalarMarshaller->setToMarshall($component->mustValidateResponses());
     $scalarMarshaller->marshall();
     $scalarMarshaller->setToMarshall($component->doesAllowSkipping());
     $scalarMarshaller->marshall();
     $componentMarshaller = new PhpQtiComponentMarshaller($ctx, $component);
     $componentMarshaller->marshall();
     $expected = "\$integer_0 = 1;\n";
     $expected .= "\$boolean_0 = false;\n";
     $expected .= "\$boolean_1 = true;\n";
     $expected .= "\$boolean_2 = false;\n";
     $expected .= "\$boolean_3 = false;\n";
     $expected .= "\$boolean_4 = false;\n";
     $expected .= "\$boolean_5 = true;\n";
     $expected .= "\$itemsessioncontrol_0 = new qtism\\data\\ItemSessionControl();\n";
     $expected .= "\$itemsessioncontrol_0->setMaxAttempts(\$integer_0);\n";
     $expected .= "\$itemsessioncontrol_0->setShowFeedback(\$boolean_0);\n";
     $expected .= "\$itemsessioncontrol_0->setAllowReview(\$boolean_1);\n";
     $expected .= "\$itemsessioncontrol_0->setShowSolution(\$boolean_2);\n";
     $expected .= "\$itemsessioncontrol_0->setAllowComment(\$boolean_3);\n";
     $expected .= "\$itemsessioncontrol_0->setValidateResponses(\$boolean_4);\n";
     $expected .= "\$itemsessioncontrol_0->setAllowSkipping(\$boolean_5);\n";
     $this->assertEquals($expected, $this->getStream()->getBinary());
 }
 /**
  * End the attempt by providing responses or by another action. If $responses
  * is provided, the values found into it will be merged to the current state
  * before ResponseProcessing is executed.
  * 
  * * If the item is adaptive and the completionStatus is indicated to be 'completed', the item session ends.
  * * If the item is non-adaptive, and the number of attempts is exceeded, the item session ends and the completionStatus is set to 'completed'.
  * * Otherwise, the item session goes to the SUSPENDED state, waiting for a next attempt.
  * 
  * @param State $responses (optional) A State composed by the candidate's responses to the item.
  * @param boolean $responseProcessing (optional) Whether to execute the responseProcessing or not.
  * @param boolean $allowLateSubmission If set to true, maximum time limits will not be taken into account, even if the a maximum time limit is in force.
  * @throws AssessmentItemSessionException
  */
 public function endAttempt(State $responses = null, $responseProcessing = true, $allowLateSubmission = false)
 {
     // End of attempt, go in SUSPEND state.
     $this->suspend();
     // Flag to indicate if time is exceed or not.
     $maxTimeExceeded = false;
     // Is timeLimits in force.
     if ($this->hasTimeLimits() === true) {
         // As per QTI 2.1 Spec, Minimum times are only applicable to assessmentSections and
         // assessmentItems only when linear navigation mode is in effect.
         if ($this->isNavigationLinear() === true && $this->timeLimits->hasMinTime() === true) {
             if ($this->mustConsiderMinTime() === true && $this['duration']->getSeconds(true) <= $this->timeLimits->getMinTime()->getSeconds(true)) {
                 // An exception is thrown to prevent the numAttempts to be incremented.
                 // Suspend and wait for a next attempt.
                 $this->suspend();
                 $msg = "The minimal duration is not yet reached.";
                 throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::DURATION_UNDERFLOW);
             }
         }
         // Check if the maxTime constraint is respected.
         // If late submission is allowed but time exceeded, the item session will be considered 'completed'.
         // Otherwise, if late submission is not allowed but time exceeded, the session goes to 'incomplete'.
         if ($this->isMaxTimeReached() === true) {
             $maxTimeExceeded = true;
             if ($this->timeLimits->doesAllowLateSubmission() === false && $allowLateSubmission === false) {
                 $this['completionStatus']->setValue(self::COMPLETION_STATUS_INCOMPLETE);
                 $msg = "The maximal duration is exceeded.";
                 $this->endItemSession();
                 throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::DURATION_OVERFLOW);
             } else {
                 $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED);
                 $this->endItemSession();
             }
         }
     }
     // As per specs, when validateResponses is turned on (true) then the candidates are not
     // allowed to submit the item until they have provided valid responses for all
     // interactions. When turned off (false) invalid responses may be accepted by the
     // system.
     if ($this->submissionMode === SubmissionMode::INDIVIDUAL && $this->itemSessionControl->mustValidateResponses() === true) {
         // Use the correct expression to control if the responses
         // are correct.
         foreach ($responses as $response) {
             // @todo Reconsider the 'valid' concept.
             $correctExpression = new Correct($response->getIdentifier());
             $correctProcessor = new CorrectProcessor($correctExpression);
             $correctProcessor->setState($responses);
             try {
                 if ($correctProcessor->process() !== true) {
                     $responseIdentifier = $response->getIdentifier();
                     $msg = "The current itemSessionControl.validateResponses attribute is set to true but ";
                     $msg .= "response '{$responseIdentifier}' is incorrect.";
                     throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::INVALID_RESPONSE);
                 }
             } catch (ExpressionProcessingException $e) {
                 $responseIdentifier = $response->getIdentifier();
                 $msg = "The current itemSessionControl.validResponses attribute is set to true but an error ";
                 $msg .= "occured while trying to detect if response '{$responseIdentifier}' was correct.";
                 throw new AssessmentItemSessionException($msg, $this, AssessmentItemSessionException::RUNTIME_ERROR, $e);
             }
         }
     }
     // Apply the responses (if provided) to the current state and deal with the responseProcessing.
     if ($responses !== null) {
         foreach ($responses as $identifier => $value) {
             $this[$identifier] = $value->getValue();
         }
     }
     // Apply response processing.
     // As per QTI 2.1 specs, For Non-adaptive Items, the values of the outcome variables are reset to their
     // default values prior to each invocation of responseProcessing. For Adaptive Items the outcome variables
     // retain the values that were assigned to them during the previous invocation of response processing.
     // For more information, see Response Processing.
     //
     // The responseProcessing can be skipped by given a false value to $responseProcessing. Why?
     // Because when the SubmissionMode is SubmissionMode::SIMULTANEOUS, the responseProcessing must
     // deffered to the end of the current testPart.
     if ($responseProcessing === true) {
         if ($this->assessmentItem->isAdaptive() === false) {
             $this->resetOutcomeVariables();
         }
         $responseProcessing = $this->assessmentItem->getResponseProcessing();
         // Some items (especially to collect information) have no response processing!
         if ($responseProcessing !== null && ($responseProcessing->hasTemplate() === true || $responseProcessing->hasTemplateLocation() === true || count($responseProcessing->getResponseRules()) > 0)) {
             $engine = $this->createResponseProcessingEngine($responseProcessing);
             $engine->process();
         }
     }
     $maxAttempts = $this->itemSessionControl->getMaxAttempts();
     if ($this->submissionMode === SubmissionMode::SIMULTANEOUS) {
         $maxAttempts = 1;
     }
     // -- Adaptive or non-adaptive item, maximum time limit reached but late submission allowed.
     if ($maxTimeExceeded === true) {
         $this->endItemSession();
         $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED);
     } else {
         if ($this->assessmentItem->isAdaptive() === true && $this->submissionMode === SubmissionMode::INDIVIDUAL && $this['completionStatus']->getValue() === self::COMPLETION_STATUS_COMPLETED) {
             $this->endItemSession();
         } else {
             if ($this->assessmentItem->isAdaptive() === false && $this['numAttempts']->getValue() >= $maxAttempts) {
                 // Close only if $maxAttempts !== 0 because 0 means no limit!
                 if ($maxAttempts !== 0) {
                     $this->endItemSession();
                 }
                 // Even if there is no limit of attempts, we consider the item completed.
                 $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED);
             } else {
                 if ($this->assessmentItem->isAdaptive() === false && $this['numAttempts']->getValue() < $maxAttempts) {
                     $this['completionStatus']->setValue(self::COMPLETION_STATUS_COMPLETED);
                 }
             }
         }
     }
     // else...
     // Wait for the next attempt.
     $this->attempting = false;
 }