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%4$s>',
+ $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%s>',
+ $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>%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%4$s>',
+ $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%s>',
+ $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%4$s>',
+ $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%s>',
+ $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>%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%4$s>',
+ $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%s>',
+ $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%4$s>',
+ $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%s>',
+ $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>%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%4$s>',
+ $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%s>',
+ $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>真%4$s><%5$s>まこと%5$s><%6$s>真%6$s>%7$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>%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">%4$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>%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