This is an automated email from the ASF dual-hosted git repository.

mbeckerle pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/main by this push:
     new dd9e836  Added test to confirm delimiter behavior.
dd9e836 is described below

commit dd9e836ba88c1598e8562d986ae6d63152693b64
Author: Michael Beckerle <[email protected]>
AuthorDate: Mon Jan 31 08:59:04 2022 -0500

    Added test to confirm delimiter behavior.
    
    DAFFODIL-2639
---
 .../runtime1/TestDelimiterFinalBacktracking.scala  |  89 ++++++++++++++++++
 .../scala/org/apache/daffodil/util/TestUtils.scala | 103 ++++++++++++++++++++-
 2 files changed, 187 insertions(+), 5 deletions(-)

diff --git 
a/daffodil-core/src/test/scala/org/apache/daffodil/runtime1/TestDelimiterFinalBacktracking.scala
 
b/daffodil-core/src/test/scala/org/apache/daffodil/runtime1/TestDelimiterFinalBacktracking.scala
new file mode 100644
index 0000000..b6508fb
--- /dev/null
+++ 
b/daffodil-core/src/test/scala/org/apache/daffodil/runtime1/TestDelimiterFinalBacktracking.scala
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.runtime1
+
+import org.apache.daffodil.util.SchemaUtils
+import org.apache.daffodil.util.StreamParser
+import org.junit.Test
+import org.apache.daffodil.xml.XMLUtils
+import org.junit.Assert.assertEquals
+
+class TestDelimiterFinalBacktracking {
+
+  val xsd = XMLUtils.XSD_NAMESPACE
+  val dfdl = XMLUtils.dfdlAppinfoSource // XMLUtils.DFDL_NAMESPACE
+  val xsi = XMLUtils.XSI_NAMESPACE
+  val example = XMLUtils.EXAMPLE_NAMESPACE
+
+  val schema1 =
+    <xs:element name="r" dfdl:lengthKind="implicit">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="a" type="xs:string"
+                      dfdl:lengthKind="delimited"
+                      dfdl:terminator="long longer"
+                      dfdl:ignoreCase="no" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  /**
+   * This test shows that even if Daffodil speculates forward to see if a 
longer
+   * delimiter is present, but it isn't, it resets the 
InputSourceDataInputStream
+   * to the position after the last character of the delimiter that was found.
+   *
+   * Daffodil has to speculate to see if the delimiter is "longer", not just 
"long", so
+   * it has to read the "e" and "R" characters to know that the first 
delimiter is just "long".
+   *
+   * If the input stream was a TCP network socket, then Daffodil would need to 
read the two
+   * bytes ("eR") past the first "long", and so would block waiting for those 
bytes to become available.
+   * But it does not consume them from its own InputSourceDataInputStream, 
which is what this test
+   * shows. Rather, daffodil looks at them, but backtracks them so they are 
not consumed. 
+   */
+  @Test def testParseTerminatorBacktrack1(): Unit = {
+    val testSchema = SchemaUtils.dfdlTestSchema(
+      <xs:include 
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+      <dfdl:format ref="tns:GeneralFormat"/>,
+      schema1,
+      elementFormDefault = "unqualified")
+
+    //
+    // Baseline behavior
+    //
+    {
+      val data = "AlongerBlong" // we should get "A" then "B"
+      val res = StreamParser.doStreamTest(testSchema, data)
+      val firstBytePos1b = ((res(0).bitPos1b - 1) / 8).toInt
+      assertEquals(7, firstBytePos1b)
+      assertEquals("Alonger", data.substring(0, firstBytePos1b))
+      assertEquals("A", (res(0).message \\ "a").text)
+      assertEquals("B", (res(1).message \\ "a").text)
+    }
+    //
+    // Now the actual test
+    //
+    val data = "AlongeRBlong" // we should get "A" then "eRB"
+    val res = StreamParser.doStreamTest(testSchema, data)
+    val firstBytePos1b = ((res(0).bitPos1b - 1) / 8).toInt
+    assertEquals(5, firstBytePos1b)
+    assertEquals("Along", data.substring(0, firstBytePos1b))
+    assertEquals("A", (res(0).message \\ "a").text)
+    assertEquals("eRB", (res(1).message \\ "a").text)
+  }
+
+}
diff --git 
a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala 
b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
index c4f5343..ad48517 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
@@ -23,12 +23,9 @@ import java.nio.channels.Channels
 import java.nio.channels.ReadableByteChannel
 import java.nio.channels.WritableByteChannel
 import scala.util.Try
-import scala.xml._
 import org.apache.commons.io.output.NullOutputStream
 import org.junit.Assert.assertEquals
 import org.apache.daffodil.Implicits._
-
-import java.io.InputStream
 import org.apache.daffodil.api.DFDL
 import org.apache.daffodil.api._
 import org.apache.daffodil.compiler.Compiler
