Fix separatorSuppressionPolicy="never"

Currently, parsing with occursCountKind "implicit" and separator
suppression policy "never" behaves similar to occursCountKind "fixed",
in that it requires maxOccurs instances, but does not allow for absent
representations. This is incorrect--SSP "never" only requires non-absent
instance up to minOccurs, and afterwards allows absent occurrences as
long as maxOccurs separators are found.

To fix this, this modifies parsing to use the same logic as
trailingEmpty and trailingEmptyStrict, but adds additional logic at the
end of parsing a repetition to ensure that there were no errors or
missing separators. This is done by using the same Rep parser, but
adding a new PositionalNever flag to differentiate the logic. A new
arrayCompleteChecks function is used to check if this flag is set and
create a parse error if errors or missing separators occurred. This also
renames finalChecks to sequenceCompleteChecks to differentiate it from
arrayCompleteChecks.

Currently, the unparsing logic is also broken for occursCountKind
"implicit" and separator suppression policy "never" if there are no
instances of an array/optional element. This is because the logic always
looks for a "start" event, which may not exist if minOccurs is zero and
all instances have an absent representation.

To fix this, this modifies unparseWithNoSuppression to not require
"start" events and relies on the existing shouldDoUnparse function to
determine if the current event is the right one to unparse. The existing
logic will then output any missing separators as needed, with a slight
tweak to handle an off-by-one error for infix separators.

DAFFODIL-2802, DAFFODIL-2499
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
index 1ad6817..5d8e25f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/SequenceGrammarMixin.scala
@@ -176,7 +176,7 @@
           "separatorSuppressionPolicy='never' with occursCountKind='implicit' requires bounded maxOccurs.",
         )
       case (e: EB, Ordered__, Never______, Implicit__, ___, max) =>
-        new RepOrderedExactlyNSequenceChild(this, e, groupIndex, max)
+        new RepOrderedWithMinMaxSequenceChild(this, e, groupIndex)
       case (
             e: EB,
             Ordered__,
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
index 8ab3d71..2dadde2 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/primitives/SequenceChild.scala
@@ -153,7 +153,7 @@
               case (true, false, TrailingEmptyStrict, UNB)
                   if !eb.isLastDeclaredRepresentedInSequence =>
                 Assert.invariantFailed("Should be SDE found elsewhere.")
-              case (true, _, Never, _) => Positional
+              case (true, _, Never, _) => PositionalNever
               case (true, _, AnyEmpty, _) => NonPositional
               case (true, _, TrailingEmpty, _) => PositionalTrailingLax
               case (true, _, TrailingEmptyStrict, _) => PositionalTrailingStrict
@@ -465,7 +465,7 @@
           isModelGroupRepPossiblyZeroLength,
           isModelGroupRepNonZeroLength,
         )
-      case Positional =>
+      case Positional | PositionalNever =>
         new PositionalGroupSeparatedSequenceChildParseResultHelper(
           mgrd,
           sscb,
@@ -509,7 +509,7 @@
           isEmptyRepZeroLength,
           isEmptyRepNonZeroLength,
         )
-      case Positional =>
+      case Positional | PositionalNever =>
         new PositionalScalarElementSeparatedSequenceChildParseResultHelper(
           sscb,
           erd,
@@ -601,7 +601,7 @@
           isEmptyRepZeroLength,
           isEmptyRepNonZeroLength,
         )
