Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
Conflicts:
	test/qtismtest/runtime/tests/AssessmentItemSessionTest.php
  • Loading branch information
= committed Jul 28, 2016
2 parents 0f39f50 + 29daead commit ff62a44
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 56 deletions.
52 changes: 2 additions & 50 deletions src/qtism/runtime/common/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,54 +200,6 @@ static public function isValidVariableIdentifier($string)
return preg_match($pattern, $string) === 1;
}

/**
* Makes $value compliant with baseType $targetBaseType, if $value is compliant. Otherwise,
* the original $value is returned.
*
* @param mixed $value A QTI Runtime compliant value.
* @param integer $targetBaseType The target baseType.
* @return mixed The juggled value if needed, otherwise the original value of $value.
*/
static public function juggle($value, $targetBaseType)
{
// A lot of people designing QTI items want to put float values
// in integer baseType'd variables... So let's go for type juggling!

$valueBaseType = self::inferBaseType($value);

if ($valueBaseType !== $targetBaseType && ($value instanceof MultipleContainer || $value instanceof OrderedContainer)) {

$class = get_class($value);

if ($valueBaseType === BaseType::FLOAT && $targetBaseType === BaseType::INTEGER) {
$value = new $class($targetBaseType, self::floatArrayToInteger($value->getArrayCopy()));
} elseif ($valueBaseType === BaseType::INTEGER && $targetBaseType === BaseType::FLOAT) {
$value = new $class($targetBaseType, self::integerArrayToFloat($value->getArrayCopy()));
} elseif ($valueBaseType === BaseType::IDENTIFIER && $targetBaseType === BaseType::STRING) {
$value = new $class($targetBaseType, $value->getArrayCopy());
} elseif ($valueBaseType === BaseType::STRING && $targetBaseType === BaseType::IDENTIFIER) {
$value = new $class($targetBaseType, $value->getArrayCopy());
} elseif ($valueBaseType === BaseType::URI && $targetBaseType === BaseType::STRING) {
$value = new $class($targetBaseType, $value->getArrayCopy());
} elseif ($valueBaseType === BaseType::STRING && $targetBaseType === BaseType::URI) {
$value = new $class($targetBaseType, $value->getArrayCopy());
} elseif ($valueBaseType === BaseType::URI && $targetBaseType === BaseType::IDENTIFIER) {
$value = new $class($targetBaseType, $value->getArrayCopy());
} elseif ($valueBaseType === BaseType::IDENTIFIER && $targetBaseType === BaseType::URI) {
$value = new $class($targetBaseType, $value->getArrayCopy());
}
} elseif ($valueBaseType !== $targetBaseType) {
// Scalar value.
if ($valueBaseType === BaseType::FLOAT && $targetBaseType === BaseType::INTEGER) {
$value = intval($value);
} elseif ($valueBaseType === BaseType::INTEGER && $targetBaseType === BaseType::FLOAT) {
$value = floatval($value);
}
}

return $value;
}

