diff --git a/src/qtism/data/content/xhtml/html5/Rb.php b/src/qtism/data/content/xhtml/html5/Rb.php new file mode 100644 index 000000000..d24397ec0 --- /dev/null +++ b/src/qtism/data/content/xhtml/html5/Rb.php @@ -0,0 +1,81 @@ +setContent(new InlineCollection()); + } + + public function getQtiClassName() + { + return self::QTI_CLASS_NAME; + } + + public function getComponents() + { + return $this->getContent(); + } + + /** + * Set the collection of Flow objects composing the Div. + * + * @param InlineCollection $content A collection of Flow objects. + */ + public function setContent(InlineCollection $content) + { + $this->content = $content; + } + + /** + * Get the collection of Flow objects composing the Div. + * + * @return InlineCollection + */ + public function getContent() + { + return $this->content; + } +} diff --git a/src/qtism/data/content/xhtml/html5/Rp.php b/src/qtism/data/content/xhtml/html5/Rp.php new file mode 100644 index 000000000..06e4b5220 --- /dev/null +++ b/src/qtism/data/content/xhtml/html5/Rp.php @@ -0,0 +1,81 @@ +setContent(new InlineCollection()); + } + + public function getQtiClassName() + { + return self::QTI_CLASS_NAME; + } + + public function getComponents() + { + return $this->getContent(); + } + + /** + * Set the collection of Flow objects composing the Div. + * + * @param InlineCollection $content A collection of Flow objects. + */ + public function setContent(InlineCollection $content) + { + $this->content = $content; + } + + /** + * Get the collection of Flow objects composing the Div. + * + * @return InlineCollection + */ + public function getContent() + { + return $this->content; + } +} diff --git a/src/qtism/data/content/xhtml/html5/Rt.php b/src/qtism/data/content/xhtml/html5/Rt.php new file mode 100644 index 000000000..58772cfed --- /dev/null +++ b/src/qtism/data/content/xhtml/html5/Rt.php @@ -0,0 +1,81 @@ +setContent(new InlineCollection()); + } + + public function getQtiClassName() + { + return self::QTI_CLASS_NAME; + } + + public function getComponents() + { + return $this->getContent(); + } + + /** + * Set the collection of Flow objects composing the Div. + * + * @param InlineCollection $content A collection of Flow objects. + */ + public function setContent(InlineCollection $content) + { + $this->content = $content; + } + + /** + * Get the collection of Flow objects composing the Div. + * + * @return InlineCollection + */ + public function getContent() + { + return $this->content; + } +} diff --git a/src/qtism/data/content/xhtml/html5/Ruby.php b/src/qtism/data/content/xhtml/html5/Ruby.php new file mode 100644 index 000000000..8e18e605b --- /dev/null +++ b/src/qtism/data/content/xhtml/html5/Ruby.php @@ -0,0 +1,81 @@ +setContent(new FlowCollection()); + } + + public function getQtiClassName() + { + return self::QTI_CLASS_NAME; + } + + public function getComponents() + { + return $this->getContent(); + } + + /** + * Set the collection of Flow objects composing the Div. + * + * @param FlowCollection $content A collection of Flow objects. + */ + public function setContent(FlowCollection $content) + { + $this->content = $content; + } + + /** + * Get the collection of Flow objects composing the Div. + * + * @return FlowCollection + */ + public function getContent() + { + return $this->content; + } +} diff --git a/src/qtism/data/storage/xml/marshalling/ContentMarshaller.php b/src/qtism/data/storage/xml/marshalling/ContentMarshaller.php index 941dad67f..ddf0e757e 100644 --- a/src/qtism/data/storage/xml/marshalling/ContentMarshaller.php +++ b/src/qtism/data/storage/xml/marshalling/ContentMarshaller.php @@ -54,6 +54,10 @@ use qtism\data\content\TemplateInline; use qtism\data\content\xhtml\html5\Figcaption; use qtism\data\content\xhtml\html5\Figure; +use qtism\data\content\xhtml\html5\Rb; +use qtism\data\content\xhtml\html5\Rp; +use qtism\data\content\xhtml\html5\Rt; +use qtism\data\content\xhtml\html5\Ruby; use qtism\data\content\xhtml\lists\Dl; use qtism\data\content\xhtml\lists\DlElement; use qtism\data\content\xhtml\lists\Li; @@ -144,6 +148,10 @@ public function __construct($version) 'i', 'kbd', 'q', + Ruby::QTI_CLASS_NAME, + Rb::QTI_CLASS_NAME, + Rp::QTI_CLASS_NAME, + Rt::QTI_CLASS_NAME, 'samp', 'small', 'span', @@ -181,7 +189,7 @@ public function __construct($version) 'feedbackBlock', 'bdo', Figure::QTI_CLASS_NAME_FIGURE, - Figcaption::QTI_CLASS_NAME_FIGCAPTION + Figcaption::QTI_CLASS_NAME_FIGCAPTION, ]; /** @@ -297,6 +305,14 @@ protected function getChildrenComponents(QtiComponent $component) return $component->getContent()->getArrayCopy(); } elseif ($component instanceof Figcaption) { return $component->getContent()->getArrayCopy(); + } elseif ($component instanceof Ruby) { + return $component->getContent()->getArrayCopy(); + } elseif ($component instanceof Rb) { + return $component->getContent()->getArrayCopy(); + } elseif ($component instanceof Rp) { + return $component->getContent()->getArrayCopy(); + } elseif ($component instanceof Rt) { + return $component->getContent()->getArrayCopy(); } } @@ -352,6 +368,8 @@ protected function getChildrenElements(DOMElement $element) return $this->getChildElementsByTagName($element, 'simpleAssociableChoice'); } elseif ($localName === Figure::QTI_CLASS_NAME_FIGURE) { return $this->getChildElementsByTagName($element, [Figcaption::QTI_CLASS_NAME_FIGCAPTION, Img::QTI_CLASS_NAME_IMG]); + } elseif ($localName === Ruby::QTI_CLASS_NAME) { + return $this->getChildElementsByTagName($element, [Rb::QTI_CLASS_NAME, Rp::QTI_CLASS_NAME, Rt::QTI_CLASS_NAME]); } elseif ($localName === 'gapImg') { return $this->getChildElementsByTagName($element, 'object'); } else { diff --git a/src/qtism/data/storage/xml/marshalling/Html5RbMarshaller.php b/src/qtism/data/storage/xml/marshalling/Html5RbMarshaller.php new file mode 100644 index 000000000..c1e10b300 --- /dev/null +++ b/src/qtism/data/storage/xml/marshalling/Html5RbMarshaller.php @@ -0,0 +1,83 @@ +hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $component; + } + + /** + * @param QtiComponent $component + * @param array $elements + * @return DOMElement + */ + protected function marshallChildrenKnown(QtiComponent $component, array $elements) + { + $element = parent::marshallChildrenKnown($component, $elements); + + if ($component->hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $element; + } + + protected static function getContentCollectionClassName() + { + return InlineCollection::class; + } +} diff --git a/src/qtism/data/storage/xml/marshalling/Html5RpMarshaller.php b/src/qtism/data/storage/xml/marshalling/Html5RpMarshaller.php new file mode 100644 index 000000000..9bb6e80ec --- /dev/null +++ b/src/qtism/data/storage/xml/marshalling/Html5RpMarshaller.php @@ -0,0 +1,83 @@ +hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $component; + } + + /** + * @param QtiComponent $component + * @param array $elements + * @return DOMElement + */ + protected function marshallChildrenKnown(QtiComponent $component, array $elements) + { + $element = parent::marshallChildrenKnown($component, $elements); + + if ($component->hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $element; + } + + protected static function getContentCollectionClassName() + { + return InlineCollection::class; + } +} diff --git a/src/qtism/data/storage/xml/marshalling/Html5RtMarshaller.php b/src/qtism/data/storage/xml/marshalling/Html5RtMarshaller.php new file mode 100644 index 000000000..19b124724 --- /dev/null +++ b/src/qtism/data/storage/xml/marshalling/Html5RtMarshaller.php @@ -0,0 +1,83 @@ +hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $component; + } + + /** + * @param QtiComponent $component + * @param array $elements + * @return DOMElement + */ + protected function marshallChildrenKnown(QtiComponent $component, array $elements) + { + $element = parent::marshallChildrenKnown($component, $elements); + + if ($component->hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $element; + } + + protected static function getContentCollectionClassName() + { + return InlineCollection::class; + } +} diff --git a/src/qtism/data/storage/xml/marshalling/Html5RubyMarshaller.php b/src/qtism/data/storage/xml/marshalling/Html5RubyMarshaller.php new file mode 100644 index 000000000..657aaa6f7 --- /dev/null +++ b/src/qtism/data/storage/xml/marshalling/Html5RubyMarshaller.php @@ -0,0 +1,84 @@ +hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $component; + } + + /** + * @param QtiComponent $component + * @param array $elements + * @return DOMElement + */ + protected function marshallChildrenKnown(QtiComponent $component, array $elements) + { + $element = parent::marshallChildrenKnown($component, $elements); + + if ($component->hasId()) { + $this->setDOMElementAttribute($element, 'id', $component->getId()); + } + + if ($component->hasClass()) { + $this->setDOMElementAttribute($element, 'class', $component->getClass()); + } + + return $element; + } + + protected static function getContentCollectionClassName() + { + return FlowCollection::class; + } +} diff --git a/src/qtism/data/storage/xml/marshalling/Qti22MarshallerFactory.php b/src/qtism/data/storage/xml/marshalling/Qti22MarshallerFactory.php index c4837864c..19d5729f9 100644 --- a/src/qtism/data/storage/xml/marshalling/Qti22MarshallerFactory.php +++ b/src/qtism/data/storage/xml/marshalling/Qti22MarshallerFactory.php @@ -26,6 +26,10 @@ use qtism\common\utils\Reflection; use qtism\data\content\xhtml\html5\Figcaption; use qtism\data\content\xhtml\html5\Figure; +use qtism\data\content\xhtml\html5\Rb; +use qtism\data\content\xhtml\html5\Rp; +use qtism\data\content\xhtml\html5\Rt; +use qtism\data\content\xhtml\html5\Ruby; use ReflectionClass; /** @@ -45,6 +49,10 @@ public function __construct() $this->addMappingEntry('bdo', SimpleInlineMarshaller::class); $this->addMappingEntry(Figure::QTI_CLASS_NAME_FIGURE, Html5FigureMarshaller::class); $this->addMappingEntry(Figcaption::QTI_CLASS_NAME_FIGCAPTION, Html5FigcaptionMarshaller::class); + $this->addMappingEntry(Ruby::QTI_CLASS_NAME, Html5RubyMarshaller::class); + $this->addMappingEntry(Rb::QTI_CLASS_NAME, Html5RbMarshaller::class); + $this->addMappingEntry(Rp::QTI_CLASS_NAME, Html5RpMarshaller::class); + $this->addMappingEntry(Rt::QTI_CLASS_NAME, Html5RtMarshaller::class); } /** diff --git a/src/qtism/runtime/rendering/markup/xhtml/RbRenderer.php b/src/qtism/runtime/rendering/markup/xhtml/RbRenderer.php new file mode 100644 index 000000000..ed7bf2d0f --- /dev/null +++ b/src/qtism/runtime/rendering/markup/xhtml/RbRenderer.php @@ -0,0 +1,46 @@ +hasId()) { + $fragment->firstChild->setAttribute('id', $component->getId()); + } + if ($component->hasClass()) { + $fragment->firstChild->setAttribute('class', $component->getClass()); + } + } +} diff --git a/src/qtism/runtime/rendering/markup/xhtml/RpRenderer.php b/src/qtism/runtime/rendering/markup/xhtml/RpRenderer.php new file mode 100644 index 000000000..e32105115 --- /dev/null +++ b/src/qtism/runtime/rendering/markup/xhtml/RpRenderer.php @@ -0,0 +1,46 @@ +hasId()) { + $fragment->firstChild->setAttribute('id', $component->getId()); + } + if ($component->hasClass()) { + $fragment->firstChild->setAttribute('class', $component->getClass()); + } + } +} diff --git a/src/qtism/runtime/rendering/markup/xhtml/RtRenderer.php b/src/qtism/runtime/rendering/markup/xhtml/RtRenderer.php new file mode 100644 index 000000000..9ceaa1156 --- /dev/null +++ b/src/qtism/runtime/rendering/markup/xhtml/RtRenderer.php @@ -0,0 +1,46 @@ +hasId()) { + $fragment->firstChild->setAttribute('id', $component->getId()); + } + if ($component->hasClass()) { + $fragment->firstChild->setAttribute('class', $component->getClass()); + } + } +} diff --git a/src/qtism/runtime/rendering/markup/xhtml/RubyRenderer.php b/src/qtism/runtime/rendering/markup/xhtml/RubyRenderer.php new file mode 100644 index 000000000..f480855c3 --- /dev/null +++ b/src/qtism/runtime/rendering/markup/xhtml/RubyRenderer.php @@ -0,0 +1,46 @@ +hasId()) { + $fragment->firstChild->setAttribute('id', $component->getId()); + } + if ($component->hasClass()) { + $fragment->firstChild->setAttribute('class', $component->getClass()); + } + } +} diff --git a/src/qtism/runtime/rendering/markup/xhtml/XhtmlRenderingEngine.php b/src/qtism/runtime/rendering/markup/xhtml/XhtmlRenderingEngine.php index 415e09931..d0637d411 100644 --- a/src/qtism/runtime/rendering/markup/xhtml/XhtmlRenderingEngine.php +++ b/src/qtism/runtime/rendering/markup/xhtml/XhtmlRenderingEngine.php @@ -25,6 +25,10 @@ use qtism\data\content\xhtml\html5\Figcaption; use qtism\data\content\xhtml\html5\Figure; +use qtism\data\content\xhtml\html5\Rb; +use qtism\data\content\xhtml\html5\Rt; +use qtism\data\content\xhtml\html5\Rp; +use qtism\data\content\xhtml\html5\Ruby; use qtism\runtime\rendering\markup\AbstractMarkupRenderingEngine; /** @@ -149,6 +153,10 @@ public function __construct() $this->registerRenderer('printedVariable', new PrintedVariableRenderer()); $this->registerRenderer('prompt', new PromptRenderer()); $this->registerRenderer('q', new QRenderer()); + $this->registerRenderer(Ruby::QTI_CLASS_NAME, new RubyRenderer()); + $this->registerRenderer(Rb::QTI_CLASS_NAME, new RbRenderer()); + $this->registerRenderer(Rt::QTI_CLASS_NAME, new RtRenderer()); + $this->registerRenderer(Rp::QTI_CLASS_NAME, new RpRenderer()); $this->registerRenderer('rubricBlock', new RubricBlockRenderer()); $this->registerRenderer('selectPointInteraction', new SelectPointInteractionRenderer()); $this->registerRenderer('simpleAssociableChoice', new SimpleAssociableChoiceRenderer()); diff --git a/src/qtism/runtime/tests/AssessmentItemSession.php b/src/qtism/runtime/tests/AssessmentItemSession.php index 1ea621c24..1a575802f 100644 --- a/src/qtism/runtime/tests/AssessmentItemSession.php +++ b/src/qtism/runtime/tests/AssessmentItemSession.php @@ -769,7 +769,7 @@ public function beginAttempt() * @throws AssessmentItemSessionException If the time limits in force are not respected, an error occurs during response processing, a state violation occurs. * @throws PhpStorageException */ - public function endAttempt(State $responses = null, $responseProcessing = true, $forceLateSubmission = false) + public function endAttempt(State $responses = null, $responseProcessing = true, $forceLateSubmission = false, $ignoreAllowSkippingCheck = false) { // Flag to indicate if time is exceed or not. $maxTimeExceeded = false; @@ -793,7 +793,9 @@ public function endAttempt(State $responses = null, $responseProcessing = true, // Apply the responses (if provided) to the current state. if ($responses !== null) { $this->checkResponseValidityConstraints($responses); - $this->checkAllowSkipping($responses); + if (!$ignoreAllowSkippingCheck) { + $this->checkAllowSkipping($responses); + } $this->mergeResponses($responses); } diff --git a/src/qtism/runtime/tests/AssessmentTestSession.php b/src/qtism/runtime/tests/AssessmentTestSession.php index 67ad3cb87..b78f15ec6 100644 --- a/src/qtism/runtime/tests/AssessmentTestSession.php +++ b/src/qtism/runtime/tests/AssessmentTestSession.php @@ -870,7 +870,7 @@ public function beginAttempt($allowLateSubmission = false) * @param bool $allowLateSubmission If set to true, maximum time limits will not be taken into account. * @throws AssessmentTestSessionException */ - public function endAttempt(State $responses, $allowLateSubmission = false) + public function endAttempt(State $responses, $allowLateSubmission = false, $ignoreAllowSkippingCheck = false) { if ($this->isRunning() === false) { $msg = 'Cannot end an attempt for the current item while the state of the test session is INITIAL or CLOSED.'; @@ -899,7 +899,7 @@ public function endAttempt(State $responses, $allowLateSubmission = false) } } else { try { - $session->endAttempt($responses, true, $allowLateSubmission); + $session->endAttempt($responses, true, $allowLateSubmission, $ignoreAllowSkippingCheck); } catch (Exception $e) { throw $this->transformException($e); } diff --git a/test/qtismtest/data/content/xhtml/html5/RbTest.php b/test/qtismtest/data/content/xhtml/html5/RbTest.php new file mode 100644 index 000000000..da09dfb45 --- /dev/null +++ b/test/qtismtest/data/content/xhtml/html5/RbTest.php @@ -0,0 +1,57 @@ +getId()); + self::assertEquals($class, $subject->getClass()); + } + + public function testCreateWithDefaultValues(): void + { + $subject = new Rb(); + + self::assertEquals('', $subject->getId()); + self::assertEquals('', $subject->getClass()); + } + + public function testGetQtiClassName(): void + { + $subject = new Rb(); + + self::assertEquals(self::SUBJECT_QTI_CLASS_NAME, $subject->getQtiClassName()); + } +} diff --git a/test/qtismtest/data/content/xhtml/html5/RpTest.php b/test/qtismtest/data/content/xhtml/html5/RpTest.php new file mode 100644 index 000000000..c94569e07 --- /dev/null +++ b/test/qtismtest/data/content/xhtml/html5/RpTest.php @@ -0,0 +1,57 @@ +getId()); + self::assertEquals($class, $subject->getClass()); + } + + public function testCreateWithDefaultValues(): void + { + $subject = new Rp(); + + self::assertEquals('', $subject->getId()); + self::assertEquals('', $subject->getClass()); + } + + public function testGetQtiClassName(): void + { + $subject = new Rp(); + + self::assertEquals(self::SUBJECT_QTI_CLASS_NAME, $subject->getQtiClassName()); + } +} diff --git a/test/qtismtest/data/content/xhtml/html5/RtTest.php b/test/qtismtest/data/content/xhtml/html5/RtTest.php new file mode 100644 index 000000000..7fa9d7ca5 --- /dev/null +++ b/test/qtismtest/data/content/xhtml/html5/RtTest.php @@ -0,0 +1,57 @@ +getId()); + self::assertEquals($class, $subject->getClass()); + } + + public function testCreateWithDefaultValues(): void + { + $subject = new Rt(); + + self::assertEquals('', $subject->getId()); + self::assertEquals('', $subject->getClass()); + } + + public function testGetQtiClassName(): void + { + $subject = new Rt(); + + self::assertEquals(self::SUBJECT_QTI_CLASS_NAME, $subject->getQtiClassName()); + } +} diff --git a/test/qtismtest/data/content/xhtml/html5/RubyTest.php b/test/qtismtest/data/content/xhtml/html5/RubyTest.php new file mode 100644 index 000000000..e911caf56 --- /dev/null +++ b/test/qtismtest/data/content/xhtml/html5/RubyTest.php @@ -0,0 +1,57 @@ +getId()); + self::assertEquals($class, $subject->getClass()); + } + + public function testCreateWithDefaultValues(): void + { + $subject = new Ruby(); + + self::assertEquals('', $subject->getId()); + self::assertEquals('', $subject->getClass()); + } + + public function testGetQtiClassName(): void + { + $subject = new Ruby(); + + self::assertEquals(self::SUBJECT_QTI_CLASS_NAME, $subject->getQtiClassName()); + } +} diff --git a/test/qtismtest/data/storage/xml/marshalling/Html5RbMarshallerTest.php b/test/qtismtest/data/storage/xml/marshalling/Html5RbMarshallerTest.php new file mode 100644 index 000000000..0e3ee0d7f --- /dev/null +++ b/test/qtismtest/data/storage/xml/marshalling/Html5RbMarshallerTest.php @@ -0,0 +1,135 @@ +assertHtml5MarshallingOnlyInQti22AndAbove(new Rb(), self::SUBJECT_QTI_CLASS_NAME); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $expected = sprintf( + '<%1$s id="%2$s" class="%3$s">text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $object = new Rb(null, null, $id, $class); + $object->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22WithDefaultValues(): void + { + $expected = sprintf( + '<%s>text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $object = new Rb(); + $object->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnMarshallerDoesNotExistInQti21(): void + { + $this->assertHtml5UnmarshallingOnlyInQti22AndAbove( + sprintf( + '<%s>', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ), + self::SUBJECT_QTI_CLASS_NAME + ); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnmarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $xml = sprintf( + '<%1$s id="%2$s" class="%3$s">text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Rb(null, null, $id, $class); + $expected->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertUnmarshalling($expected, $xml); + } + + public function testUnmarshall22WithDefaultValues(): void + { + $xml = sprintf( + '<%s>text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Rb(); + $expected->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertUnmarshalling($expected, $xml); + } +} diff --git a/test/qtismtest/data/storage/xml/marshalling/Html5RpMarshallerTest.php b/test/qtismtest/data/storage/xml/marshalling/Html5RpMarshallerTest.php new file mode 100644 index 000000000..26c02fed1 --- /dev/null +++ b/test/qtismtest/data/storage/xml/marshalling/Html5RpMarshallerTest.php @@ -0,0 +1,135 @@ +assertHtml5MarshallingOnlyInQti22AndAbove(new Rp(), self::SUBJECT_QTI_CLASS_NAME); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $expected = sprintf( + '<%1$s id="%2$s" class="%3$s">text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $object = new Rp(null, null, $id, $class); + $object->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22WithDefaultValues(): void + { + $expected = sprintf( + '<%s>text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $object = new Rp(); + $object->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnMarshallerDoesNotExistInQti21(): void + { + $this->assertHtml5UnmarshallingOnlyInQti22AndAbove( + sprintf( + '<%s>', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ), + self::SUBJECT_QTI_CLASS_NAME + ); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnmarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $xml = sprintf( + '<%1$s id="%2$s" class="%3$s">text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Rp(null, null, $id, $class); + $expected->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertUnmarshalling($expected, $xml); + } + + public function testUnmarshall22WithDefaultValues(): void + { + $xml = sprintf( + '<%s>text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Rp(); + $expected->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertUnmarshalling($expected, $xml); + } +} diff --git a/test/qtismtest/data/storage/xml/marshalling/Html5RtMarshallerTest.php b/test/qtismtest/data/storage/xml/marshalling/Html5RtMarshallerTest.php new file mode 100644 index 000000000..d3c1f16d2 --- /dev/null +++ b/test/qtismtest/data/storage/xml/marshalling/Html5RtMarshallerTest.php @@ -0,0 +1,135 @@ +assertHtml5MarshallingOnlyInQti22AndAbove(new Rt(), self::SUBJECT_QTI_CLASS_NAME); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $expected = sprintf( + '<%1$s id="%2$s" class="%3$s">text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $object = new Rt(null, null, $id, $class); + $object->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22WithDefaultValues(): void + { + $expected = sprintf( + '<%s>text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $object = new Rt(); + $object->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnMarshallerDoesNotExistInQti21(): void + { + $this->assertHtml5UnmarshallingOnlyInQti22AndAbove( + sprintf( + '<%s>', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ), + self::SUBJECT_QTI_CLASS_NAME + ); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnmarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $xml = sprintf( + '<%1$s id="%2$s" class="%3$s">text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Rt(null, null, $id, $class); + $expected->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertUnmarshalling($expected, $xml); + } + + public function testUnmarshall22WithDefaultValues(): void + { + $xml = sprintf( + '<%s>text content', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Rt(); + $expected->setContent(new InlineCollection([new TextRun('text content')])); + + $this->assertUnmarshalling($expected, $xml); + } +} diff --git a/test/qtismtest/data/storage/xml/marshalling/Html5RubyMarshallerTest.php b/test/qtismtest/data/storage/xml/marshalling/Html5RubyMarshallerTest.php new file mode 100644 index 000000000..fe344cbf3 --- /dev/null +++ b/test/qtismtest/data/storage/xml/marshalling/Html5RubyMarshallerTest.php @@ -0,0 +1,147 @@ +assertHtml5MarshallingOnlyInQti22AndAbove(new Ruby(), self::SUBJECT_QTI_CLASS_NAME); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $expected = sprintf( + '<%1$s id="%2$s" class="%3$s"><%4$s>真<%5$s>まこと<%6$s>真', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(Rt::QTI_CLASS_NAME), + $this->prefixTag(Rb::QTI_CLASS_NAME), + $this->prefixTag(Rp::QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $rb = new Rb(); + $rb->setContent(new InlineCollection([new TextRun('まこと')])); + + $rt = new Rt(); + $rt->setContent(new InlineCollection([new TextRun('真')])); + + $rp = new Rp(); + $rp->setContent(new InlineCollection([new TextRun('真')])); + + $object = new Ruby(null, null, $id, $class); + $object->setContent(new FlowCollection([ $rt, $rb, $rp])); + + $this->assertMarshalling($expected, $object); + } + + /** + * @throws MarshallerNotFoundException + * @throws MarshallingException + */ + public function testMarshall22WithDefaultValues(): void + { + $expected = sprintf( + '<%s/>', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $ruby = new Ruby(); + + $this->assertMarshalling($expected, $ruby); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnMarshallerDoesNotExistInQti21(): void + { + $this->assertHtml5UnmarshallingOnlyInQti22AndAbove( + sprintf( + '<%s>', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ), + self::SUBJECT_QTI_CLASS_NAME + ); + } + + /** + * @throws MarshallerNotFoundException + */ + public function testUnmarshall22(): void + { + $id = 'id'; + $class = 'testclass'; + + $xml = sprintf( + '<%1$s id="%2$s" class="%3$s">', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $id, + $class, + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Ruby(null, null, $id, $class); + + $this->assertUnmarshalling($expected, $xml); + } + + public function testUnmarshall22WithDefaultValues(): void + { + $xml = sprintf( + '<%s>', + $this->namespaceTag(self::SUBJECT_QTI_CLASS_NAME), + $this->prefixTag(self::SUBJECT_QTI_CLASS_NAME) + ); + + $expected = new Ruby(); + + $this->assertUnmarshalling($expected, $xml); + } +} diff --git a/test/qtismtest/runtime/expressions/VariableProcessorTest.php b/test/qtismtest/runtime/expressions/VariableProcessorTest.php index d1217ebec..6380514a2 100644 --- a/test/qtismtest/runtime/expressions/VariableProcessorTest.php +++ b/test/qtismtest/runtime/expressions/VariableProcessorTest.php @@ -106,12 +106,12 @@ public function testWeighted() $variableExpr = $this->createComponentFromXml(''); $variableProcessor->setExpression($variableExpr); $result = $variableProcessor->process(); - $this::assertEquals(11.11, $result[0]->getValue()); - $this::assertEquals(13.31, $result[1]->getValue()); + $this::assertEquals(11.11, round($result[0]->getValue(), 2)); + $this::assertEquals(13.31, round($result[1]->getValue(), 2)); // The value in the state must be unchanged. $stateVal = $assessmentTestSession['Q01.var2']; - $this::assertEquals(10.1, $stateVal[0]->getValue()); - $this::assertEquals(12.1, $stateVal[1]->getValue()); + $this::assertEquals(10.1, round($stateVal[0]->getValue(), 2)); + $this::assertEquals(12.1, round($stateVal[1]->getValue(), 2)); } public function testMultipleOccurences() diff --git a/test/qtismtest/runtime/tests/AssessmentItemSessionTest.php b/test/qtismtest/runtime/tests/AssessmentItemSessionTest.php index e7cc7c546..ad2f542f3 100644 --- a/test/qtismtest/runtime/tests/AssessmentItemSessionTest.php +++ b/test/qtismtest/runtime/tests/AssessmentItemSessionTest.php @@ -262,6 +262,22 @@ public function testSkippingForbiddenSimple() } } + public function testIgnoreSkippingForbiddenSimple() + { + $itemSession = $this->instantiateBasicAssessmentItemSession(); + $itemSessionControl = new ItemSessionControl(); + $itemSessionControl->setAllowSkipping(false); + $itemSession->setItemSessionControl($itemSessionControl); + $itemSession->beginItemSession(); + + $itemSession->beginAttempt(); + + // Test with empty state... + // no exception should be produced + $itemSession->endAttempt(new State([new ResponseVariable('RESPONSE', Cardinality::SINGLE, BaseType::IDENTIFIER)]), true, false, true); + $this->assertTrue(true); + } + public function testSkippingForbiddenDefaultValue() { $doc = new XmlDocument(); diff --git a/test/samples/custom/items/2_2/ruby_html5.xml b/test/samples/custom/items/2_2/ruby_html5.xml new file mode 100644 index 000000000..f8097d298 --- /dev/null +++ b/test/samples/custom/items/2_2/ruby_html5.xml @@ -0,0 +1,49 @@ + + + + + + + + + + 0 + + + +
+
+

村田

+ + + まこと + +

の出身地はどこですか

+
+
+ + 選びなさい + + + 北海道 + ほっかいどう + + + 東北 + 北陸 + 関東 + 甲信越 + 近畿 + 関西 + 四国 + 中国 + 九州 + +
+ +
\ No newline at end of file