Skip to content

Commit

Permalink
Add support for escalating SDWs to Errors
Browse files Browse the repository at this point in the history
- add tunable 'escalateWarningsToErrors' to control escalation
- add new SchemaDefinitionErrorFromWarning class that extends SDW so any changes to SDW in the future will be automatically inherited
- make RSDW and RSDE extend SDW and SDEs respectively
- add test to demonstrate tunable
- add test to demonstrate tunable ignored when warning is suppressed
- add test to demonstrate runtime SDW can be escalated as well

DAFFODIL-2810
  • Loading branch information
olabusayoT committed Oct 4, 2024
1 parent e02ae7f commit 66366ce
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="escalateWarningsToErrors" type="xs:boolean" default="false" minOccurs="0">
<xs:annotation>
<xs:documentation>
This tunable allows the escalation of Schema Definition Warnings to Errors.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="generatedNamespacePrefixStem" type="xs:string" default="tns" minOccurs="0">
<xs:annotation>
<xs:documentation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ final class RuntimeExpressionDPath[T <: AnyRef](
if (value.asInstanceOf[String].length == 0) {
val e = new RuntimeSchemaDefinitionError(
ci.schemaFileLocation,
state,
"Non-empty string required."
)
doSDE(e, state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import org.apache.daffodil.lib.exceptions.SchemaFileLocation
import org.apache.daffodil.lib.exceptions._
import org.apache.daffodil.lib.util.Maybe
import org.apache.daffodil.lib.util.Maybe._
import org.apache.daffodil.runtime1.processors.CompileState
import org.apache.daffodil.runtime1.processors.ParseOrUnparseState

class SchemaDefinitionError(
Expand All @@ -49,6 +48,20 @@ class SchemaDefinitionError(

}

class SchemaDefinitionErrorFromWarning(sdw: SchemaDefinitionWarning)
extends SchemaDefinitionWarning(
sdw.warnID,
sdw.schemaContext,
sdw.annotationContext,
sdw.kind,
sdw.args: _*
) {

override def isError = true
override def modeName = super.modeName + " Warning Escalated"

}

/**
* Specific class used for this specific error, because we need to pick this off
* in the debugger for special handling.
Expand All @@ -58,60 +71,41 @@ class RelativePathPastRootError(schemaContext: SchemaFileLocation, kind: String,

class RuntimeSchemaDefinitionError(
schemaContext: SchemaFileLocation,
runtimeContext: ParseOrUnparseState,
causedBy: Throwable,
fmtString: String,
args: Any*
) extends SchemaDefinitionDiagnosticBase(
Maybe(schemaContext),
(runtimeContext match { // TODO: this is ugly.
case cs: CompileState => Nope
case _ => Maybe(runtimeContext)
}),
) extends SchemaDefinitionError(
Option(schemaContext),
None,
Maybe(causedBy),
Maybe(fmtString),
fmtString,
args: _*
) {

override def isError = true
override def modeName = "Runtime Schema Definition"

def this(
schemaContext: SchemaFileLocation,
runtimeContext: ParseOrUnparseState,
fmtString: String,
args: Any*
) =
this(schemaContext, runtimeContext, null, fmtString, args: _*)
}

class RuntimeSchemaDefinitionWarning(
warnID: WarnID,
schemaContext: SchemaFileLocation,
runtimeContext: ParseOrUnparseState,
kind: String,
args: Any*
) extends SchemaDefinitionDiagnosticBase(
) extends SchemaDefinitionWarning(
warnID,
Some(schemaContext),
Some(runtimeContext),
None,
Nope,
Maybe(kind + s" (id: ${warnID})"),
kind,
args: _*
) {

override def isError = false
override def modeName = "Runtime Schema Definition"

}

class SchemaDefinitionWarning(
warnID: WarnID,
schemaContext: Option[SchemaFileLocation],
annotationContext: Option[SchemaFileLocation],
kind: String,
args: Any*
val warnID: WarnID,
val schemaContext: Option[SchemaFileLocation],
val annotationContext: Option[SchemaFileLocation],
val kind: String,
val args: Any*
) extends SchemaDefinitionDiagnosticBase(
schemaContext,
None,
Expand Down Expand Up @@ -269,7 +263,12 @@ trait ImplementsThrowsOrSavesSDE extends ImplementsThrowsSDE with SavesErrorsAnd
fmt,
args: _*
)
warn(sdw)
if (tunable.escalateWarningsToErrors) {
val sde = new SchemaDefinitionErrorFromWarning(sdw)
toss(sde)
} else {
warn(sdw)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,9 +513,9 @@ class DataProcessor(
state.dataInputStream.inputSource.setInvalid
state.setFailed(sde)
}
case rsde: RuntimeSchemaDefinitionError => {
case sdefw: SchemaDefinitionErrorFromWarning => {
state.dataInputStream.inputSource.setInvalid
state.setFailed(rsde)
state.setFailed(sdefw)
}
case e: ErrorAlreadyHandled => {
state.setFailed(e.th)
Expand Down Expand Up @@ -574,8 +574,8 @@ class DataProcessor(
unparserState.setFailed(sde)
unparserState.unparseResult
}
case rsde: RuntimeSchemaDefinitionError => {
unparserState.setFailed(rsde)
case sdefw: SchemaDefinitionErrorFromWarning => {
unparserState.setFailed(sdefw)
unparserState.unparseResult
}
case e: ErrorAlreadyHandled => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import org.apache.daffodil.runtime1.dpath.DState
import org.apache.daffodil.runtime1.dsom.DPathCompileInfo
import org.apache.daffodil.runtime1.dsom.RuntimeSchemaDefinitionError
import org.apache.daffodil.runtime1.dsom.RuntimeSchemaDefinitionWarning
import org.apache.daffodil.runtime1.dsom.SchemaDefinitionErrorFromWarning
import org.apache.daffodil.runtime1.dsom.ValidationError
import org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitive
import org.apache.daffodil.runtime1.infoset._
Expand Down Expand Up @@ -555,13 +556,13 @@ abstract class ParseOrUnparseState protected (

final def SDE(str: String, args: Any*) = {
val ctxt = getContext()
val rsde = new RuntimeSchemaDefinitionError(ctxt.schemaFileLocation, this, str, args: _*)
val rsde = new RuntimeSchemaDefinitionError(ctxt.schemaFileLocation, str, args: _*)
ctxt.toss(rsde)
}

final def SDEButContinue(str: String, args: Any*) = {
val ctxt = getContext()
val rsde = new RuntimeSchemaDefinitionError(ctxt.schemaFileLocation, this, str, args: _*)
val rsde = new RuntimeSchemaDefinitionError(ctxt.schemaFileLocation, str, args: _*)
diagnostics = rsde :: diagnostics
}

Expand All @@ -573,8 +574,13 @@ abstract class ParseOrUnparseState protected (
tssdw.contains(warnID) || tssdw.contains(WarnID.All)
if (!suppress) {
val rsdw =
new RuntimeSchemaDefinitionWarning(warnID, ctxt.schemaFileLocation, this, str, args: _*)
diagnostics = rsdw :: diagnostics
new RuntimeSchemaDefinitionWarning(warnID, ctxt.schemaFileLocation, str, args: _*)
if (tunable.escalateWarningsToErrors) {
val sde = new SchemaDefinitionErrorFromWarning(rsdw)
ctxt.toss(sde)
} else {
diagnostics = rsdw :: diagnostics
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,14 @@ class SuspensionTracker(suspensionWaitYoung: Int, suspensionWaitOld: Int) {
class SuspensionDeadlockException(suspExprs: Seq[Suspension])
extends RuntimeSchemaDefinitionError(
suspExprs(0).rd.schemaFileLocation,
suspExprs(0).savedUstate,
"Expressions/Unparsers are circularly deadlocked (mutually defined):\n%s",
suspExprs.groupBy { _.rd }.mapValues { _(0) }.values.mkString(" - ", "\n - ", "")
suspExprs
.groupBy {
_.rd
}
.mapValues {
_(0)
}
.values
.mkString(" - ", "\n - ", "")
)
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,7 @@ trait DoSDEMixin {
throw sde
}
case other => {
val sde = new RuntimeSchemaDefinitionError(
state.getContext().schemaFileLocation,
state,
e,
null
)
val sde = new RuntimeSchemaDefinitionError(state.getContext().schemaFileLocation, e.getMessage)
state.setFailed(sde)
state.toss(sde)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ case class NotParsableParser(context: ElementRuntimeData) extends PrimParserNoDa
// create an SDE and toss it
val rsde = new RuntimeSchemaDefinitionError(
context.schemaFileLocation,
state,
"This schema was compiled without parse support. Check the parseUnparsePolicy tunable or dfdlx:parseUnparsePolicy property."
)
context.toss(rsde)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ case class NotUnparsableUnparser(override val context: ElementRuntimeData)
// create an SDE and toss it
val rsde = new RuntimeSchemaDefinitionError(
context.schemaFileLocation,
state,
"This schema was compiled without unparse support. Check the parseUnparsePolicy tunable or dfdlx:parseUnparsePolicy property."
)
context.toss(rsde)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,67 @@
</tdml:infoset>
</tdml:parserTestCase>


<tdml:defineSchema name="warning_escalated">
<xs:include schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
<dfdl:format ref="ex:GeneralFormat" lengthKind="delimited" encoding="utf-8" representation="text"/>
<xs:element name="elem" type="xs:string">
<xs:annotation>
<xs:appinfo source="http://www.ogf.org/dfdl/dfdl-1.0/" />
</xs:annotation>
</xs:element>

<xs:element name="elem2" type="xs:string" daf:suppressSchemaDefinitionWarnings="appinfoDFDLSourceWrong">
<xs:annotation>
<xs:appinfo source="http://www.ogf.org/dfdl/dfdl-1.0/" />
</xs:annotation>
</xs:element>
</tdml:defineSchema>


<tdml:defineConfig name="escalateWarnings">
<daf:tunables>
<daf:escalateWarningsToErrors>true</daf:escalateWarningsToErrors>
</daf:tunables>
</tdml:defineConfig>

<!--
Test Name: schema_warning_escalated_to_error
Schema: warning_escalated
Root: elem
Purpose: This test demonstrates escalating warnings to errors
-->

<tdml:parserTestCase name="schema_warning_escalated_to_error" root="elem"
model="warning_escalated"
ignoreUnexpectedWarnings="false"
config="escalateWarnings">
<tdml:document><![CDATA[test]]></tdml:document>
<tdml:errors>
<tdml:error>Schema Definition Warning Escalated Error</tdml:error>
<tdml:error>appinfoDFDLSourceWrong</tdml:error>
<tdml:error>xs:appinfo source attribute</tdml:error>
</tdml:errors>
</tdml:parserTestCase>

<!--
Test Name: schema_warning_escalated_to_error2
Schema: warning_escalated
Root: elem2
Purpose: This test demonstrates escalating warnings to errors doesn't happen when
warning is suppressed
-->

<tdml:parserTestCase name="schema_warning_escalated_to_error2" root="elem2"
model="warning_escalated"
ignoreUnexpectedWarnings="false"
config="escalateWarnings">
<tdml:document><![CDATA[test]]></tdml:document>
<tdml:infoset>
<tdml:dfdlInfoset>
<elem2>test</elem2>
</tdml:dfdlInfoset>
</tdml:infoset>
</tdml:parserTestCase>

</tdml:testSuite>
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ct="http://w3.ibm.com/xmlns/dfdl/ctInfoset"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:ex="http://example.com"
xmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"
defaultRoundTrip="true">

<tdml:defineSchema name="v">
Expand Down Expand Up @@ -2221,6 +2222,26 @@
</tdml:parserTestCase>


<tdml:defineConfig name="escalateWarnings">
<daf:tunables>
<daf:escalateWarningsToErrors>true</daf:escalateWarningsToErrors>
</daf:tunables>
</tdml:defineConfig>

<tdml:parserTestCase name="defineVariable_nonConstantExpression_setVar_err2" root="root"
model="defineVariable_nonConstantExpression_setVar" description="escalate warning"
config="escalateWarnings">
<tdml:document/>
<tdml:errors>
<tdml:error>Schema Definition Warning Escalated Error</tdml:error>
<tdml:error>variableSet</tdml:error>
<tdml:error>Cannot set variable</tdml:error>
<tdml:error>after reading the default value</tdml:error>
<tdml:error>State was: VariableRead</tdml:error>
</tdml:errors>
</tdml:parserTestCase>


<tdml:defineSchema name="variable_direction_schema">
<xs:include schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
<dfdl:format ref="ex:GeneralFormat"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,12 @@ class TestSDE {
@Test def test_schema_warning_locally_suppressed(): Unit = {
runner.runOneTest("schema_warning_locally_suppressed")
}

@Test def test_schema_warning_escalated_to_error(): Unit = {
runner.runOneTest("schema_warning_escalated_to_error")
}

@Test def test_schema_warning_escalated_to_error2(): Unit = {
runner.runOneTest("schema_warning_escalated_to_error2")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ class TestVariables {
@Test def test_defineVariable_nonConstantExpression_setVar_err(): Unit = {
runner.runOneTest("defineVariable_nonConstantExpression_setVar_err")
}
@Test def test_defineVariable_nonConstantExpression_setVar_err2(): Unit = {
runner.runOneTest("defineVariable_nonConstantExpression_setVar_err2")
}

// DAFFODIL-2444 - This test triggers an unhandled NoSuchElement exception, which if handled then runs into an Assert.invariant
// @Test def test_defineVariable_ref_infoset_err(): Unit = { runner.runOneTest("defineVariable_ref_infoset_err") }
Expand Down

0 comments on commit 66366ce

Please sign in to comment.