/**
* Transforms the content of float array to an integer array.
*
Expand Down Expand Up @@ -281,11 +233,11 @@ static public function integerArrayToFloat($integerArray)
}

/**
* Transform a given PHP runtime value to a QtiDatatype object.
* Transform a given PHP scalar value to a QtiScalar equivalent object.
*
* @param mixed|null $v
* @param integer $baseType A value from the BaseType enumeration.
* @return \qtism\common\datatypes\Integer|\qtism\common\datatypes\IntOrIdentifier|\qtism\common\datatypes\Identifier|\qtism\common\datatypes\String|\qtism\common\datatypes\Uri|\qtism\common\datatypes\Float|\qtism\common\datatypes\Boolean|null
* @return \qtism\common\datatypes\QtiScalar
*/
static public function valueToRuntime($v, $baseType)
{
Expand Down
5 changes: 5 additions & 0 deletions src/qtism/runtime/rules/SetValueProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public function process()
// juggle a little bit (int -> float, float -> int)
if ($val !== null && $var->getCardinality() === Cardinality::SINGLE) {
$baseType = $var->getBaseType();

// If we are trying to put a container in a scalar, let's make some changes...
if (($val->getCardinality() === Cardinality::MULTIPLE || $val->getCardinality() === Cardinality::ORDERED) && count($val) > 0) {
$val = $val[0];
}

if ($baseType === BaseType::INTEGER && $val instanceof QtiFloat) {
$val = new QtiInteger(intval($val->getValue()));
Expand Down
72 changes: 72 additions & 0 deletions test/qtismtest/runtime/processing/ResponseProcessingEngineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use qtism\runtime\rules\RuleProcessingException;
use qtism\runtime\common\ProcessingException;
use qtism\runtime\common\State;
use qtism\runtime\common\MultipleContainer;
use qtism\runtime\common\OutcomeVariable;
use qtism\runtime\common\ResponseVariable;
use qtism\runtime\processing\ResponseProcessingEngine;
Expand Down Expand Up @@ -131,4 +132,75 @@ public function testResponseProcessingExitResponse() {
$engine->process();
$this->assertEquals(1, $state['OUTCOME']->getValue());
}

public function testSetOutcomeValueWithSum() {
$responseProcessing = $this->createComponentFromXml('
<responseProcessing>
<responseCondition>
<responseIf>
<isNull>
<variable identifier="response-X" />
</isNull>
<setOutcomeValue identifier="score-X">
<baseValue baseType="integer">0</baseValue>
</setOutcomeValue>
</responseIf>
<responseElseIf>
<match>
<variable identifier="response-X" />
<correct identifier="response-X" />
</match>
<setOutcomeValue identifier="score-X">
<variable identifier="maxscore-X" />
</setOutcomeValue>
</responseElseIf>
<responseElse>
<setOutcomeValue identifier="score-X">
<baseValue baseType="integer">0</baseValue>
</setOutcomeValue>
</responseElse>
</responseCondition>
<setOutcomeValue identifier="SCORE">
<sum>
<variable identifier="score-X" />
</sum>
</setOutcomeValue>
<setOutcomeValue identifier="MAXSCORE">
<sum>
<variable identifier="maxscore-X" />
</sum>
</setOutcomeValue>
</responseProcessing>
');


$responseX = new ResponseVariable('response-X', Cardinality::MULTIPLE, BaseType::IDENTIFIER);
$responseX->setCorrectResponse(new MultipleContainer(BaseType::IDENTIFIER, array(new QtiIdentifier('ChoiceA'), new QtiIdentifier('ChoiceB'))));

$score = new OutcomeVariable('SCORE', Cardinality::SINGLE, BaseType::FLOAT, new QtiFloat(0.));
$maxScore = new OutcomeVariable('MAXSCORE', Cardinality::SINGLE, BaseType::FLOAT, new QtiFloat(0.));
$scoreX = new OutcomeVariable('score-X', Cardinality::SINGLE, BaseType::FLOAT, new QtiFloat(0.));
$maxScoreX = new OutcomeVariable('maxscore-X', Cardinality::SINGLE, BaseType::FLOAT, new QtiFloat(1.));

$state = new State(
array(
$responseX,
$score,
$maxScore,
$scoreX,
$maxScoreX
)
);

// Try correct response...
$state['response-X'][] = new QtiIdentifier('ChoiceA');
$state['response-X'][] = new QtiIdentifier('ChoiceB');

$engine = new ResponseProcessingEngine($responseProcessing, $state);
$engine->process();

$this->assertEquals(1., $state['score-X']->getValue());
$this->assertEquals(1., $state['SCORE']->getValue());
$this->assertEquals(1., $state['MAXSCORE']->getValue());
}
}
53 changes: 50 additions & 3 deletions test/qtismtest/runtime/rules/SetOutcomeValueProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,12 @@ public function testSetOutcomeValueWrongJugglingMultipleOne() {
$processor->process();
}

public function testSetOutcomeValueWrongJugglingMultipleTwo() {
public function testSetOutcomeValueJugglingMultiple() {
$rule = $this->createComponentFromXml('
<setOutcomeValue identifier="SCORE">
<multiple>
<baseValue baseType="float">1337.1337</baseValue>
<baseValue baseType="float">7777.7777</baseValue>
</multiple>
</setOutcomeValue>
');
Expand All @@ -110,9 +111,55 @@ public function testSetOutcomeValueWrongJugglingMultipleTwo() {
$score = new OutcomeVariable('SCORE', Cardinality::SINGLE, BaseType::INTEGER);
$state = new State(array($score));
$processor->setState($state);

// In this case, juggling will put the first entry of the multiple container
// in the target single cardinality variable. The float value is then changed into an integer value.
$processor->process();
$this->assertEquals(1337, $state['SCORE']->getValue());
}

public function testSetOutcomeValueJugglingOrdered() {
$rule = $this->createComponentFromXml('
<setOutcomeValue identifier="SCORE">
<ordered>
<baseValue baseType="float">1337.1337</baseValue>
<baseValue baseType="float">7777.7777</baseValue>
</ordered>
</setOutcomeValue>
');

$this->setExpectedException('qtism\\runtime\\rules\\RuleProcessingException');
$processor = new SetOutcomeValueProcessor($rule);
$score = new OutcomeVariable('SCORE', Cardinality::SINGLE, BaseType::INTEGER);
$state = new State(array($score));
$processor->setState($state);

// In this case, juggling will put the first entry of the multiple container
// in the target single cardinality variable. The float value is then changed into an integer value.
$processor->process();
$this->assertEquals(1337, $state['SCORE']->getValue());
}

public function testSetOutcomeValueWrongJugglingMultipleBecauseWrongBaseType() {
$rule = $this->createComponentFromXml('
<setOutcomeValue identifier="SCORE">
<multiple>
<baseValue baseType="string">hello</baseValue>
<baseValue baseType="string">world</baseValue>
</multiple>
</setOutcomeValue>
');

$processor = new SetOutcomeValueProcessor($rule);
$score = new OutcomeVariable('SCORE', Cardinality::SINGLE, BaseType::INTEGER);
$state = new State(array($score));
$processor->setState($state);

$this->setExpectedException(
'qtism\\runtime\\rules\\RuleProcessingException',
'Unable to set value hello to variable \'SCORE\' (cardinality = single, baseType = integer).'
);
$processor->process();

}

public function testSetOutcomeValueModerate() {
Expand Down Expand Up @@ -160,4 +207,4 @@ public function testSetOutcomeValueNoVariable() {

$processor->process();
}
}
}
32 changes: 29 additions & 3 deletions test/qtismtest/runtime/rules/SetTemplateValueProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,12 @@ public function testSetTemplateValueWrongJugglingMultipleOne() {
$processor->process();
}

public function testSetTemplateValueWrongJugglingMultipleTwo() {
public function testSetTemplateValueJugglingMultiple() {
$rule = $this->createComponentFromXml('
<setTemplateValue identifier="TPL1">
<multiple>
<baseValue baseType="float">1337.1337</baseValue>
<baseValue baseType="float">7777.7777</baseValue>
</multiple>
</setTemplateValue>
');
Expand All @@ -111,8 +112,33 @@ public function testSetTemplateValueWrongJugglingMultipleTwo() {
$state = new State(array($score));
$processor->setState($state);

$this->setExpectedException('qtism\\runtime\\rules\\RuleProcessingException');
$processor->process();
// In this case, juggling will put the first entry of the multiple container
// in the target single cardinality variable. The float value is then changed into an integer value.
$processor->process();
$this->assertEquals(1337, $state['TPL1']->getValue());
}

public function testSetTemplateValueJugglingOrdered() {
$rule = $this->createComponentFromXml('
<setTemplateValue identifier="TPL1">
<ordered>
<baseValue baseType="float">1337.1337</baseValue>
<baseValue baseType="float">7777.7777</baseValue>
</ordered>
</setTemplateValue>
');

$processor = new SetTemplateValueProcessor($rule);
$score = new TemplateVariable('TPL1', Cardinality::SINGLE, BaseType::INTEGER);
$state = new State(array($score));
$processor->setState($state);

$processor->process();
// In this case, juggling will put the first entry of the multiple container
// in the target single cardinality variable. The float value is then changed into an integer value.
$processor->process();
$this->assertEquals(1337, $state['TPL1']->getValue());
}

public function testSetOutcomeValueModerate() {
Expand Down Expand Up @@ -160,4 +186,4 @@ public function testSetOutcomeValueNoVariable() {

$processor->process();
}
}
}
14 changes: 14 additions & 0 deletions test/qtismtest/runtime/tests/AssessmentItemSessionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -445,4 +445,18 @@ public function testSetOutcomeValuesWithSum() {

$this->assertEquals(1., $itemSession['score-X']->getValue());
}

public function testSetOutcomeValuesWithSumJuggling() {
$doc = new XmlDocument();
$doc->load(self::samplesDir() . 'custom/items/set_outcome_values_with_sum_juggling.xml');

$itemSession = new AssessmentItemSession($doc->getDocumentComponent());
$itemSession->beginItemSession();
$itemSession->beginAttempt();

$responses = new State(array(new ResponseVariable('response-X', Cardinality::MULTIPLE, BaseType::IDENTIFIER, new MultipleContainer(BaseType::IDENTIFIER, array(new QtiIdentifier('ChoiceB'), new QtiIdentifier('ChoiceC'))))));
$itemSession->endAttempt($responses);

$this->assertEquals(1., $itemSession['score-X']->getValue());
}
}
Loading

0 comments on commit ff62a44

Please sign in to comment.