diff --git a/qtism/data/QtiDocument.php b/qtism/data/QtiDocument.php index 2f1c468a1..0d89eb821 100644 --- a/qtism/data/QtiDocument.php +++ b/qtism/data/QtiDocument.php @@ -26,69 +26,85 @@ use qtism\data\storage\StorageException; abstract class QtiDocument { - + /** - * + * * @var string */ private $version = '2.1'; - + /** - * + * * @var QtiComponent */ private $documentComponent; - + /** - * + * * @var string */ private $url; - + public function __construct($version = '2.1', QtiComponent $documentComponent = null) { $this->setVersion($version); $this->setDocumentComponent($documentComponent); } - + public function setVersion($version) { $this->version = $version; } - + public function getVersion() { return $this->version; } - + public function setDocumentComponent(QtiComponent $documentComponent = null) { $this->documentComponent = $documentComponent; } - + /** - * + * * @return QtiComponent */ public function getDocumentComponent() { return $this->documentComponent; } - + protected function setUrl($url) { $this->url = $url; } - + public function getUrl() { return $this->url; } - + /** - * + * * @param string $url - * @throws StorageException + * @throws StorageException */ abstract public function load($url); - + /** - * + * * @param string $url * @throws StorageException */ abstract public function save($url); -} \ No newline at end of file + + /** + * Load the document content from a string. + * + * @param string $data + * @return string + * @throws StorageException + */ + abstract public function loadFromString($data); + + /** + * Save the document content as a string. + * + * @return string + */ + abstract public function saveToString(); +} diff --git a/qtism/data/storage/php/PhpDocument.php b/qtism/data/storage/php/PhpDocument.php index b01d6df26..1da4b94fd 100644 --- a/qtism/data/storage/php/PhpDocument.php +++ b/qtism/data/storage/php/PhpDocument.php @@ -34,7 +34,6 @@ use qtism\data\AssessmentTest; use qtism\data\storage\php\marshalling\PhpCollectionMarshaller; use qtism\data\storage\php\marshalling\PhpQtiComponentMarshaller; -use qtism\data\QtiIdentifiable; use qtism\data\storage\php\marshalling\PhpArrayMarshaller; use qtism\common\beans\Bean; use qtism\common\collections\AbstractCollection; @@ -49,128 +48,158 @@ /** * Represents a PHP source code document containing the appropriate source code * to load a QTI Component and the components it contains. - * + * * @author Jérôme Bogaerts * */ class PhpDocument extends QtiDocument { - + /** * Create a new PhpDocument object. - * + * * @param QtiComponent $documentComponent The root QtiComponent object to contained by the PhpDocument. */ public function __construct($version = '2.1', QtiComponent $documentComponent = null) { parent::__construct($version, $documentComponent); } - + /** * Save the PhpDocument to a specific location. - * + * * @param string $url A URL (Uniform Resource Locator) describing where to save the document. + * @return $this * @throws PhpStorageException If an error occurs while saving. */ public function save($url) { - + try { + $stream = $this->transformToPhp(); + file_put_contents($url, $stream->getBinary()); + } + catch (StreamAccessException $e) { + $msg = "An error occured while writing the PHP source code stream."; + throw new PhpStorageException($msg, 0, $e); + } + } + + /** + * Return components as string + * + * @return Stream + * @throws PhpStorageException + */ + public function saveToString() + { + try { + $memoryStream = $this->transformToPhp(); + return $memoryStream->getBinary(); + } + catch (StreamAccessException $e) { + $msg = "An error occured while writing the PHP source code stream."; + throw new PhpStorageException($msg, 0, $e); + } + } + + /** + * Convert components to php source + * + * @return MemoryStream + * @throws PhpStorageException + */ + protected function transformToPhp() + { $stack = new SplStack(); $stack->push($this->getDocumentComponent()); - + // 1st/2nd pass marker. $marker = array(); - - try { - // marshalling context. - $stream = new MemoryStream(); - $stream->open(); - $streamAccess = new PhpStreamAccess($stream); - $streamAccess->writeOpeningTag(); - $ctx = new PhpMarshallingContext($streamAccess); - $ctx->setFormatOutput(true); - - while (count($stack) > 0) { - - $component = $stack->pop(); - $isMarked = in_array($component, $marker, true); - - if ($isMarked === false && ($component instanceof QtiComponent)) { - // -- QtiComponent node, 1st pass. - // Mark as explored. - array_push($marker, $component); - // Ask for a 2nd pass. - $stack->push($component); - - // Let's look at the Bean properties and ask for a future exploration. - $bean = new Bean($component, false, self::getBaseImplementation($component)); - $ctorGetters = $bean->getConstructorGetters(); - $bodyGetters = $bean->getGetters(true); - $getters = array_reverse(array_merge($bodyGetters->getArrayCopy(), $ctorGetters->getArrayCopy())); - - foreach ($getters as $getter) { - $stack->push(call_user_func(array($component, $getter->getName()))); - } - } - // Warning!!! Check for Coords Datatype objects. Indeed, it extends AbstractCollection, but must not be considered as it is. - else if ($isMarked === false && ($component instanceof AbstractCollection && !$component instanceof Coords)) { - // AbstractCollection node, 1st pass. - // Mark as explored. - array_push($marker, $component); - // Ask for a 2nd pass. - $stack->push($component); - - // Explore all values of the collection please! - $values = array_reverse($component->getArrayCopy()); - foreach ($values as $val) { - $stack->push($val); - } - } - else if ($isMarked === true && $component instanceof QtiComponent) { - // QtiComponent, 2nd pass. - $marshaller = new PhpQtiComponentMarshaller($ctx, $component); - $marshaller->setAsInstanceOf(self::getBaseImplementation($component)); - - if ($component === $this->getDocumentComponent()) { - $marshaller->setVariableName('rootcomponent'); - } - - $marshaller->marshall(); - } - else if ($component instanceof QtiDatatype) { - // Leaf node QtiDataType. - $marshaller = new PhpQtiDatatypeMarshaller($ctx, $component); - $marshaller->marshall(); - } - else if ($isMarked === true && $component instanceof AbstractCollection) { - // AbstractCollection, 2nd pass. - $marshaller = new PhpCollectionMarshaller($ctx, $component); - $marshaller->marshall(); - } - else if (PhpUtils::isScalar($component) === true) { - // Leaf node (QtiDatatype or PHP scalar (including the null value)). - $marshaller = new PhpScalarMarshaller($ctx, $component); - $marshaller->marshall(); + + // marshalling context. + $stream = new MemoryStream(); + $stream->open(); + $streamAccess = new PhpStreamAccess($stream); + $streamAccess->writeOpeningTag(); + $ctx = new PhpMarshallingContext($streamAccess); + $ctx->setFormatOutput(true); + + while (count($stack) > 0) { + + $component = $stack->pop(); + $isMarked = in_array($component, $marker, true); + + if ($isMarked === false && ($component instanceof QtiComponent)) { + // -- QtiComponent node, 1st pass. + // Mark as explored. + array_push($marker, $component); + // Ask for a 2nd pass. + $stack->push($component); + + // Let's look at the Bean properties and ask for a future exploration. + $bean = new Bean($component, false, self::getBaseImplementation($component)); + $ctorGetters = $bean->getConstructorGetters(); + $bodyGetters = $bean->getGetters(true); + $getters = array_reverse(array_merge($bodyGetters->getArrayCopy(), $ctorGetters->getArrayCopy())); + + foreach ($getters as $getter) { + $stack->push(call_user_func(array($component, $getter->getName()))); } - else if (is_array($component) === true) { - // Leaf node array. - $marshaller = new PhpArrayMarshaller($ctx, $component); - $marshaller->marshall(); + } + // Warning!!! Check for Coords Datatype objects. Indeed, it extends AbstractCollection, but must not be considered as it is. + else if ($isMarked === false && ($component instanceof AbstractCollection && !$component instanceof Coords)) { + // AbstractCollection node, 1st pass. + // Mark as explored. + array_push($marker, $component); + // Ask for a 2nd pass. + $stack->push($component); + + // Explore all values of the collection please! + $values = array_reverse($component->getArrayCopy()); + foreach ($values as $val) { + $stack->push($val); } - else { - $msg = "Datatype '" . gettype($component) . "' cannot be handled by the PhpDocument::save() method."; - throw new PhpStorageException($msg); + } + else if ($isMarked === true && $component instanceof QtiComponent) { + // QtiComponent, 2nd pass. + $marshaller = new PhpQtiComponentMarshaller($ctx, $component); + $marshaller->setAsInstanceOf(self::getBaseImplementation($component)); + + if ($component === $this->getDocumentComponent()) { + $marshaller->setVariableName('rootcomponent'); } + + $marshaller->marshall(); + } + else if ($component instanceof QtiDatatype) { + // Leaf node QtiDataType. + $marshaller = new PhpQtiDatatypeMarshaller($ctx, $component); + $marshaller->marshall(); + } + else if ($isMarked === true && $component instanceof AbstractCollection) { + // AbstractCollection, 2nd pass. + $marshaller = new PhpCollectionMarshaller($ctx, $component); + $marshaller->marshall(); + } + else if (PhpUtils::isScalar($component) === true) { + // Leaf node (QtiDatatype or PHP scalar (including the null value)). + $marshaller = new PhpScalarMarshaller($ctx, $component); + $marshaller->marshall(); + } + else if (is_array($component) === true) { + // Leaf node array. + $marshaller = new PhpArrayMarshaller($ctx, $component); + $marshaller->marshall(); + } + else { + $msg = "Datatype '" . gettype($component) . "' cannot be handled by the PhpDocument::save() method."; + throw new PhpStorageException($msg); } - - file_put_contents($url, $stream->getBinary()); - } - catch (StreamAccessException $e) { - $msg = "An error occured while writing the PHP source code stream."; - throw new PhpStorageException($msg, 0, $e); } + + return $stream; } - + /** * Load a PHP QTI document at the specified URL. - * + * * @param string $url A URL (Uniform Resource Locator) describing where to find the PHP document to load. * @throws PhpStorageException If an error occurs while loading the PHP file located at $url. */ @@ -179,18 +208,43 @@ public function load($url) { $msg = "The PHP document located at '${url}' is not readable or does not exist."; throw new PhpStorageException($msg); } - + try { require($url); $this->setDocumentComponent($rootcomponent); $this->setUrl($url); } catch (Exception $e) { - $msg = "A PHP Runtime Error occured while executing the PHP source code representing the document to be loaded at '${url}'."; + $msg = "A PHP Runtime Error occurred while executing the PHP source code representing the document to be loaded at '${url}'."; + throw new PhpStorageException($msg, 0, $e); + } + } + + /** + * Load a PHP QTI document using a stream resource. + * + * @param $data + * @throws PhpStorageException + */ + public function loadFromString($data) + { + try { + if (empty($data)) { + throw new Exception('Unable to find valid data to load.'); + } + $data = trim($data); + if (substr($data,0,5)==='' . $data); + } else { + eval($data); + } + $this->setDocumentComponent($rootcomponent); + } catch (Exception $e) { + $msg = "A PHP Runtime Error occurred while executing the PHP source code representing the document."; throw new PhpStorageException($msg, 0, $e); } } - + protected static function getBaseImplementation($object) { if ($object instanceof AssessmentTest) { return "qtism\\data\\AssessmentTest"; diff --git a/test/qtism/data/storage/php/PhpDocumentTest.php b/test/qtism/data/storage/php/PhpDocumentTest.php index 846a9899a..a7ea0d498 100644 --- a/test/qtism/data/storage/php/PhpDocumentTest.php +++ b/test/qtism/data/storage/php/PhpDocumentTest.php @@ -19,6 +19,16 @@ public function testSimpleLoad() { $this->assertEquals('php_storage_simple', $assessmentTest->getIdentifier()); } + public function testSimpleLoadFromString() { + $doc = new PhpDocument(); + $doc->loadFromString(file_get_contents(self::samplesDir() . 'custom/php/php_storage_simple.php')); + + $assessmentTest = $doc->getDocumentComponent(); + $this->assertInstanceOf('qtism\\data\\AssessmentTest', $assessmentTest); + + $this->assertEquals('php_storage_simple', $assessmentTest->getIdentifier()); + } + public function testSimpleSave() { $doc = new XmlCompactDocument(); @@ -30,6 +40,16 @@ public function testSimpleSave() { unlink($file); } + public function testSimpleSaveToString() { + $doc = new XmlCompactDocument(); + $doc->load(self::samplesDir() . 'custom/php/php_storage_simple.xml'); + $phpDoc = new PhpDocument('2.1', $doc->getDocumentComponent()); + $phpStr = $phpDoc->saveToString(); + + $phpDoc->loadFromString($phpStr); + $this->assertEquals('php_storage_simple', $phpDoc->getDocumentComponent()->getIdentifier()); + } + public function testCustomOperatorOne() { $doc = new XmlDocument(); $doc->load(self::samplesDir() . 'custom/operators/custom_operator_1.xml'); @@ -99,6 +119,33 @@ public function testLoadTestSamples($testUri, $rootType) { $this->assertFalse(file_exists($file)); } + /** + * + * @dataProvider loadTestSamplesDataProvider + * @param string $testUri + * @param string $rootType The expected fully qualified class name of the document component. + */ + public function testLoadTestSamplesFromString($testUri, $rootType) { + // Basic XML -> PHP transormation + saveTotring + loadFromString + $xmlDoc = new XmlDocument('2.1'); + $xmlDoc->loadFromString(file_get_contents($testUri)); + + $phpDoc = new PhpDocument(); + $phpDoc->setDocumentComponent($xmlDoc->getDocumentComponent()); + + $file = tempnam('/tmp', 'qsm'); + file_put_contents($file, $phpDoc->saveToString()); + + $phpDoc = new PhpDocument(); + $phpDoc->loadFromString(file_get_contents($file)); + + $this->assertInstanceOf($rootType, $phpDoc->getDocumentComponent()); + $this->assertNull($phpDoc->getUrl()); + + unlink($file); + $this->assertFalse(file_exists($file)); + } + public function testLoadInteractionMixSaschsen() { $xmlDoc = new XmlDocument('2.1'); $xmlDoc->load(self::samplesDir() . 'ims/tests/interaction_mix_sachsen/interaction_mix_sachsen.xml'); @@ -119,61 +166,61 @@ public function testLoadInteractionMixSaschsen() { public function loadTestSamplesDataProvider() { return array( - array(self::samplesDir() . 'ims/tests/arbitrary_collections_of_item_outcomes/arbitrary_collections_of_item_outcomes.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/arbitrary_weighting_of_item_outcomes/arbitrary_weighting_of_item_outcomes.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/basic_statistics_as_outcomes/basic_statistics_as_outcomes.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/branching_based_on_the_response_to_an_assessmentitem/branching_based_on_the_response_to_an_assessmentitem.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/controlling_the_duration_of_an_item_attempt/controlling_the_duration_of_an_item_attempt.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/controlling_item_feedback_in_relation_to_the_test/controlling_item_feedback_in_relation_to_the_test.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/early_termination_of_test_based_on_accumulated_item_outcomes/early_termination_of_test_based_on_accumulated_item_outcomes.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/feedback_examples_test/feedback_examples_test.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/golden_required_items_and_sections/golden_required_items_and_sections.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/interaction_mix_sachsen/interaction_mix_sachsen.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/items_arranged_into_sections_within_tests/items_arranged_into_sections_within_tests.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/mapping_item_outcomes_prior_to_aggregation/mapping_item_outcomes_prior_to_aggregation.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/randomizing_the_order_of_items_and_sections/randomizing_the_order_of_items_and_sections.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/sets_of_items_with_leading_material/sets_of_items_with_leading_material.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/simple_feedback_test/simple_feedback_test.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'ims/tests/specifiying_the_number_of_allowed_attempts/specifiying_the_number_of_allowed_attempts.xml', 'qtism\\data\\AssessmentTest'), - array(self::samplesDir() . 'rendering/various_content.xml', 'qtism\\data\\content\\RubricBlock'), - array(self::samplesDir() . 'rendering/associateinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/choiceinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/choiceinteraction_2.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/drawinginteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/endattemptinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/extendedtextinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/gapmatchinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/graphicgapmatchinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/graphicorderinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/hotspotinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/hottextinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/inlinechoiceinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/itembodywithfeedback_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/matchinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/mediainteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/mediainteraction_2.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/mediainteraction_3.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/orderinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/selectpointinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/positionobjectinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/sliderinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/textentryinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/uploadinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), - array(self::samplesDir() . 'rendering/itemfeedback_1.xml', 'qtism\\data\\AssessmentItem'), - array(self::samplesDir() . 'rendering/empty_object.xml', 'qtism\\data\\content\\xhtml\\Object'), - array(self::samplesDir() . 'rendering/empty_rubricblock.xml', 'qtism\\data\\content\\RubricBlock'), - array(self::samplesDir() . 'rendering/rubricblock_1.xml', 'qtism\\data\\content\\RubricBlock'), - array(self::samplesDir() . 'rendering/rubricblock_2.xml', 'qtism\\data\\content\\RubricBlock'), - array(self::samplesDir() . 'rendering/rubricblock_3.xml', 'qtism\\data\\content\\RubricBlock'), - array(self::samplesDir() . 'rendering/math_1.xml', 'qtism\\data\\AssessmentItem'), - array(self::samplesDir() . 'rendering/math_2.xml', 'qtism\\data\\AssessmentItem'), - array(self::samplesDir() . 'rendering/math_3.xml', 'qtism\\data\\AssessmentItem'), - array(self::samplesDir() . 'rendering/math_4.xml', 'qtism\\data\\Content\\RubricBlock'), - array(self::samplesDir() . 'custom/operators/custom_operator_1.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), - array(self::samplesDir() . 'custom/operators/custom_operator_2.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), - array(self::samplesDir() . 'custom/operators/custom_operator_3.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), - array(self::samplesDir() . 'custom/operators/custom_operator_nested_1.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), - array(self::samplesDir() . 'custom/interactions/custom_interaction_pci.xml', 'qtism\\data\\AssessmentItem') + array(self::samplesDir() . 'ims/tests/arbitrary_collections_of_item_outcomes/arbitrary_collections_of_item_outcomes.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/arbitrary_weighting_of_item_outcomes/arbitrary_weighting_of_item_outcomes.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/basic_statistics_as_outcomes/basic_statistics_as_outcomes.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/branching_based_on_the_response_to_an_assessmentitem/branching_based_on_the_response_to_an_assessmentitem.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/controlling_the_duration_of_an_item_attempt/controlling_the_duration_of_an_item_attempt.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/controlling_item_feedback_in_relation_to_the_test/controlling_item_feedback_in_relation_to_the_test.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/early_termination_of_test_based_on_accumulated_item_outcomes/early_termination_of_test_based_on_accumulated_item_outcomes.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/feedback_examples_test/feedback_examples_test.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/golden_required_items_and_sections/golden_required_items_and_sections.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/interaction_mix_sachsen/interaction_mix_sachsen.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/items_arranged_into_sections_within_tests/items_arranged_into_sections_within_tests.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/mapping_item_outcomes_prior_to_aggregation/mapping_item_outcomes_prior_to_aggregation.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/randomizing_the_order_of_items_and_sections/randomizing_the_order_of_items_and_sections.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/sets_of_items_with_leading_material/sets_of_items_with_leading_material.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/simple_feedback_test/simple_feedback_test.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'ims/tests/specifiying_the_number_of_allowed_attempts/specifiying_the_number_of_allowed_attempts.xml', 'qtism\\data\\AssessmentTest'), + array(self::samplesDir() . 'rendering/various_content.xml', 'qtism\\data\\content\\RubricBlock'), + array(self::samplesDir() . 'rendering/associateinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/choiceinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/choiceinteraction_2.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/drawinginteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/endattemptinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/extendedtextinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/gapmatchinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/graphicgapmatchinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/graphicorderinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/hotspotinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/hottextinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/inlinechoiceinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/itembodywithfeedback_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/matchinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/mediainteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/mediainteraction_2.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/mediainteraction_3.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/orderinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/selectpointinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/positionobjectinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/sliderinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/textentryinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/uploadinteraction_1.xml', 'qtism\\data\\content\\ItemBody'), + array(self::samplesDir() . 'rendering/itemfeedback_1.xml', 'qtism\\data\\AssessmentItem'), + array(self::samplesDir() . 'rendering/empty_object.xml', 'qtism\\data\\content\\xhtml\\Object'), + array(self::samplesDir() . 'rendering/empty_rubricblock.xml', 'qtism\\data\\content\\RubricBlock'), + array(self::samplesDir() . 'rendering/rubricblock_1.xml', 'qtism\\data\\content\\RubricBlock'), + array(self::samplesDir() . 'rendering/rubricblock_2.xml', 'qtism\\data\\content\\RubricBlock'), + array(self::samplesDir() . 'rendering/rubricblock_3.xml', 'qtism\\data\\content\\RubricBlock'), + array(self::samplesDir() . 'rendering/math_1.xml', 'qtism\\data\\AssessmentItem'), + array(self::samplesDir() . 'rendering/math_2.xml', 'qtism\\data\\AssessmentItem'), + array(self::samplesDir() . 'rendering/math_3.xml', 'qtism\\data\\AssessmentItem'), + array(self::samplesDir() . 'rendering/math_4.xml', 'qtism\\data\\Content\\RubricBlock'), + array(self::samplesDir() . 'custom/operators/custom_operator_1.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), + array(self::samplesDir() . 'custom/operators/custom_operator_2.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), + array(self::samplesDir() . 'custom/operators/custom_operator_3.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), + array(self::samplesDir() . 'custom/operators/custom_operator_nested_1.xml', 'qtism\\data\\expressions\\operators\\CustomOperator'), + array(self::samplesDir() . 'custom/interactions/custom_interaction_pci.xml', 'qtism\\data\\AssessmentItem') ); } } diff --git a/test/qtism/data/storage/xml/XmlAssessmentItemDocumentTest.php b/test/qtism/data/storage/xml/XmlAssessmentItemDocumentTest.php index d67622d42..dc99edca7 100644 --- a/test/qtism/data/storage/xml/XmlAssessmentItemDocumentTest.php +++ b/test/qtism/data/storage/xml/XmlAssessmentItemDocumentTest.php @@ -1,11 +1,8 @@ getDocumentComponent(); $this->assertInstanceOf('qtism\\data\\AssessmentItem', $assessmentItem); } + + /** + * @dataProvider validFileProvider + */ + public function testLoadFromString($uri) { + $doc = new XmlDocument('2.1'); + $doc->loadFromString(file_get_contents($uri)); + + $assessmentItem = $doc->getDocumentComponent(); + $this->assertInstanceOf('qtism\\data\\AssessmentItem', $assessmentItem); + } /** * @dataProvider validFileProvider @@ -44,6 +52,27 @@ public function testWrite($uri) { // Nobody else touched it? $this->assertFalse(file_exists($file)); } + + /** + * @dataProvider validFileProvider + */ + public function testSaveToString($uri) { + $doc = new XmlDocument('2.1'); + $doc->load($uri); + + $assessmentItem = $doc->getDocumentComponent(); + $this->assertInstanceOf('qtism\\data\\AssessmentItem', $assessmentItem); + + $file = tempnam('/tmp', 'qsm'); + file_put_contents($file, $doc->saveToString()); + + $this->assertTrue(file_exists($file)); + $this->testLoadFromString($file); + + unlink($file); + // Nobody else touched it? + $this->assertFalse(file_exists($file)); + } public function testLoad21() { $file = self::samplesDir() . 'ims/items/2_1/associate.xml'; @@ -289,4 +318,4 @@ private static function decorateUri($uri, $version = '2.1') { return self::samplesDir() . 'ims/items/2_0/' . $uri; } } -} \ No newline at end of file +} diff --git a/test/qtism/runtime/pci/json/JsonMarshallerTest.php b/test/qtism/runtime/pci/json/JsonMarshallerTest.php index 9252ed9ee..47e1d07e4 100644 --- a/test/qtism/runtime/pci/json/JsonMarshallerTest.php +++ b/test/qtism/runtime/pci/json/JsonMarshallerTest.php @@ -198,8 +198,8 @@ public function marshallMultipleProvider() { $returnValue[] = array($container, $json); // duration multiple("P3Y6M4DT12H30M5S", "P4Y"). - $container = new MultipleContainer(BaseType::DURATION, array(new Duration('P3Y6M'), new Duration('P4Y'))); - $json = json_encode(array('list' => array('duration' => array('P3Y6M', 'P4Y')))); + $container = new MultipleContainer(BaseType::DURATION, array(new Duration('PT4M10S'), new Duration('P4Y'))); + $json = json_encode(array('list' => array('duration' => array('PT4M10S', 'P4Y')))); $returnValue[] = array($container, $json); // uri multiple("file:///aFile.txt", "file:///abc.txt"). diff --git a/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php b/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php index 0ff4dfde0..b4f98d170 100644 --- a/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php +++ b/test/qtism/runtime/tests/AssessmentTestSessionTimingTest.php @@ -345,12 +345,12 @@ public function testSuspendResume() { $assessmentTestSession->beginAttempt(); $assessmentTestSession->suspend(); - sleep(1); + sleep(2); $this->assertEquals(AssessmentTestSessionState::SUSPENDED, $assessmentTestSession->getState()); $assessmentTestSession->resume(); - // Even if we slept a second, no additional time taken into account because the test was suspended. + // Even if we slept two seconds, no additional time taken into account because the test was suspended. $this->assertEquals(0, $assessmentTestSession['Q01.duration']->getSeconds(true)); $this->assertEquals(AssessmentTestSessionState::INTERACTING, $assessmentTestSession->getState());