@@ -39,13 +36,21 @@ import org.apache.daffodil.grammar.VariableMapFactory
 import org.apache.daffodil.infoset.InfosetInputter
 import org.apache.daffodil.infoset.InfosetOutputter
 import org.apache.daffodil.infoset.ScalaXMLInfosetInputter
-import org.apache.daffodil.infoset.ScalaXMLInfosetOutputter
-import org.apache.daffodil.io.InputSourceDataInputStream
 import org.apache.daffodil.processors.DataProcessor
 import org.apache.daffodil.processors.VariableMap
 import org.apache.daffodil.xml.XMLUtils
 import org.apache.daffodil.xml._
 
+import java.io.ByteArrayInputStream
+import org.apache.daffodil.infoset.ScalaXMLInfosetOutputter
+import org.apache.daffodil.io.InputSourceDataInputStream
+
+import java.io.InputStream
+import scala.collection.mutable.ArrayBuffer
+import scala.xml._
+
+
+
 object INoWarnU2 { ImplicitsSuppressUnusedImportWarning() }
 
 /*
@@ -371,3 +376,91 @@ class Fakes private () {
   lazy val fakeDP = new FakeDataProcessor
 
 }
+
+/**
+ * Testing class for streaming message parse behavior
+ */
+object StreamParser {
+  case class CompileFailure (diags: Seq[Diagnostic]) extends Exception("DFDL 
Schema Compile Failure"){
+    override def getMessage() = diags.map{ _.toString }.mkString(",\n")
+  }
+
+  /**
+   * Result object for parse calls. Just a tuple.
+   */
+  case class Result (message: Node, // document that is the current parse 
result, or null
+    diags: Seq[Diagnostic],  // diagnostics.
+    isProcessingError: Boolean,
+    isValidationError: Boolean,
+    bitPos1b: Long) {
+
+    def toXML: Node = {
+      <Result>
+        { message }
+        { if (!diags.isEmpty) {
+        <diagnostics>
+          { diags map { diag => <diagnostic>{ diag.toString} </diagnostic> } }
+        </diagnostics>
+        }
+      else Null
+      }
+      </Result> %
+        (if (isProcessingError) new 
UnprefixedAttribute("isProcessingError",isProcessingError.toString, Null) else 
Null) %
+        (if (isValidationError) new UnprefixedAttribute("isValidationError", 
isValidationError.toString, Null) else Null) %
+        new UnprefixedAttribute("bitPos1b", bitPos1b.toString, Null)
+    }
+  }
+
+
+  def doStreamTest(schema: Node, data: String): Seq[Result] = {
+    val mp = new StreamParser(schema)
+    val is: InputStream = new ByteArrayInputStream(data.getBytes("ascii"))
+    mp.setInputStream(is)
+    var r: StreamParser.Result = null
+    val results = new ArrayBuffer[Result]
+    val resStream = Stream.continually( mp.parse ).takeWhile( r => 
!r.isProcessingError)
+    resStream.toSeq
+  }
+}
+
+class StreamParser(val schema: Node) {
+
+  val outputter = new ScalaXMLInfosetOutputter()
+  var dis: InputSourceDataInputStream = _
+  var dp: DFDL.DataProcessor = _
+  //
+  // First compile the DFDL Schema
+  val c = Compiler()
+  val pf = c.compileNode(schema)
+  val pfDiags = pf.getDiagnostics
+  if (pf.isError) throw new StreamParser.CompileFailure(pfDiags)
+  dp = pf.onPath("/")
+    .withValidationMode(ValidationMode.Full)
+  // .withDebuggerRunner(new TraceDebuggerRunner()) // DAFFODIL-2624 - cannot 
trace in streaming SAPI
+  // .withDebugging(true)
+  val dpDiags = dp.getDiagnostics
+  if (dp.isError) throw new StreamParser.CompileFailure(dpDiags)
+  val compilationWarnings = if (!pfDiags.isEmpty) pfDiags else dpDiags // 
dpDiags might be empty. That's ok.
+
+  def setInputStream(inputStream: InputStream): Unit = {
+    dis = InputSourceDataInputStream(inputStream)
+  }
+
+  /**
+   * Called to pull messages from the data stream.
+   *
+   * @return a Result object containing the results of the parse including 
diagnostic information.
+   */
+  def parse = {
+    if (dis == null) throw new IllegalStateException("Input stream must be 
provided by setInputStream() call.")
+    val res: DFDL.ParseResult = dp.parse(dis, outputter)
+    val procErr = res.isProcessingError
+    val validationErr = res.isValidationError
+    val diags = res.getDiagnostics
+    val doc = if (!procErr) outputter.getResult else null
+    val bitPos1b = res.resultState.currentLocation.bitPos1b
+    val r = new StreamParser.Result(doc, diags, procErr, validationErr, 
bitPos1b)
+    outputter.reset()
+    r
+  }
+}

Reply via email to