\s*| [^<]* | \s*[^<]* | \s*\s*Warning on the result: compliant with <2012> only"""
+ }
+
+ test("a pass whose plugin then fails to end it keeps its notes and warnings in the partial summary") {
+ val run = new Run("end")
+ val ex = intercept[HarnessExecutionException](run(remarking("a", run.fail)))
+ ex.getCause.getMessage should equal("end failed")
+ ex.partial.results.map(r => (r.outcome, r.notes, r.warnings)) should equal(
+ List((Passed, List("an allowed alternative"), List("compliant with <2012> only"))))
+ ex.partial.passedWithWarnings.map(_.id) should equal(List("a"))
+ }
+
+ test("a failure keeps no notes or warnings, and isn't a pass with warnings") {
+ val run = new Run("step a")
+ val summary = run(remarking("a", run.fail))
+ summary.results.map(r => (r.notes, r.warnings)) should equal(List((Nil, Nil)))
+ summary.passedWithWarnings should equal(Nil)
+ run.saved("a").map(s => (s.notes, s.warnings)) should equal(Some((Nil, Nil)))
}
test("a saved result records its revision, and why the procedure was skipped") {
diff --git a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/CommonTimeTest.scala b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/CommonTimeTest.scala
index 9947118..71c1e96 100644
--- a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/CommonTimeTest.scala
+++ b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/CommonTimeTest.scala
@@ -31,12 +31,12 @@ class CommonTimeTest extends AnyFunSuite with Matchers {
describes(CommonTime.relativeTimeSupported, relative) should not include "DUT supports Binary Input Change"
}
- test("8.15.2 accepts IIN2.1 from a device configured to reject reads of relative time events, and notes it") {
+ test("8.15.2 accepts IIN2.1 from a device configured to reject reads of relative time events, and warns of it") {
val rejecting = Map(
TimeSyncOptions.binaryInput.withRelativeTime.id -> "false",
TimeSyncOptions.binaryInput.withRelativeTimeRejected.id -> "true")
- describes(CommonTime.readWithRelativeTime, rejecting) should include("Note the result")
- describes(CommonTime.readWithRelativeTime, relative) should not include "Note the result"
+ describes(CommonTime.readWithRelativeTime, rejecting) should include("Record a warning on the result")
+ describes(CommonTime.readWithRelativeTime, relative) should not include "Record a warning on the result"
}
test("8.15.3 runs where 8.15.2 gets events with relative time: with time recorded, and the read not rejected") {
diff --git a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/ControlStatusTest.scala b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/ControlStatusTest.scala
index 017b8a7..0b6090a 100644
--- a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/ControlStatusTest.scala
+++ b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/ControlStatusTest.scala
@@ -3,9 +3,10 @@
package io.stepfunc.dnp3c.conformance.v31r1
+import io.stepfunc.dnp3c.conformance.Run
import io.stepfunc.dnp3c.conformance.api.{ Index, Options }
import io.stepfunc.dnp3c.conformance.api.options.BinaryOutputOptions
-import io.stepfunc.dnp3c.dnp3.Validate
+import io.stepfunc.dnp3c.dnp3.{ LinkOverride, RespondingDriver, Validate }
import io.stepfunc.dnp3c.dnp3.app.Message
import io.stepfunc.dnp3c.dnp3.app.objects.CommandStatus
import org.junit.runner.RunWith
@@ -13,7 +14,8 @@ import org.scalatestplus.junit.JUnitRunner
import org.scalatest.matchers.should.Matchers
import org.scalatest.funsuite.AnyFunSuite
-// 3.1r1's 8.2.6: a select whose status field is non-zero is echoed with a non-zero status, or with 0, which is noted
+// 3.1r1's 8.2.6: a select whose status field is non-zero is echoed with a non-zero status, or with 0, which is warned
+// of
@RunWith(classOf[JUnitRunner])
class ControlStatusTest extends AnyFunSuite with Matchers {
@@ -31,7 +33,7 @@ class ControlStatusTest extends AnyFunSuite with Matchers {
(passes, zero)
}
- test("an echo with a non-zero status passes, as does one with status 0, which is noted") {
+ test("an echo with a non-zero status passes, as does one with status 0, which is flagged") {
result(response(126)) should equal((true, false))
result(response(4)) should equal((true, false))
result(response(0)) should equal((true, true))
@@ -44,4 +46,23 @@ class ControlStatusTest extends AnyFunSuite with Matchers {
// a different control code
result(response(4, change = bytes => bytes.updated(7, (bytes(7) ^ 0x01).toByte)))._1 should equal(false)
}
+
+ // a DUT that echoes each select with the status given
+ private class Echoing(status: Int) extends RespondingDriver() {
+ override def writeMessage(message: Message, options: LinkOverride): Unit =
+ enqueue(response(status, change = _ => message.headers.head.toBytes.init :+ status.toByte))
+ }
+
+ test("8.2.6 passes an echo with status 0 with a warning that it complies with 1815-2012 only, and not a note") {
+ val zero = new Echoing(0)
+ Run.passes(ControlStatus.nonZeroStatus, zero) should equal(true)
+ zero.reporter.warnings should equal(List(
+ "the DUT echoes a select whose status field isn't zero with status code 0: it complies with IEEE 1815-2012, but " +
+ "not with IEEE 1815-202x"))
+ zero.reporter.notes should equal(Nil)
+
+ val nonZero = new Echoing(4)
+ Run.passes(ControlStatus.nonZeroStatus, nonZero) should equal(true)
+ nonZero.reporter.warnings should equal(Nil)
+ }
}
diff --git a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/StaticVariationsTest.scala b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/StaticVariationsTest.scala
index ceb4cd1..c151a97 100644
--- a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/StaticVariationsTest.scala
+++ b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/StaticVariationsTest.scala
@@ -4,10 +4,12 @@
package io.stepfunc.dnp3c.conformance.v31r1
import io.stepfunc.dnp3c.conformance.Run
-import io.stepfunc.dnp3c.conformance.api.options.{ CounterOptions, OutstationOptions }
+import io.stepfunc.dnp3c.conformance.api.options.{ AnalogOutputOptions, CounterOptions, OutstationOptions }
import io.stepfunc.dnp3c.dnp3._
import io.stepfunc.dnp3c.dnp3.app._
-import io.stepfunc.dnp3c.dnp3.app.objects.{ Group20Var1, Group20Var2, Group20Var5, Group20Var6 }
+import io.stepfunc.dnp3c.dnp3.app.objects.{
+ Group20Var1, Group20Var2, Group20Var5, Group20Var6, Group40Var1, Group40Var2
+}
import io.stepfunc.dnp3c.protocol.Hex
import org.junit.runner.RunWith
import org.scalatestplus.junit.JUnitRunner
@@ -85,9 +87,11 @@ class StaticVariationsTest extends AnyFunSuite with Matchers {
test("counters in the variations requested, the abnormal one with flags, pass, and the flags are noted") {
val plugin = new Plugin
- val (passed, notes) = run(plugin, dut(plugin))
+ val d = dut(plugin)
+ val (passed, notes) = run(plugin, d)
passed should equal(true)
notes.exists(_.contains("came back with flags 0x03")) should equal(true)
+ d.reporter.warnings should equal(Nil)
plugin.abnormal should equal(None) // set back to normal at the end
}
@@ -108,9 +112,11 @@ class StaticVariationsTest extends AnyFunSuite with Matchers {
test("a level 1 or 2 device may answer with IIN2.1 and skip the test; a level 3 one may not") {
val plugin = new Plugin
- val (passed, notes) = run(plugin, dut(plugin, nullError = true), level = 2)
+ val d = dut(plugin, nullError = true)
+ val (passed, notes) = run(plugin, d, level = 2)
passed should equal(true)
notes.exists(_.contains("as a subset level 1 or 2 device may")) should equal(true)
+ d.reporter.warnings should equal(Nil)
run(new Plugin, dut(new Plugin, nullError = true), level = 3)._1 should equal(false)
}
@@ -120,4 +126,27 @@ class StaticVariationsTest extends AnyFunSuite with Matchers {
val plugin2 = new Plugin(says = 0x02)
run(plugin2, dut(plugin2))._1 should equal(false)
}
+
+ // analog output status points 0 and 1, flags 0x01, each in the variation requested, but point 0 always in 32 bits
+ private def analogOutputs = new ScriptedDut(
+ StubDriver.reporter(new Plugin),
+ request => {
+ val requested = request.headers.head.variation
+ val headers = List(0, 1).flatMap { i =>
+ val v = if (i == 0) Group40Var1 else requested
+ val value = if (v == Group40Var1) "01 05 00 00 00" else "01 05 00"
+ Message.parseObjectHeaders(Hex.from("28 %02X 00 %02X %02X %s".format(v.id, i, i, value)))
+ }
+ (IIN.empty, headers)
+ }
+ )
+
+ test("analog output status kept only in 32 bits passes with a warning that it isn't conformant at level 1 or 2") {
+ val d = analogOutputs
+ val options = Map(OutstationOptions.subsetLevel.id -> "2", AnalogOutputOptions.statusOnly32Bit.id -> "0")
+ Run.passes(StaticVariations.test(StaticVariations.analogOutputStatus), d, options) should equal(true)
+ d.reporter.warnings should equal(List(
+ "analog output status point 0 comes back only in 32 bits: the DUT isn't conformant at subset level 1 or 2"))
+ d.reporter.notes should equal(Nil)
+ }
}
diff --git a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/WithTimeTest.scala b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/WithTimeTest.scala
index b26c33c..6e429da 100644
--- a/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/WithTimeTest.scala
+++ b/src/test/scala/io/stepfunc/dnp3c/conformance/v31r1/WithTimeTest.scala
@@ -31,11 +31,12 @@ class WithTimeTest extends AnyFunSuite with Matchers {
TimeSyncOptions.binaryInput.withTime.id -> "false",
TimeSyncOptions.binaryInput.withTimeRejected.id -> "true"))
- test("8.14.2j passes a device declared to reject reads with time, noting it complies with 1815-2012 only") {
+ test("8.14.2j passes a device declared to reject reads with time, warning it complies with 1815-2012 only") {
val reporter = StubDriver.reporter(new Plugin)
val dut = new ScriptedDut(reporter, answer)
Run.passes(InputEvents.binaryAllWithTime, dut, rejecting) should equal(true)
- reporter.notes.head should include("IEEE 1815-2012, but not with IEEE 1815-202x")
+ reporter.warnings.head should include("IEEE 1815-2012, but not with IEEE 1815-202x")
+ reporter.notes should equal(Nil)
}
test("8.14.2k and l are skipped for that device") {
@@ -54,11 +55,12 @@ class WithTimeTest extends AnyFunSuite with Matchers {
TimeSyncOptions.binaryInput.withRelativeTime.id -> "false",
TimeSyncOptions.binaryInput.withRelativeTimeRejected.id -> "true"))
- test("8.14.2m passes a device declared to reject reads with relative time, noting it complies with 1815-2012") {
+ test("8.14.2m passes a device declared to reject reads with relative time, warning it complies with 1815-2012") {
val reporter = StubDriver.reporter(new Plugin)
val dut = new ScriptedDut(reporter, answer)
Run.passes(InputEvents.binaryAllWithRelativeTime, dut, rejectingRelative) should equal(true)
- reporter.notes.head should include("relative time with IIN2.1")
+ reporter.warnings.head should include("relative time with IIN2.1")
+ reporter.notes should equal(Nil)
}
test("8.14.2n to p are skipped for that device, and p for any device without relative time") {
|