From c6a23bf891b9c8437a7bbe232abc95337fc24187 Mon Sep 17 00:00:00 2001 From: Pierre Kisters Date: Mon, 17 Apr 2023 09:50:59 +0200 Subject: [PATCH] replaced scalatest with munit --- .github/workflows/publish.yml | 20 +- .github/workflows/test.yml | 13 +- README.md | 3 +- build.sbt | 107 ++++---- .../bms/base32/Base32Check1Spec.scala | 249 +++++++++--------- .../de/bitmarck/bms/base32/Base32Spec.scala | 96 +++---- 6 files changed, 259 insertions(+), 229 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index af8538f..8c88512 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,7 +2,7 @@ name: publish on: release: - types: [published] + types: [ published ] jobs: publish: @@ -11,17 +11,19 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + + - uses: actions/setup-java@v3.11.0 with: - distribution: 'adopt' - java-version: '8' - - name: Import PGP key - env: - PGP_PRIVATE_KEY: ${{ secrets.PGP_PRIVATE_KEY }} - run: gpg --import <(echo "$PGP_PRIVATE_KEY") + distribution: 'temurin' + java-version: '17' + cache: 'sbt' + gpg-private-key: ${{ secrets.PGP_PRIVATE_KEY }} + - name: Publish env: CI_VERSION: ${{ github.ref }} SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - run: sbt "; test; publishSigned; sonatypeBundleRelease" + SBT_OPTS: '-Xmx2G' + run: | + sbt "; test; sonatypeBundleClean; publishSigned; sonatypeBundleRelease" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0cbb9c..ce10dd7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,9 +15,14 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + + - uses: actions/setup-java@v3.11.0 with: - distribution: 'adopt' - java-version: '8' + distribution: 'temurin' + java-version: '17' + cache: 'sbt' + - name: Run tests - run: sbt "test" + env: + SBT_OPTS: '-Xmx2G' + run: sbt "; test" diff --git a/README.md b/README.md index fb87ae6..34cb3a9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # base32check-java -[![Test Workflow](https://github.com/bitmarck-service/base32check-java/workflows/test/badge.svg)](https://github.com/bitmarck-service/base32check-java/actions?query=workflow%3Atest) + +[![test](https://github.com/bitmarck-service/base32check-java/actions/workflows/test.yml/badge.svg)](https://github.com/bitmarck-service/base32check-java/actions/workflows/test.yml) [![Release Notes](https://img.shields.io/github/release/bitmarck-service/base32check-java.svg?maxAge=3600)](https://github.com/bitmarck-service/base32check-java/releases/latest) [![Maven Central](https://img.shields.io/maven-central/v/de.bitmarck.bms/base32check-java)](https://search.maven.org/artifact/de.bitmarck.bms/base32check-java) [![Apache License 2.0](https://img.shields.io/github/license/bitmarck-service/base32check-scala.svg?maxAge=3600)](https://www.apache.org/licenses/LICENSE-2.0) diff --git a/build.sbt b/build.sbt index 33267a8..65f021c 100644 --- a/build.sbt +++ b/build.sbt @@ -1,52 +1,63 @@ -organization := "de.bitmarck.bms" -name := "base32check-java" -version := "0.0.2-SNAPSHOT" +ThisBuild / scalaVersion := "2.13.10" +ThisBuild / autoScalaLibrary := false +ThisBuild / crossPaths := false +ThisBuild / versionScheme := Some("early-semver") +ThisBuild / organization := "de.bitmarck.bms" +name := (root / name).value + +val V = new { + val logbackClassic = "1.4.6" + val munit = "0.7.29" +} -javacOptions ++= Seq( - "-source", "1.8", - "-target", "1.8" +lazy val commonSettings: SettingsDefinition = Def.settings( + version := { + val Tag = "refs/tags/v?([0-9]+(?:\\.[0-9]+)+(?:[+-].*)?)".r + sys.env.get("CI_VERSION").collect { case Tag(tag) => tag } + .getOrElse("0.0.1-SNAPSHOT") + }, + + licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0")), + + homepage := Some(url("https://base32check.org")), + scmInfo := Some( + ScmInfo( + url("https://github.com/bitmarck-service/base32check-java"), + "scm:git@github.com:bitmarck-service/base32check-java.git" + ) + ), + developers := List( + Developer(id = "u016595", name = "Pierre Kisters", email = "pierre.kisters@bitmarck.de", url = url("https://github.com/lhns/")) + ), + + libraryDependencies ++= Seq( + "ch.qos.logback" % "logback-classic" % V.logbackClassic % Test, + "org.scalameta" %% "munit" % V.munit % Test, + ), + + testFrameworks += new TestFramework("munit.Framework"), + + Compile / doc / sources := Seq.empty, + + publishMavenStyle := true, + + publishTo := sonatypePublishToBundle.value, + + sonatypeCredentialHost := "oss.sonatype.org", + + credentials ++= (for { + username <- sys.env.get("SONATYPE_USERNAME") + password <- sys.env.get("SONATYPE_PASSWORD") + } yield Credentials( + "Sonatype Nexus Repository Manager", + sonatypeCredentialHost.value, + username, + password + )).toList ) -scalaVersion := "2.13.4" -autoScalaLibrary := false -crossPaths := false - -licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0")) - -homepage := Some(url("https://base32check.org")) -scmInfo := Some( - ScmInfo( - url("https://github.com/bitmarck-service/base32check-scala"), - "scm:git@github.com:bitmarck-service/base32check-scala.git" +lazy val root: Project = project.in(file(".")) + .settings(commonSettings) + .settings( + name := "base32check-java" ) -) -developers := List( - Developer(id = "u016595", name = "Pierre Kisters", email = "pierre.kisters@bitmarck.de", url = url("https://github.com/lhns/")) -) - -libraryDependencies ++= Seq( - "org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0" % Test, - "org.scalatest" %% "scalatest" % "3.2.15" % Test, -) - - -Compile / doc / sources := Seq.empty - -version := { - val tagPrefix = "refs/tags/" - sys.env.get("CI_VERSION").filter(_.startsWith(tagPrefix)).map(_.drop(tagPrefix.length)).getOrElse(version.value) -} - -publishMavenStyle := true - -publishTo := sonatypePublishToBundle.value - -credentials ++= (for { - username <- sys.env.get("SONATYPE_USERNAME") - password <- sys.env.get("SONATYPE_PASSWORD") -} yield Credentials( - "Sonatype Nexus Repository Manager", - "oss.sonatype.org", - username, - password -)).toList diff --git a/src/test/scala/de/bitmarck/bms/base32/Base32Check1Spec.scala b/src/test/scala/de/bitmarck/bms/base32/Base32Check1Spec.scala index 356fb54..0ac3070 100644 --- a/src/test/scala/de/bitmarck/bms/base32/Base32Check1Spec.scala +++ b/src/test/scala/de/bitmarck/bms/base32/Base32Check1Spec.scala @@ -1,151 +1,162 @@ package de.bitmarck.bms.base32 -import org.scalacheck.Gen -import org.scalatest.matchers.should.Matchers._ -import org.scalatest.wordspec.AnyWordSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ - -class Base32Check1Spec extends AnyWordSpec { - private val base32check1 = Base32Check1.getInstance() - - private val base32Char = Gen.oneOf(Base32Alphabet) - private val base32String = Gen.listOfN(16, base32Char).map(_.mkString) - - "Base32Check1" should { - val tests = Table( - "payload" -> "check1", - "" -> 'A', - "A" -> 'A', - "AB" -> 'Q', - "ABC" -> 'J', - "ABCD" -> 'V', - "ABCDE" -> 'I', - "ABCDEF" -> 'G', - "ABCDEFG" -> 'A', - "ABCDEFGH" -> 'T', - "ABCDEFGHI" -> '5', - "ABCDEFGHIJ" -> 'K', - "ABCDEFGHIJK" -> 'A', - "ABCDEFGHIJKL" -> 'F', - "ABCDEFGHIJKLM" -> 'U', - "ABCDEFGHIJKLMN" -> 'M', - "ABCDEFGHIJKLMNO" -> 'R', - "ABCDEFGHIJKLMNOP" -> '7', - "ABCDEFGHIJKLMNOPQ" -> 'X', - "ABCDEFGHIJKLMNOPQR" -> 'D', - "ABCDEFGHIJKLMNOPQRS" -> 'I', - "ABCDEFGHIJKLMNOPQRST" -> '5', - "ABCDEFGHIJKLMNOPQRSTU" -> 'U', - "ABCDEFGHIJKLMNOPQRSTUV" -> 'Q', - "ABCDEFGHIJKLMNOPQRSTUVW" -> 'D', - "ABCDEFGHIJKLMNOPQRSTUVWX" -> 'K', - "ABCDEFGHIJKLMNOPQRSTUVWXY" -> 'J', - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -> 'Y', - "ABCDEFGHIJKLMNOPQRSTUVWXYZ2" -> 'R', - "ABCDEFGHIJKLMNOPQRSTUVWXYZ23" -> 'V', - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234" -> 'U', - "ABCDEFGHIJKLMNOPQRSTUVWXYZ2345" -> 'U', - // 31 chars % 31 == 0 chars - see https://github.com/espadrine/base32check/pull/2 : - "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456" -> 'V', - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" -> 'V', - // 62 chars % 31 == 0 chars - see https://github.com/espadrine/base32check/pull/2 : - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ2345" -> '6', - // 93 chars % 31 == 0 chars - see https://github.com/espadrine/base32check/pull/2 : - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234" -> 'K', // 93 chars - "CONSECRATIO" -> 'X', - "CAFEBABE" -> 'N', - "CAFEDEAD" -> 'A', - "DEADBEEF" -> 'L', - "234567" -> 'Z' - ).ensuring( - _.forall { case (payload, check1) => Base32Regex.matches(payload + check1) }, - "String is not a valid Base32 string!" - ) - - "compute a checksum" in { - forAll(tests)((payload, check1) => base32check1.compute(payload) shouldBe check1) +import munit.FunSuite + +import scala.util.Random + +class Base32Check1Spec extends FunSuite { + private val base32Check1 = Base32Check1.getInstance() + + private val tests = Seq[(String, Char)]( + "" -> 'A', + "A" -> 'A', + "AB" -> 'Q', + "ABC" -> 'J', + "ABCD" -> 'V', + "ABCDE" -> 'I', + "ABCDEF" -> 'G', + "ABCDEFG" -> 'A', + "ABCDEFGH" -> 'T', + "ABCDEFGHI" -> '5', + "ABCDEFGHIJ" -> 'K', + "ABCDEFGHIJK" -> 'A', + "ABCDEFGHIJKL" -> 'F', + "ABCDEFGHIJKLM" -> 'U', + "ABCDEFGHIJKLMN" -> 'M', + "ABCDEFGHIJKLMNO" -> 'R', + "ABCDEFGHIJKLMNOP" -> '7', + "ABCDEFGHIJKLMNOPQ" -> 'X', + "ABCDEFGHIJKLMNOPQR" -> 'D', + "ABCDEFGHIJKLMNOPQRS" -> 'I', + "ABCDEFGHIJKLMNOPQRST" -> '5', + "ABCDEFGHIJKLMNOPQRSTU" -> 'U', + "ABCDEFGHIJKLMNOPQRSTUV" -> 'Q', + "ABCDEFGHIJKLMNOPQRSTUVW" -> 'D', + "ABCDEFGHIJKLMNOPQRSTUVWX" -> 'K', + "ABCDEFGHIJKLMNOPQRSTUVWXY" -> 'J', + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -> 'Y', + "ABCDEFGHIJKLMNOPQRSTUVWXYZ2" -> 'R', + "ABCDEFGHIJKLMNOPQRSTUVWXYZ23" -> 'V', + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234" -> 'U', + "ABCDEFGHIJKLMNOPQRSTUVWXYZ2345" -> 'U', + // 31 chars % 31 == 0 chars - see https://github.com/espadrine/base32check/pull/2 : + "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456" -> 'V', + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" -> 'V', + // 62 chars % 31 == 0 chars - see https://github.com/espadrine/base32check/pull/2 : + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ2345" -> '6', + // 93 chars % 31 == 0 chars - see https://github.com/espadrine/base32check/pull/2 : + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234" -> 'K', // 93 chars + "CONSECRATIO" -> 'X', + "CAFEBABE" -> 'N', + "CAFEDEAD" -> 'A', + "DEADBEEF" -> 'L', + "234567" -> 'Z' + ).ensuring( + _.forall { case (payload, check1) => Base32Regex.matches(payload + check1) }, + "String is not a valid Base32 string!" + ) + + private def randomString: String = + Array.fill(Random.nextInt(16) + 1)(Base32Alphabet(Random.nextInt(Base32Alphabet.length))).mkString + + private lazy val randomBase32Strings: Seq[String] = + Iterator.continually(randomString) + .filter(string => Base32Regex.matches(string) && string.length % 8 == 0) + .take(20) + .toSeq + + private def assertDifferentChecksum(original: String, modified: String): Unit = + if (original != modified) + assertNotEquals(base32Check1.compute(original), base32Check1.compute(modified)) + + test("Base32Check1 should compute a checksum") { + tests.foreach { case (payload, check1) => + assertEquals(base32Check1.compute(payload), check1) } + } - "accept a valid checksum" in { - forAll(tests)((payload, check1) => base32check1.validate(payload + check1) shouldBe true) + test("Base32Check1 should accept a valid checksum") { + tests.foreach { case (payload, check1) => + assert(base32Check1.validate(payload + check1)) } + } - "reject an invalid checksum" in { - forAll(tests)((payload, check1) => whenever(check1 != 'A')(base32check1.validate(payload) shouldBe false)) + test("Base32Check1 should reject an invalid checksum") { + tests.foreach { case (payload, check1) => + assertEquals(base32Check1.validate(payload), check1 == 'A') } + } - "throw up on input characters not in the Base32 alphabet" in { - val invalid = Table("payload") ++ (Char.MinValue to Char.MaxValue).map("" + _).filterNot(Base32Alphabet.contains) - forAll(invalid)(payload => intercept[IllegalArgumentException](base32check1.compute(payload))) + test("Base32Check1 should throw up on input characters not in the Base32 alphabet") { + val invalid: Seq[String] = (Char.MinValue to Char.MaxValue).map("" + _).filterNot(Base32Alphabet.contains) + invalid.foreach { payload => + intercept[IllegalArgumentException](base32Check1.compute(payload)) } + } - // https://espadrine.github.io/blog/posts/a-base32-checksum.html - "detect all single character substitutions (1sub)" in { - forAllBase32Strings { string => - forAll(base32Char) { char => - assertAll(string.indices.map(string -> string.updated(_, char))) + // https://espadrine.github.io/blog/posts/a-base32-checksum.html + test("Base32Check1 should detect all single character substitutions (1sub)") { + randomBase32Strings.foreach { string => + Base32Alphabet.foreach { char => + string.indices.map { + string -> string.updated(_, char) + }.foreach { case (original, modified) => + assertDifferentChecksum(original, modified) } } } + } - // https://espadrine.github.io/blog/posts/a-base32-checksum.html - "detect all character transpositions with zero characters in between them (0-trans)" in { - forAllBase32Strings { string => - assertAll(string.indices.drop(1).map { i => + // https://espadrine.github.io/blog/posts/a-base32-checksum.html + test("Base32Check1 should detect all character transpositions with zero characters in between them (0-trans)") { + randomBase32Strings.foreach { string => + string.indices.drop(1) + .map { i => string -> string.updated(i - 1, string(i)).updated(i, string(i - 1)) - }) + }.foreach { case (original, modified) => + assertDifferentChecksum(original, modified) } } + } - // https://espadrine.github.io/blog/posts/a-base32-checksum.html - "detect all character transpositions with one character in between them (1-trans)" in { - forAllBase32Strings { string => - assertAll(string.indices.drop(2).map { i => - string -> string.updated(i - 2, string(i)).updated(i, string(i - 2)) - }) + // https://espadrine.github.io/blog/posts/a-base32-checksum.html + test("Base32Check1 should detect all character transpositions with one character in between them (1-trans)") { + randomBase32Strings.foreach { string => + string.indices.drop(2).map { i => + string -> string.updated(i - 2, string(i)).updated(i, string(i - 2)) + }.foreach { case (original, modified) => + assertDifferentChecksum(original, modified) } } + } - // https://espadrine.github.io/blog/posts/a-base32-checksum.html - "detect all identical substitutions of two identical characters with zero characters in between them (0-twin)" in { - forAllBase32Strings { string => - forAll(base32Char, minSuccessful(1)) { origChar => - forAll(base32Char) { substChar => - assertAll(string.indices.drop(1).map { i => - string.updated(i - 1, origChar).updated(i, origChar) -> string.updated(i - 1, substChar).updated(i, substChar) - }) + // https://espadrine.github.io/blog/posts/a-base32-checksum.html + test("Base32Check1 should detect all identical substitutions of two identical characters with zero characters in between them (0-twin)") { + randomBase32Strings.foreach { string => + Base32Alphabet.foreach { origChar => // minSuccessful(1) + Base32Alphabet.foreach { substChar => + string.indices.drop(1).map { i => + string.updated(i - 1, origChar).updated(i, origChar) -> string.updated(i - 1, substChar).updated(i, substChar) + }.foreach { case (original, modified) => + assertDifferentChecksum(original, modified) } } } } + } - // https://espadrine.github.io/blog/posts/a-base32-checksum.html - "detect all identical substitutions of two identical characters with one character in between them (1-twin)" in { - forAllBase32Strings { string => - forAll(base32Char, minSuccessful(1)) { origChar => - forAll(base32Char) { substChar => - assertAll(string.indices.drop(2).map { i => - string.updated(i - 2, origChar).updated(i, origChar) -> string.updated(i - 2, substChar).updated(i, substChar) - }) + // https://espadrine.github.io/blog/posts/a-base32-checksum.html + test("Base32Check1 should detect all identical substitutions of two identical characters with one character in between them (1-twin)") { + randomBase32Strings.foreach { string => + Base32Alphabet.foreach { origChar => // minSuccessful(1) + Base32Alphabet.foreach { substChar => + string.indices.drop(2).map { i => + string.updated(i - 2, origChar).updated(i, origChar) -> string.updated(i - 2, substChar).updated(i, substChar) + }.foreach { case (original, modified) => + assertDifferentChecksum(original, modified) } } } } } - - private def forAllBase32Strings(f: String => Any): Unit = { - forAll(base32String) { string: String => - whenever(Base32Regex.matches(string) && string.length % 8 == 0) { - f(string) - } - } - } - - private def assertAll(tests: Seq[(String, String)]): Unit = { - forAll(Table("original" -> "modified") ++ tests) { (original, modified) => - whenever(original != modified) { - base32check1.compute(original) should not be base32check1.compute(modified) - } - } - } } diff --git a/src/test/scala/de/bitmarck/bms/base32/Base32Spec.scala b/src/test/scala/de/bitmarck/bms/base32/Base32Spec.scala index 1b10d1a..cb41879 100644 --- a/src/test/scala/de/bitmarck/bms/base32/Base32Spec.scala +++ b/src/test/scala/de/bitmarck/bms/base32/Base32Spec.scala @@ -1,59 +1,59 @@ package de.bitmarck.bms.base32 -import java.nio.charset.StandardCharsets.US_ASCII +import munit.FunSuite -import org.scalacheck.Arbitrary._ -import org.scalacheck.Gen._ -import org.scalatest.matchers.should.Matchers._ -import org.scalatest.wordspec.AnyWordSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ - -class Base32Spec extends AnyWordSpec { - "Base32Codec" should { - // See [RFC 4648, Section 10](https://tools.ietf.org/html/rfc4648#section-10): - val rfc4648Tests = Table("bytes" -> "uppercaseString") ++ Seq( - "" -> "", - "f" -> "MY======", - "fo" -> "MZXQ====", - "foo" -> "MZXW6===", - "foob" -> "MZXW6YQ=", - "fooba" -> "MZXW6YTB", - "foobar" -> "MZXW6YTBOI======" - ).map { case (input, output) => (input.getBytes(US_ASCII), output) } - - // Note that `BigInt("...", 16).toByteArray` is converted using big-endian byte order including a sign bit, which - // results in a leading sign byte, which needs to be dropped to produce the expected results: - val customTests = Table("bytes" -> "uppercaseString") ++ Seq( - "cafebabe" -> "ZL7LVPQ=", - "cafedead" -> "ZL7N5LI=", - "deadbeef" -> "32W353Y=", - "cafebabe deadbeef cafe" -> "ZL7LVPW6VW7O7SX6" - ).map { case (input, output) => (BigInt(input.filter(_ != ' '), 16).toByteArray.drop(1), output) } - - val tests = (rfc4648Tests ++ customTests).ensuring( - _.forall { case (_, uppercaseString) => Base32Regex.matches(uppercaseString) }, - "String is not a valid Base32 production!" - ) - - "decode a sequence of bytes from a string" in { - forAll(tests) { (bytes, uppercaseString) => - Base32.decode(uppercaseString) shouldBe bytes - } +import java.nio.charset.StandardCharsets.US_ASCII +import scala.util.Random + +class Base32Spec extends FunSuite { + // See [RFC 4648, Section 10](https://tools.ietf.org/html/rfc4648#section-10): + val rfc4648Tests: Seq[(Array[Byte], String)] = Seq[(String, String)]( + "" -> "", + "f" -> "MY======", + "fo" -> "MZXQ====", + "foo" -> "MZXW6===", + "foob" -> "MZXW6YQ=", + "fooba" -> "MZXW6YTB", + "foobar" -> "MZXW6YTBOI======" + ).map { case (input, output) => (input.getBytes(US_ASCII), output) } + + // Note that `BigInt("...", 16).toByteArray` is converted using big-endian byte order including a sign bit, which + // results in a leading sign byte, which needs to be dropped to produce the expected results: + val customTests: Seq[(Array[Byte], String)] = Seq[(String, String)]( + "cafebabe" -> "ZL7LVPQ=", + "cafedead" -> "ZL7N5LI=", + "deadbeef" -> "32W353Y=", + "cafebabe deadbeef cafe" -> "ZL7LVPW6VW7O7SX6" + ).map { case (input, output) => (BigInt(input.filter(_ != ' '), 16).toByteArray.drop(1), output) } + + val tests: Seq[(Array[Byte], String)] = (rfc4648Tests ++ customTests).ensuring( + _.forall { case (_, uppercaseString) => Base32Regex.matches(uppercaseString) }, + "String is not a valid Base32 production!" + ) + + test("decode a sequence of bytes from a string") { + tests.foreach { case (bytes, uppercaseString) => + assertEquals(Base32.decode(uppercaseString).toSeq, bytes.toSeq) } + } - "encode a sequence of bytes to a string" in { - forAll(tests) { (bytes, uppercaseString) => - Base32.encode(bytes) shouldBe uppercaseString - } + test("encode a sequence of bytes to a string") { + tests.foreach { case (bytes, uppercaseString) => + assertEquals(Base32.encode(bytes), uppercaseString) } + } + + private def randomString: String = Random.nextString(Random.nextInt(32) + 1) + lazy val randomInvalidBase32Strings: Seq[String] = + Iterator.continually(randomString) + .filter(string => !Base32Regex.matches(string) || string.length % 8 != 0) + .take(20) + .toSeq - "throw up when decoding a string which is not a valid Base32 encoding" in { - forAll(listOf(arbitrary[Char]).map(s => s.mkString), minSuccessful(100)) { string => - whenever(!Base32Regex.matches(string) || string.length % 8 != 0) { - intercept[IllegalArgumentException](Base32.decode(string)) - } - } + test("throw up when decoding a string which is not a valid Base32 encoding") { + randomInvalidBase32Strings.foreach { string => + intercept[IllegalArgumentException](Base32.decode(string)) } } }