-      case Positional =>
+      case Positional | PositionalNever =>
         new PositionalRepElementSeparatedSequenceChildParseResultHelper(
           sscb,
           erd,
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
index d98a9d0..d824a57 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/unparsers/runtime1/SeparatedSequenceUnparsers.scala
@@ -610,122 +610,95 @@
           state.arrayIterationIndexStack.push(1L)
           state.occursIndexStack.push(1L)
           val erd = unparser.erd
+          Assert.invariant(erd.isArray || erd.isOptional)
+          Assert.invariant(erd.isRepresented) // arrays/optionals cannot have inputValueCalc
+
           var numOccurrences = 0
           val maxReps = unparser.maxRepeats(state)
-          // val isBounded = unparser.isBoundedMax // not needed for the no-suppression case
 
-          //
-          // The number of occurrances we unparse is always exactly driven
-          // by the number of infoset events for the repeating/optional element.
-          //
-          // For RepUnparser - array/optional case - in all cases we should get a
-          // startArray event. If we don't then
-          // the element must be entirely optional, so we get no events for it
-          // at all.
-          //
+          Assert.invariant(state.inspect)
+          val ev = state.inspectAccessor
+          val isArr = erd.isArray
 
-          if (state.inspect) {
-            val ev = state.inspectAccessor
-            val isArr = erd.isArray
-            if (ev.isStart && (isArr || erd.isOptional)) {
-              if (ev.erd eq erd) {
-                //
-                // StartArray for this unparser's array element
-                //
-                unparser.startArrayOrOptional(state)
-                while ({
-                  doUnparser = unparser.shouldDoUnparser(unparser, state)
-                  doUnparser
-                }) {
-                  //
-                  // These are so we can check invariants on these stacks being
-                  // pushed and popped reliably, and incremented only once
-                  //
-                  val arrayIterationIndexBefore = state.arrayIterationPos
-                  val arrayIterationIndexStackDepthBefore =
-                    state.arrayIterationIndexStack.length
-                  val occursIndexBefore = state.occursPos
-                  val occursIndexStackDepthBefore = state.occursIndexStack.length
-                  val groupIndexBefore = state.groupPos
-                  val groupIndexStackDepthBefore = state.groupIndexStack.length
-
-                  Assert.invariant(
-                    erd.isRepresented,
-                  ) // since this is an array, can't have inputValueCalc
-
-                  if (isArr)
-                    if (state.dataProc.isDefined)
-                      state.dataProc.get.beforeRepetition(state, this)
-
-                  unparseOne(unparser, erd, state)
-                  numOccurrences += 1
-                  Assert.invariant(
-                    state.arrayIterationIndexStack.length == arrayIterationIndexStackDepthBefore,
-                  )
-                  state.moveOverOneArrayIterationIndexOnly()
-                  Assert.invariant(state.arrayIterationPos == arrayIterationIndexBefore + 1)
-
-                  Assert.invariant(state.occursIndexStack.length == occursIndexStackDepthBefore)
-                  state.moveOverOneOccursIndexOnly()
-                  Assert.invariant(state.occursPos == occursIndexBefore + 1)
-
-                  Assert.invariant(state.groupIndexStack.length == groupIndexStackDepthBefore)
-                  state.moveOverOneGroupIndexOnly() // array elements are always represented.
-                  Assert.invariant(state.groupPos == groupIndexBefore + 1)
-
-                  if (isArr)
-                    if (state.dataProc.isDefined)
-                      state.dataProc.get.afterRepetition(state, this)
-                }
-                //
-                // If not enough occurences in array, we output extra separators
-                //
-                if (maxReps > numOccurrences) {
-                  var numExtraSeps = erd.maxOccurs - numOccurrences
-                  while (numExtraSeps > 0) {
-                    unparseJustSeparator(state)
-                    numExtraSeps -= 1
-                  }
-                }
-                unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
-                  state,
-                  unparser,
-                  numOccurrences,
-                  maxReps,
-                  state.arrayIterationPos - 1,
-                )
-                unparser.endArrayOrOptional(erd, state)
-              } else {
-                //
-                // start array for some other array. Not this one. So we
-                // don't unparse anything here, and we'll go on to the next
-                // sequence child, which hopefully will be a matching array.
-                //
-                Assert.invariant(erd.minOccurs == 0L)
-              }
-            } else if (ev.isStart) {
-              Assert.invariant(!ev.erd.isArray && !erd.isOptional)
-              //
-              // start of scalar.
-              // That has to be for a different element later in the sequence
-              // since this one has a RepUnparser (i.e., is NOT scalar)
-              //
-              val eventNQN = ev.erd.namedQName
-              Assert.invariant(eventNQN != erd.namedQName)
-            } else {
-              Assert.invariant(ev.isEnd && ev.erd.isComplexType)
-              unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
-                state,
-                unparser,
-                numOccurrences,
-                maxReps,
-                0,
-              )
-            }
-          } else {
-            // no event (state.inspect returned false)
-            Assert.invariantFailed("No event for unparsing.")
+          // If the event is for this Rep unparser, we need to consume the StartArray event
+          if (ev.erd eq erd) {
+            unparser.startArrayOrOptional(state)
           }
+
+          // Unparse each occurrence of this array in the infoset. Note that there could be zero
+          // occurrences
+          while ({
+            doUnparser = unparser.shouldDoUnparser(unparser, state)
+            doUnparser
+          }) {
+            //
+            // These are so we can check invariants on these stacks being
+            // pushed and popped reliably, and incremented only once
+            //
+            val arrayIterationIndexBefore = state.arrayIterationPos
+            val arrayIterationIndexStackDepthBefore =
+              state.arrayIterationIndexStack.length
+            val occursIndexBefore = state.occursPos
+            val occursIndexStackDepthBefore = state.occursIndexStack.length
+            val groupIndexBefore = state.groupPos
+            val groupIndexStackDepthBefore = state.groupIndexStack.length
+
+            if (isArr && state.dataProc.isDefined)
+              state.dataProc.get.beforeRepetition(state, this)
+
+            unparseOne(unparser, erd, state)
+            numOccurrences += 1
+            Assert.invariant(
+              state.arrayIterationIndexStack.length == arrayIterationIndexStackDepthBefore,
+            )
+            state.moveOverOneArrayIterationIndexOnly()
+            Assert.invariant(state.arrayIterationPos == arrayIterationIndexBefore + 1)
+
+            Assert.invariant(state.occursIndexStack.length == occursIndexStackDepthBefore)
+            state.moveOverOneOccursIndexOnly()
+            Assert.invariant(state.occursPos == occursIndexBefore + 1)
+
+            Assert.invariant(state.groupIndexStack.length == groupIndexStackDepthBefore)
+            state.moveOverOneGroupIndexOnly() // array elements are always represented.
+            Assert.invariant(state.groupPos == groupIndexBefore + 1)
+
+            if (isArr && state.dataProc.isDefined)
+              state.dataProc.get.afterRepetition(state, this)
+          }
+
+          // If not enough occurrences are in the infoset, we output extra separators because
+          // we are unparsing with no suppression
+          if (maxReps > numOccurrences) {
+            var numExtraSeps = {
+              val sepsNeeded = erd.maxOccurs - numOccurrences
+              if ((spos eq Infix) && state.groupPos == 1) {
+                // If separatorPosition is infix and we haven't output anything for this sequence
+                // yet, then we need one less extra separator, since the separator is skipped
+                // for the first instance of infix separators.
+                sepsNeeded - 1
+              } else {
+                sepsNeeded
+              }
+            }
+            while (numExtraSeps > 0) {
+              unparseJustSeparator(state)
+              numExtraSeps -= 1
+            }
+          }
+
+          unparser.checkFinalOccursCountBetweenMinAndMaxOccurs(
+            state,
+            unparser,
+            numOccurrences,
+            maxReps,
+            state.arrayIterationPos - 1,
+          )
+
+          // If the event is for this Rep unparser, we need to consume the EndArray event
+          if (ev.erd eq erd) {
+            unparser.endArrayOrOptional(erd, state)
+          }
+
           state.arrayIterationIndexStack.pop()
           state.occursIndexStack.pop()
         }
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
index 4349447..e701035 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceChildParseResultHelper.scala
@@ -62,6 +62,7 @@
   sealed abstract class PositionalLike extends Type
   sealed abstract class PositionalTrailing extends PositionalLike
   case object Positional extends PositionalLike
