Skip to content

Commit

Permalink
better definition of the constraint for the name of a position / fiel…
Browse files Browse the repository at this point in the history
…d of view (FOV)
  • Loading branch information
vreuter committed Oct 14, 2024
1 parent da6fe28 commit 046151d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 4 deletions.
9 changes: 6 additions & 3 deletions modules/imaging/src/main/scala/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.github.iltotore.iron.cats.given
import io.github.iltotore.iron.constraint.any.{Not, StrictEqual}
import io.github.iltotore.iron.constraint.char.{Digit, Letter}
import io.github.iltotore.iron.constraint.collection.{Empty, ForAll}
import io.github.iltotore.iron.constraint.string.Match

import at.ac.oeaw.imba.gerlich.gerlib.numeric.*
import at.ac.oeaw.imba.gerlich.gerlib.numeric.instances.nonnegativeInt.given
Expand Down Expand Up @@ -62,10 +63,12 @@ package object imaging:

/** A position name must be nonempty and contain only certain characters;
* furthermore, it must not start with a hyphen for potential ambiguity with
* a negative number.
* a negative number. We also exclude a number in scientific notation.
*/
private type PositionNameConstraint = Not[Empty] & Not[ForAll[Digit]] &
ForAll[PositionNameCharacterConstraint]
private type PositionNameConstraint = Not[Empty] &
ForAll[PositionNameCharacterConstraint] &
Not[Match["-?[0-9]+\\.?[0-9]*"]] & // Exclude orginary numbers.
Not[Match["-?[0-9]\\.?[0-9]+E-?[0-9]{1,3}"]] // Exclude scientific notation.

/** The name of a position / field of view is a string whose characters all
* fulfill the constraint.
Expand Down
52 changes: 51 additions & 1 deletion modules/imaging/src/test/scala/TestFieldOfViewLike.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package at.ac.oeaw.imba.gerlich.gerlib
package imaging

import org.scalacheck.Gen
import org.scalacheck.{Gen, Shrink}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
Expand Down Expand Up @@ -50,4 +50,54 @@ class TestFieldOfViewLike
}
}
}

test("PositionName cannot be parsed from an integer.") {
given noShrink[A]: Shrink[A] = Shrink.shrinkAny[A]

forAll { (z: Int) =>
val input = z.toString
PositionName.parse(input) match {
case Left(msg) =>
msg.startsWith(s"Could not refine string ($input)") shouldBe true
case Right(name) =>
fail(
s"Expected PositionName.parse to fail on input '${input}', but it succeessfully yielded ${name}"
)
}
}
}

test("PositionName cannot be parsed from any real number.") {
forAll { (x: Double) =>
val input = x.toString
PositionName.parse(input) match {
case Left(msg) =>
msg.startsWith(s"Could not refine string ($input)") shouldBe true
case Right(name) =>
fail(
s"Expected PositionName.parse to fail on input '${input}', but it succeessfully yielded ${name}"
)
}
}
}

test(
"Regression: PositionName will NOT be parsed from an exponent-like (scientific notation) string."
) {
given noShrink[A]: Shrink[A] = Shrink.shrinkAny[A]
def chooseBase =
Gen.oneOf(Gen.choose(-9.999, -1.001), Gen.choose(1.001, 9.999))
def chooseExponent = Gen.oneOf(Gen.choose(-308, -1), Gen.choose(1, 308))
forAll(chooseBase, chooseExponent) { (base, exponent) =>
val input = s"${base}E${exponent}"
PositionName.parse(input) match {
case Left(msg) =>
msg.startsWith(s"Could not refine string ($input)") shouldBe true
case Right(name) =>
fail(
s"Expected PositionName.parse to fail on input '${input}', but it succeessfully yielded ${name}"
)
}
}
}
end TestFieldOfViewLike
5 changes: 5 additions & 0 deletions modules/imaging/src/test/scala/TestImagingInstances.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ class TestImagingInstances
ScalaCheckPropertyChecks,
should.Matchers:

override implicit val generatorDrivenConfig: PropertyCheckConfiguration =
// Boost up the minimum number of successes since the tests are fast, but the
// space of cases is complex.
PropertyCheckConfiguration(minSuccessful = 10000)

given Arbitrary[PositionName] =
import io.github.iltotore.iron.autoRefine
type GoodChar = Char :| PositionNameCharacterConstraint
Expand Down

0 comments on commit 046151d

Please sign in to comment.