Skip to content

Commit

Permalink
Improve diagnostic in TDML Runner
Browse files Browse the repository at this point in the history
Note that this change enforces the syntax of TDML better.
The dfdlInfoset element used to tolerate having spurious text in it,
so long as it also had a root element.
Two TDML files had to be fixed in Daffodil.
If TDML files in schema projects have these spurious characters that
were formerly ignored, this change will break
those tests.

DAFFODIL-533
  • Loading branch information
mbeckerle committed Nov 4, 2024
1 parent b019330 commit 3955ba5
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,15 @@

<complexType name="dfdlInfosetType" mixed="true">
<sequence>
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
<!--
Can be empty if type is 'file'.
Otherwise, can be any single element (maxOccurs unbounded removed),
which is the root element of the infoset.
Mixed="true" because when type='file' the content is just a string
that is the file path
-->
<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="1"/>
</sequence>
<attribute name="type" use="optional" default="infoset">
<simpleType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import java.nio.charset.StandardCharsets
import scala.collection.mutable
import scala.language.postfixOps
import scala.util.Try
import scala.xml.Elem
import scala.xml.Node
import scala.xml.NodeSeq
import scala.xml.NodeSeq.seqToNodeSeq
Expand Down Expand Up @@ -2769,41 +2770,52 @@ case class Infoset(i: NodeSeq, parent: TestCase) {

case class DFDLInfoset(di: Node, parent: Infoset) {

private lazy val infosetNodeSeq = {
val testCase: TestCase = parent.parent
val loader = testCase.parent.loader
val optDataSchema: Option[URI] = {
testCase.optSchemaFileURI.orElse(testCase.optEmbeddedSchema.map { _.uriForLoading })
}
val src =
(di \ "@type").toString match {
case "infoset" | "" => {
val rawElem = di.child.filter {
_.isInstanceOf[scala.xml.Elem]
}.head
UnitTestSchemaSource(rawElem, testCase.tcName)
}
case "file" => {
val path = di.text.trim()
val maybeURI = parent.parent.parent.findTDMLResource(path)
val uri = maybeURI.getOrElse(
throw new FileNotFoundException(
"TDMLRunner: infoset file '" + path + "' was not found"
)
)
URISchemaSource(uriToDiagnosticFile(uri), uri)
}
case value => Assert.abort("Unknown value for type attribute on dfdlInfoset: " + value)
}
private lazy val testCase: TestCase = parent.parent
private lazy val loader = testCase.parent.loader
private val ty: String = {
val t = (di \ "@type").text.trim
if (t.isEmpty) "infoset" else t
}

private val elemOrStr: Either[Elem, String] = {
val (elems, others) = di.child.partition(_.isInstanceOf[scala.xml.Elem])
(elems, others.text.trim) match {
case (Seq(elem: Elem), "") if (ty == "infoset") => Left(elem)
case (Seq(), str) if (ty == "file") => Right(str)
case _ =>
Assert.usageError(
"""dfdlInfoset element must contain a single root element or a file path (when 'type="file"')."""
)
}
}

lazy val contents: Elem = {
elemOrStr match {
case Left(elem) => elem
case Right(path) => infosetNodeFromFile(path)
}
}

private def infosetNodeFromFile(path: String): Elem = {

val maybeURI = parent.parent.parent.findTDMLResource(path)
val uri = maybeURI.getOrElse(
throw new FileNotFoundException(
"TDMLRunner: infoset file '" + path + "' was not found"
)
)
val infosetSrc = URISchemaSource(uriToDiagnosticFile(uri), uri)

val testSuite = testCase.parent
val before = testSuite.loadingExceptions.clone()

val elem = loader.load(infosetSrc, None) // no schema
//
// TODO: DAFFODIL-288 validate the infoset also
// You can pass the optDataSchema, which appears to be the correct thing
// but in many cases it doesn't seem to be able to resolve things.
//
val testSuite = testCase.parent
val before = testSuite.loadingExceptions.clone()
// val elem: Node = loader.load(src, optDataSchema)
val elem = loader.load(src, None) // no schema
// val elem: Node = loader.load(infosetSrc, optDataSchema)

// The expected infoset is loaded using the normalizeCRLFtoLF mode (which
// is the default), so MS-DOS/Windows CRLFs in expected data XML files will
Expand All @@ -2818,17 +2830,9 @@ case class DFDLInfoset(di: Node, parent: Infoset) {
val newExceptions = (testSuite.loadingExceptions -- before).toSeq
testCase.toss(TDMLException(newExceptions, None), None)
}
elem
elem.asInstanceOf[Elem]
}

lazy val contents = {
Assert.usage(
infosetNodeSeq.size == 1,
"dfdlInfoset element must contain a single root element"
)
val c = infosetNodeSeq.head
c
}
}

object DiagnosticType extends Enumeration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import java.io.File

import org.apache.daffodil.lib.Implicits._
import org.apache.daffodil.lib.Implicits.using
import org.apache.daffodil.lib.exceptions.UsageException
import org.apache.daffodil.lib.util._
import org.apache.daffodil.lib.xml.XMLUtils

Expand Down Expand Up @@ -336,6 +337,17 @@ class UnitTestTDMLRunner {
assertEquals(expected, actual.toList)
}

@Test def testDFDLInfosetEmptyDiagnosticMsg(): Unit = {
val xml = <tdml:dfdlInfoset/>
val exc = intercept[UsageException] {
val di = new DFDLInfoset(xml, null)
di.contents
}
val m = exc.getMessage
assertTrue(m.contains("dfdlInfoset"))
assertTrue(m.contains("single root element"))
}

@Test def testLSB1(): Unit = {
val xml = <document bitOrder="LSBFirst">
<documentPart type="bits">00000010</documentPart>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.apache.daffodil.processor.tdml

import java.io.File
import java.io.FileNotFoundException

import org.apache.daffodil.lib.Implicits._
import org.apache.daffodil.lib.Implicits.using
Expand Down Expand Up @@ -1061,4 +1062,32 @@ f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
assertTrue(exc.getMessage.contains("Either tdml:infoset or tdml:error"))
assertTrue(exc.getMessage.contains("must be present in the test"))
}

@Test def testInfosetFileNotFound() = {
val testSuite =
<tdml:testSuite suiteName="theSuiteName" xmlns:tdml={tdml} xmlns:dfdl={dfdl}
xmlns:xs={xsd}>
<tdml:defineSchema name="mySchema">
<xs:include schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
<dfdl:format ref="GeneralFormat"/>
<xs:element name="data" type="xs:int" dfdl:lengthKind="delimited"/>
</tdml:defineSchema>
<tdml:unparserTestCase name="infosetFileNotFound" root="data" model="mySchema">
<tdml:infoset>
<tdml:dfdlInfoset type="file">/this/does/not/exist.xml</tdml:dfdlInfoset>
</tdml:infoset>
<tdml:document/>
</tdml:unparserTestCase>
</tdml:testSuite>

val runner = new Runner(testSuite)
val e = intercept[FileNotFoundException] {
runner.runOneTest("infosetFileNotFound")
}
runner.reset
val msg = e.getMessage()
assertTrue(msg.contains("not found"))
assertTrue(msg.contains("/this/does/not/exist.xml"))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
<tdml:unparserTestCase name="nestedNoOVCinHiddenContext" root="e7"
model="ChoicesInHiddenContexts.dfdl.xsd" description="hidden group ref">
<tdml:infoset>
<tdml:dfdlInfoset>)
<tdml:dfdlInfoset>
<ex:e7/>
</tdml:dfdlInfoset>
</tdml:infoset>
Expand Down

0 comments on commit 3955ba5

Please sign in to comment.