+  case object PositionalNever extends PositionalLike
   case object PositionalTrailingLax extends PositionalTrailing
   case object PositionalTrailingStrict extends PositionalTrailing
   case object NonPositional extends Type
@@ -94,7 +95,30 @@
    * Define this as final here so we aren't creating proliferation of
    * traits/classes just for this one little issue.
    */
-  final override def finalChecks(
+  final def arrayCompleteChecks(
+    parser: SequenceChildParser,
+    pstate: PState,
+    resultOfTry: ParseAttemptStatus,
+    priorResultOfTry: ParseAttemptStatus,
+  ): Unit = {
+    if ((sscb eq PositionalNever)) {
+      val resultToTest = resultOfTry
+      resultToTest match {
+        case ParseAttemptStatus.FailureUnspecified | ParseAttemptStatus.MissingSeparator =>
+          parser.PE(
+            pstate,
+            "maxOccurs instances and their separators are required when dfdl:separatorSuppressionPolicy='never'",
+          )
+        case _ => // ok
+      }
+    }
+  }
+
+  /**
+   * Define this as final here so we aren't creating proliferation of
+   * traits/classes just for this one little issue.
+   */
+  final def sequenceCompleteChecks(
     parser: SequenceChildParser,
     pstate: PState,
     resultOfTry: ParseAttemptStatus,
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
index 62bf01c..8f542cf 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SeparatedSequenceParsers.scala
@@ -48,12 +48,19 @@
     separatorHelper.parseOneWithSeparator(pstate, requiredOptional)
   }
 
-  final override def finalChecks(
+  final override def arrayCompleteChecks(
     pstate: PState,
     resultOfTry: ParseAttemptStatus,
     priorResultOfTry: ParseAttemptStatus,
   ): Unit =
-    parseResultHelper.finalChecks(self, pstate, resultOfTry, priorResultOfTry)
+    parseResultHelper.arrayCompleteChecks(self, pstate, resultOfTry, priorResultOfTry)
+
+  final override def sequenceCompleteChecks(
+    pstate: PState,
+    resultOfTry: ParseAttemptStatus,
+    priorResultOfTry: ParseAttemptStatus,
+  ): Unit =
+    parseResultHelper.sequenceCompleteChecks(self, pstate, resultOfTry, priorResultOfTry)
 
 }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
index 0fb5559..acede97 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildBases.scala
@@ -215,7 +215,16 @@
 
   def pouStatus: PoUStatus
 
-  def finalChecks(
+  def arrayCompleteChecks(
+    pstate: PState,
+    resultOfTry: ParseAttemptStatus,
+    priorResultOfTry: ParseAttemptStatus,
+  ): Unit = {
+    // does nothing by default.
+    // overridden in separated sequence child parsers in some cases
+  }
+
+  def sequenceCompleteChecks(
     pstate: PState,
     resultOfTry: ParseAttemptStatus,
     priorResultOfTry: ParseAttemptStatus,
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
index 3059f0d..de54c80 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceChildParseResultHelper.scala
@@ -87,17 +87,6 @@
     requiredOptional: RequiredOptionalStatus,
   ): ParseAttemptStatus
 
-  /**
-   * Overridden for PositionalTrailingStrict case.
-   */
-  def finalChecks(
-    parser: SequenceChildParser,
-    pstate: PState,
-    resultOfTry: ParseAttemptStatus,
-    priorResultOfTry: ParseAttemptStatus,
-  ): Unit = {
-    // do nothing by default
-  }
 }
 
 trait ElementSequenceChildParseResultHelper extends SequenceChildParseResultHelper {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
index 59c741c..b1275f1 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/parsers/SequenceParserBases.scala
@@ -245,6 +245,7 @@
 
             } // end while for each repeat
             parser.endArray(pstate)
+            parser.arrayCompleteChecks(pstate, resultOfTry, priorResultOfTry)
           } // end match case RepeatingChildParser
 
           case nonRepresentedParser: NonRepresentedSequenceChildParser => {
@@ -402,7 +403,7 @@
         pstate.walker.walk()
       }
 
-      if (child ne null) child.finalChecks(pstate, resultOfTry, priorResultOfTry)
+      if (child ne null) child.sequenceCompleteChecks(pstate, resultOfTry, priorResultOfTry)
       ()
     } finally {
       pstate.mpstate.groupIndexStack.pop()
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
index 96143b8..811b859 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
@@ -169,7 +169,6 @@
       <dfdlInfoset>
         <tns:e_infix_never>
           <x>foo</x>
-          <y/>
       </tns:e_infix_never>
       </dfdlInfoset>
     </infoset>
@@ -215,7 +214,6 @@
       <dfdlInfoset>
         <tns:e_infix_never>
           <x>foo;</x>
-          <y></y>
         </tns:e_infix_never>
       </dfdlInfoset>
     </infoset>
@@ -241,7 +239,6 @@
       <dfdlInfoset>
         <tns:e_infix_never>
           <x>foo/</x>
-          <y></y>
         </tns:e_infix_never>
       </dfdlInfoset>
     </infoset>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
index 5742538..95a3621 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section14/sequence_groups/SequenceGroupDelimiters.tdml
@@ -629,6 +629,22 @@
       </xs:complexType>
     </xs:element>
 
+    <xs:element name="policyNeverOptionalStringArray">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="," dfdl:sequenceKind="ordered" dfdl:separatorSuppressionPolicy="never">
+          <xs:element name="A" type="xs:string" minOccurs="0" maxOccurs="5" dfdl:occursCountKind="implicit" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="policyNeverOptionalIntArray">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="," dfdl:sequenceKind="ordered" dfdl:separatorSuppressionPolicy="never">
+          <xs:element name="A" type="xs:int" minOccurs="0" maxOccurs="5" dfdl:occursCountKind="implicit" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
   </tdml:defineSchema>
 
   <tdml:parserTestCase name="separatorSuppressionPolicy_never" model="separatorSuppressionPolicy">
@@ -641,10 +657,90 @@
         <tns:policyNever>
           <tns:A>Hello</tns:A>
           <tns:B>world</tns:B>
-          <tns:C></tns:C>
         </tns:policyNever>
       </tdml:dfdlInfoset>
     </tdml:infoset>
   </tdml:parserTestCase>
 
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalStringArray_1" model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[1,2,3,4,5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalStringArray>
+          <A>1</A>
+          <A>2</A>
+          <A>3</A>
+          <A>4</A>
+          <A>5</A>
+        </tns:policyNeverOptionalStringArray>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalStringArray_2" model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[,,,,]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalStringArray />
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalStringArray_3" model="separatorSuppressionPolicy" root="policyNeverOptionalStringArray">
+    <tdml:document><![CDATA[,,,]]></tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>maxOccurs</tdml:error>
+      <tdml:error>required</tdml:error>
+      <tdml:error>separatorSuppressionPolicy</tdml:error>
+      <tdml:error>never</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalIntArray_1" model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[1,2,3,4,5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalIntArray>
+          <A>1</A>
+          <A>2</A>
+          <A>3</A>
+          <A>4</A>
+          <A>5</A>
+        </tns:policyNeverOptionalIntArray>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalIntArray_2" model="separatorSuppressionPolicy">
+    <tdml:document><![CDATA[,,,,]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tns:policyNeverOptionalIntArray />
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalIntArray_3" model="separatorSuppressionPolicy" root="policyNeverOptionalIntArray">
+    <tdml:document><![CDATA[1,a,3,4,5]]></tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>maxOccurs</tdml:error>
+      <tdml:error>required</tdml:error>
+      <tdml:error>separatorSuppressionPolicy</tdml:error>
+      <tdml:error>never</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="separatorSuppressionPolicy_never_optionalIntArray_4" model="separatorSuppressionPolicy" root="policyNeverOptionalIntArray">
+    <tdml:document><![CDATA[1,2,3,4]]></tdml:document>
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>maxOccurs</tdml:error>
+      <tdml:error>required</tdml:error>
+      <tdml:error>separatorSuppressionPolicy</tdml:error>
+      <tdml:error>never</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml
index 8fe36b7..eb2d97d 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/usertests/SepTests.tdml
@@ -223,14 +223,7 @@
 
   <!-- Test for DAFFODIL-2499. empty strings should not be creating empty elements here. -->
   <tdml:parserTestCase name="test_sep_ssp_never_1" root="file1" model="s3"
-                       implementations="ibm">
-    <!--
-      This test doesn't work on Daffodil due  to DAFFODIL-2499.
-
-      However, we enable it in the scala test driver (not commented out) but just
-      specify only the ibm implementation here, so that if cross testing this file
-      you will see that it works there.
-    -->
+                       implementations="daffodil ibm">
     <tdml:document>madonna,,,,,,,,,</tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
@@ -268,7 +261,7 @@
 
   <!-- Test for DAFFODIL-2499. empty strings should not be creating empty elements here. -->
   <tdml:parserTestCase name="test_sep_ssp_never_3" root="file1" model="s3"
-                       implementations="ibm">
+                       implementations="daffodil ibm">
     <tdml:document>madonna,,,,,,,,,</tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
@@ -440,4 +433,181 @@
     </tdml:errors>
   </tdml:parserTestCase>
 
+
+  <tdml:defineSchema name="s6" elementFormDefault="unqualified">
+
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+    <dfdl:format
+      ref="ex:GeneralFormat"
+      emptyValueDelimiterPolicy="none"
+      lengthKind="delimited"
+      separatorPosition="infix"
+      separatorSuppressionPolicy="trailingEmpty"
+    />
+
+    <xs:group name="emptyElementDetectionAssertion">
+      <xs:sequence>
+        <xs:element name="emptyElementDetected" type="xs:string">
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:discriminator test="{ fn:string-length(.) eq 0 }"/>
+            </xs:appinfo>
+          </xs:annotation>
+        </xs:element>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/">
+              <dfdl:assert test="{ fn:false() }"/>
+            </xs:appinfo>
+          </xs:annotation>
+        </xs:sequence>
+      </xs:sequence>
+    </xs:group>
+
+    <xs:element name="root" dfdl:initiator="RECORD">
+      <xs:complexType>
+        <xs:sequence dfdl:separator="/" dfdl:separatorPosition="prefix" dfdl:terminator="%NL;">
+          <xs:element name="field1" type="xs:string" />
+          <xs:element name="field2" type="xs:string" minOccurs="0" />
+          <xs:element name="field3" type="xs:string" minOccurs="0" />
+          <xs:element name="groupOfFields" minOccurs="1" maxOccurs="3" dfdl:occursCountKind="parsed">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:sequence dfdl:separator="/" dfdl:separatorPosition="infix" dfdl:separatorSuppressionPolicy="never">
+                  <xs:element name="field4" minOccurs="0">
+                    <xs:complexType>
+                      <xs:choice>
+                        <xs:group ref="ex:emptyElementDetectionAssertion" />
+                        <xs:sequence dfdl:separator="-">
+                          <xs:element name="field4a" type="xs:string" />
+                          <xs:element name="field4b" type="xs:string" />
+                        </xs:sequence>
+                      </xs:choice>
+                    </xs:complexType>
+                  </xs:element>
+                  <xs:element name="field5" minOccurs="0">
+                    <xs:complexType>
+                      <xs:choice>
+                        <xs:group ref="ex:emptyElementDetectionAssertion" />
+                        <xs:sequence dfdl:separator="-">
+                          <xs:element name="field5a" type="xs:string" />
+                          <xs:element name="field5b" type="xs:string" />
+                        </xs:sequence>
+                      </xs:choice>
+                    </xs:complexType>
+                  </xs:element>
+                  <xs:element name="field6" minOccurs="0">
+                    <xs:complexType>
+                      <xs:choice>
+                        <xs:group ref="ex:emptyElementDetectionAssertion" />
+                        <xs:sequence dfdl:separator="-">
+                          <xs:element name="field6a" type="xs:string" />
+                          <xs:element name="field6b" type="xs:string" />
+                        </xs:sequence>
+                      </xs:choice>
+                    </xs:complexType>
+                  </xs:element>
+                </xs:sequence>
+                <xs:sequence>
+                  <xs:annotation>
+                    <xs:appinfo source="http://www.ogf.org/dfdl/">
+                      <dfdl:assert test="{ fn:exists(field4) or fn:exists(field5) or fn:exists(field6) }" />
+                    </xs:appinfo>
+                  </xs:annotation>
+                </xs:sequence>
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+          <xs:element name="field7" type="xs:string" minOccurs="0" />
+          <xs:element name="field8" type="xs:string" minOccurs="0" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="test_sep_ssp_never_6" root="root" model="s6"
+                       implementations="daffodil">
+    <tdml:document>
+      <tdml:documentPart type="text" replaceDFDLEntities="true">RECORD/1/2/3/4-1/5-1/6-1/4-2/5-2/6-2/7/8%LF;</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:root xmlns:ex="http://example.com">
+          <field1>1</field1>
+          <field2>2</field2>
+          <field3>3</field3>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>1</field4b>
+            </field4>
+            <field5>
+              <field5a>5</field5a>
+              <field5b>1</field5b>
+            </field5>
+            <field6>
+              <field6a>6</field6a>
+              <field6b>1</field6b>
+            </field6>
+          </groupOfFields>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>2</field4b>
+            </field4>
+            <field5>
+              <field5a>5</field5a>
+              <field5b>2</field5b>
+            </field5>
+            <field6>
+              <field6a>6</field6a>
+              <field6b>2</field6b>
+            </field6>
+          </groupOfFields>
+          <field7>7</field7>
+          <field8>8</field8>
+        </ex:root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="test_sep_ssp_never_7" root="root" model="s6"
+                       implementations="daffodil">
+    <tdml:document>
+      <tdml:documentPart type="text" replaceDFDLEntities="true">RECORD/1/2/3/4-1//6-1/4-2/5-2//7/8%LF;</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:root xmlns:ex="http://example.com">
+          <field1>1</field1>
+          <field2>2</field2>
+          <field3>3</field3>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>1</field4b>
+            </field4>
+            <field6>
+              <field6a>6</field6a>
+              <field6b>1</field6b>
+            </field6>
+          </groupOfFields>
+          <groupOfFields>
+            <field4>
+              <field4a>4</field4a>
+              <field4b>2</field4b>
+            </field4>
+            <field5>
+              <field5a>5</field5a>
+              <field5b>2</field5b>
+            </field5>
+          </groupOfFields>
+          <field7>7</field7>
+          <field8>8</field8>
+        </ex:root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
index 435cb99..9bdeb77 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section14/sequence_groups/TestSequenceGroups.scala
@@ -57,6 +57,27 @@
   @Test def test_separatorSuppressionPolicy_never(): Unit = {
     runner_01.runOneTest("separatorSuppressionPolicy_never")
   }
+  @Test def test_separatorSuppressionPolicy_never_optionalStringArray_1(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalStringArray_1")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalStringArray_2(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalStringArray_2")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalStringArray_3(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalStringArray_3")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_1(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_1")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_2(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_2")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_3(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_3")
+  }
+  @Test def test_separatorSuppressionPolicy_never_optionalIntArray_4(): Unit = {
+    runner_01.runOneTest("separatorSuppressionPolicy_never_optionalIntArray_4")
+  }
 
   // DAFFODIL-669
   //  @Test def test_emptySequenceSDE() { runner_02.runOneTest("emptySequenceSDE") }
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala b/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala
index fbc82c8..2453e67 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/usertests/TestSepTests.scala
@@ -47,28 +47,18 @@
     runner.runOneTest("test_sep_trailingEmptyStrict_2")
   }
 
-  // DAFFODIL-2499 - separatorSuppressionPolicy 'never'
-  // Note: this test isn't commented out, because it works for IBM DFDL in cross testing
-  // The TDML for this test just has it disabled for the daffodil implementation.
-  // Add daffodil to implementations to see the erroneous daffodil behavior.
   @Test def test_sep_ssp_never_1(): Unit = { runner.runOneTest("test_sep_ssp_never_1") }
-
   @Test def test_sep_ssp_never_2(): Unit = { runner.runOneTest("test_sep_ssp_never_2") }
-
-  // DAFFODIL-2499 - separatorSuppressionPolicy 'never'
-  // Note: this test isn't commented out, because it works for IBM DFDL in cross testing
-  // The TDML for this test just has it disabled for the daffodil implementation.
-  // Add daffodil to implementations to see the erroneous daffodil behavior.
   @Test def test_sep_ssp_never_3(): Unit = { runner.runOneTest("test_sep_ssp_never_3") }
-
   @Test def test_sep_ssp_never_4_ibm(): Unit = { runner.runOneTest("test_sep_ssp_never_4_ibm") }
-
   @Test def test_sep_ssp_never_4_daffodil(): Unit = {
     runner.runOneTest("test_sep_ssp_never_4_daffodil")
   }
-
   @Test def test_sep_ssp_never_5(): Unit = { runner.runOneTest("test_sep_ssp_never_5") }
 
+  @Test def test_sep_ssp_never_6(): Unit = { runner.runOneTest("test_sep_ssp_never_6") }
+  @Test def test_sep_ssp_never_7(): Unit = { runner.runOneTest("test_sep_ssp_never_7") }
+
   // DAFFODIL-2791
   @Test def test_treatAsMissing_occursIndex(): Unit = {
     runner.runOneTest("test_treatAsMissing_occursIndex")