{"id":30565611,"url":"https://github.com/sageserpent-open/americium","last_synced_at":"2026-02-28T17:00:24.252Z","repository":{"id":3364012,"uuid":"4410073","full_name":"sageserpent-open/americium","owner":"sageserpent-open","description":"Generation of test case data for Scala and Java, in the spirit of QuickCheck. When your test fails, it gives you a minimised failing test case and a way of reproducing the failure immediately.","archived":false,"fork":false,"pushed_at":"2026-02-21T16:01:37.000Z","size":37189,"stargazers_count":17,"open_issues_count":14,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-21T21:26:44.151Z","etag":null,"topics":["java","parameterised-tests","property-based-testing","scala","testing-tools"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sageserpent-open.png","metadata":{"files":{"readme":"README-OLD.markdown","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2012-05-22T17:23:58.000Z","updated_at":"2026-02-21T14:56:19.000Z","dependencies_parsed_at":"2024-01-05T15:28:03.350Z","dependency_job_id":"0f2da669-2155-4622-ba66-6526344e07aa","html_url":"https://github.com/sageserpent-open/americium","commit_stats":null,"previous_names":[],"tags_count":129,"template":false,"template_full_name":null,"purl":"pkg:github/sageserpent-open/americium","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sageserpent-open%2Famericium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sageserpent-open%2Famericium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sageserpent-open%2Famericium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sageserpent-open%2Famericium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sageserpent-open","download_url":"https://codeload.github.com/sageserpent-open/americium/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sageserpent-open%2Famericium/sbom","scorecard":{"id":364496,"data":{"date":"2025-08-11","repo":{"name":"github.com/sageserpent-open/americium","commit":"324e4ee54cb90723be1f4c6f7bb28271f822339f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":6.1,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":9,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: jobLevel 'contents' permission set to 'write': .github/workflows/release.yaml:9","Warn: no topLevel permission defined: .github/workflows/release.yaml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/scala.yml:15"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":6,"reason":"Found 13/19 approved changesets -- score normalized to 6","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yaml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/sageserpent-open/americium/release.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scala.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/sageserpent-open/americium/scala.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scala.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/sageserpent-open/americium/scala.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scala.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/sageserpent-open/americium/scala.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":3,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'master'","Info: 'force pushes' disabled on branch 'master'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'master'","Warn: could not determine whether codeowners review is allowed","Warn: no status checks found to merge onto branch 'master'","Warn: PRs are not required to make changes on branch 'master'; or we don't have data to detect it.If you think it might be the latter, make sure to run Scorecard with a PAT or use Repo Rules (that are always public) instead of Branch Protection settings"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 24 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T11:32:49.928Z","repository_id":3364012,"created_at":"2025-08-18T11:32:49.928Z","updated_at":"2025-08-18T11:32:49.928Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29943631,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T13:49:17.081Z","status":"ssl_error","status_checked_at":"2026-02-28T13:48:50.396Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","parameterised-tests","property-based-testing","scala","testing-tools"],"created_at":"2025-08-28T16:01:36.936Z","updated_at":"2026-02-28T17:00:24.224Z","avatar_url":"https://github.com/sageserpent-open.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Americium - **_Test cases galore! Automatic case shrinkage! Bring your own test style. For Scala and Java..._**\n\n[![Maven Central](https://index.scala-lang.org/sageserpent-open/americium/americium/latest-by-scala-version.svg?color=2465cd\u0026style=flat)](https://index.scala-lang.org/sageserpent-open/americium/americium)\n\n```java\nimport com.google.common.collect.ImmutableList;\nimport com.sageserpent.americium.java.Trials;\nimport com.sageserpent.americium.java.TrialsApi;\nimport com.sageserpent.americium.java.TrialsFactoring;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collection;\n\npublic class SystemUnderTest {\n    public static void printSum(Collection\u003cInteger\u003e input) {\n        final int sum =\n                input.stream().reduce((lhs, rhs) -\u003e lhs + rhs).get(); // Oops.\n\n        System.out.println(sum);\n    }\n}\n\nclass ReallySimpleTestSuite {\n    final TrialsApi api = Trials.api();\n\n    final Trials\u003cImmutableList\u003cInteger\u003e\u003e trials =\n            api.integers().immutableLists();\n\n    @Test\n    void doesItEmitSmoke() {\n        try {\n            trials.withLimit(100).supplyTo(SystemUnderTest::printSum);\n        } catch (\n                TrialsFactoring.TrialException exception) {\n            System.out.println(exception.getCause()); // java.util.NoSuchElementException: No value present\n            System.out.println(exception.provokingCase()); // []\n            System.out.println(exception.recipe()); // [{\"ChoiceOf\" : {\"index\" : 0}}]\n        }\n\n        try {\n            trials.withRecipe(\"[{\\\"ChoiceOf\\\" : {\\\"index\\\" : 0}}]\")\n                  .supplyTo(SystemUnderTest::printSum);\n        } catch (\n                TrialsFactoring.TrialException exception) {\n            System.out.println(exception.getCause()); // java.util.NoSuchElementException: No value present\n            System.out.println(exception.provokingCase()); // []\n            System.out.println(exception.recipe()); // [{\"ChoiceOf\" : {\"index\" : 0}}]\n        }\n    }\n}\n```\n\n## What? ##\n\nWelcome to Americium, the home of the `Trials` testing utility for Scala and Java.\n\n`Trials` ...\n\n1. Generates test case data for parameterised tests.\n2. Offers automatic and efficient shrinkage to a minimal or nearly-minimal test case. __Yes, invariants are preserved on\n   test case data.__ No, you don't need to write custom shrinkage code.\n3. Offers __direct reproduction of a failing, minimised test case__.\n4. Gets out of the way of testing style - doesn't care about whether the tests are pure functional or imperative,\n   doesn't offer a DSL or try to structure your test suite.\n5. Supports Scala and Java as first class citizens.\n   \u003cbr\u003e\u003cbr\u003e\n7. Has an __optional__ integration with JUnit5 in the spirit of the `@ParameterizedTest` annotation.\n   \u003cbr\u003e\u003cbr\u003e\n9. Covers finite combinations of atomic cases without duplication when building composite cases.\n10. Supports covariance of test case generation in Scala, so cases for a subclass can be substituted for cases for a\n    supertrait/superclass.\n11. Supports covariance of test case generation in Java, so cases for a subclass can be substituted for cases for a\n    superinterface/superclass.\n12. Allows automatic derivation of test case generation for sum/product types (aka case class hierarchies) in the spirit\n    of Scalacheck Shapeless.\n\nIn addition, there are some enhancements to the Scala `Random` class that might also pique your interest, but go see for\nyourself in the code, it's simple enough...\n\n## Cookbook ##\n\n- Start with a trials api for either Java or Scala.\n- Coax some trials instances out of the api - either use the factory methods that give you canned trials instances, or\n  specify your own cases to choose from (either with equal probability or with weights), or hard-wire in some single\n  value.\n- Transform them by mapping.\n- Combine them together by flat-mapping.\n- Filter out what you don't want.\n- You can alternate between different ways of making the same shape of case data, either with equal probability or with\n  weights.\n- Use helper methods to make a trials from some collection out of a simpler trials for the collection's elements.\n- Once you've built up the right kind of trials instance, put it to use: specify an upper limit for the number of cases\n  you want to examine and feed them to your test code. When your test code throws an exception, the trials machinery\n  will try to shrink down whatever test case caused it.\n\n### Java ###\n\n```java\n\n\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableSortedSet;\nimport com.google.common.collect.Maps;\nimport org.junit.jupiter.api.Test;\n\nimport java.awt.*;\nimport java.awt.geom.Ellipse2D;\nimport java.awt.geom.Rectangle2D;\nimport java.math.BigInteger;\nimport java.time.*;\n\nclass Cookbook {\n    /* Start with a trials api for Java. */\n\n    private final static TrialsApi api = Trials.api();\n\n    /*\n     Coax some trials instances out of the api...\n     ... either use the factory methods that give you canned trials instances\n      ...\n    */\n\n    final Trials\u003cInteger\u003e integers = api.integers();\n\n    final Trials\u003cString\u003e strings = api.strings();\n\n    final Trials\u003cInstant\u003e instants = api.instants();\n\n    /*\n     ... or specify your own cases to choose from ...\n     ... either with equal probability ...\n    */\n\n    final Trials\u003cColor\u003e colors = api.choose(Color.RED, Color.GREEN, Color.BLUE);\n\n    /* ... or with weights ... */\n\n    final Trials\u003cString\u003e elementsInTheHumanBody = api.chooseWithWeights(\n            Maps.immutableEntry(65,\n                                \"Oxygen\"),\n            Maps.immutableEntry(18,\n                                \"Carbon\"),\n            Maps.immutableEntry(10,\n                                \"Hydrogen\"),\n            Maps.immutableEntry(3,\n                                \"Nitrogen\"));\n\n    /* ... or hard-wire in some single value. */\n\n    final Trials\u003cObject\u003e thisIsABitEmbarrassing = api.only(null);\n\n    /* Transform them by mapping. */\n\n    final Trials\u003cInteger\u003e evenNumbers = integers.map(integral -\u003e 2 * integral);\n\n    final Trials\u003cZoneId\u003e zoneIds =\n            api\n                    .choose(\"UTC\",\n                            \"Europe/London\",\n                            \"Asia/Singapore\",\n                            \"Atlantic/Madeira\")\n                    .map(ZoneId::of);\n\n    /* Combine them together by flat-mapping. */\n\n    final Trials\u003cZonedDateTime\u003e zonedDateTimes =\n            instants.flatMap(instant -\u003e zoneIds.map(zoneId -\u003e ZonedDateTime.ofInstant(\n                    instant,\n                    zoneId)));\n\n    /* Filter out what you don't want. */\n\n    final Trials\u003cZonedDateTime\u003e notOnASunday = zonedDateTimes.filter(\n            zonedDateTime -\u003e !zonedDateTime\n                    .toOffsetDateTime()\n                    .getDayOfWeek()\n                    .equals(DayOfWeek.SUNDAY));\n\n    /*\n     You can alternate between different ways of making the same shape \n     case data...\n     ... either with equal probability ...\n    */\n\n    final Trials\u003cRectangle2D\u003e rectangles =\n            api.doubles().flatMap(x -\u003e api.doubles().flatMap(\n                    y -\u003e api\n                            .doubles()\n                            .flatMap(w -\u003e api\n                                    .doubles()\n                                    .map(h -\u003e new Rectangle2D.Double(x,\n                                                                     y,\n                                                                     w,\n                                                                     h)))));\n\n    final Trials\u003cEllipse2D\u003e ellipses =\n            api.doubles().flatMap(x -\u003e api.doubles().flatMap(\n                    y -\u003e api\n                            .doubles()\n                            .flatMap(w -\u003e api\n                                    .doubles()\n                                    .map(h -\u003e new Ellipse2D.Double(x,\n                                                                   y,\n                                                                   w,\n                                                                   h)))));\n\n    final Trials\u003cShape\u003e shapes = api.alternate(rectangles, ellipses);\n\n    /* ... or with weights. */\n\n    final Trials\u003cBigInteger\u003e likelyToBePrime = api.alternateWithWeights(\n            Maps.immutableEntry(10,\n                                api\n                                        .choose(1, 3, 5, 7, 11, 13, 17, 19)\n                                        .map(BigInteger::valueOf)),\n            // Mostly from this pool of small primes - nice and quick.\n            Maps.immutableEntry(1,\n                                api\n                                        .longs()\n                                        .map(BigInteger::valueOf)\n                                        .map(BigInteger::nextProbablePrime))\n            // Occasionally we want a big prime and will pay the cost of \n            // computing it.\n    );\n\n    /* Use helper methods to make a trials from some collection out of a simpler\n     trials for the collection's elements. */\n\n    final Trials\u003cImmutableList\u003cShape\u003e\u003e listsOfShapes = shapes.immutableLists();\n\n    final Trials\u003cImmutableSortedSet\u003cBigInteger\u003e\u003e sortedSetsOfPrimes =\n            likelyToBePrime.immutableSortedSets(BigInteger::compareTo);\n\n    /*\n     Once you've built up the right kind of trials instance, put it to\n     use: specify an upper limit for the number of cases you want to examine\n     and feed them to your test code. When your test code throws an exception,\n     the trials machinery will try to shrink down whatever test case caused it.\n    */\n\n    @Test\n    public void theExtraDayInALeapYearIsJustNotToleratedIfItsNotOnASunday() {\n        notOnASunday.withLimit(50).supplyTo(when -\u003e {\n            final LocalDate localDate = when.toLocalDate();\n\n            try {\n                assert !localDate.getMonth().equals(Month.FEBRUARY) ||\n                       localDate.getDayOfMonth() != 29;\n            } catch (AssertionError exception) {\n                System.out.println(when);   // Watch the shrinkage in action!\n                throw exception;\n            }\n        });\n    }\n}\n```\n\n### Scala ###\n\n```scala\nimport com.sageserpent.americium.Trials.api\n\nimport org.scalatest.flatspec.AnyFlatSpec\n\nimport java.awt.geom.{Ellipse2D, Rectangle2D}\nimport java.awt.{List =\u003e _, _}\nimport java.math.BigInteger\nimport java.time._\nimport scala.collection.immutable.SortedSet\n\nclass Cookbook extends AnyFlatSpec {\n  /* Coax some trials instances out of the api...\n   * ... either use the factory methods that give you canned trials instances\n   * ... */\n  val integers: Trials[Int] = api.integers\n\n  val strings: Trials[String] = api.strings\n\n  val instants: Trials[Instant] = api.instants\n\n  /* ... or specify your own cases to choose from ...\n   * ... either with equal probability ... */\n\n  val colors: Trials[Color] =\n    api.choose(Color.RED, Color.GREEN, Color.BLUE)\n\n  /* ... or with weights ... */\n\n  val elementsInTheHumanBody: Trials[String] = api.chooseWithWeights(\n    65 -\u003e \"Oxygen\",\n    18 -\u003e \"Carbon\",\n    10 -\u003e \"Hydrogen\",\n    3 -\u003e \"Nitrogen\"\n  )\n\n  /* ... or hard-wire in some single value. */\n\n  val thisIsABitEmbarrassing: Trials[Null] = api.only(null)\n\n  /* Transform them by mapping. */\n\n  val evenNumbers: Trials[Int] = integers.map(integral =\u003e 2 * integral)\n\n  val zoneIds: Trials[ZoneId] = api\n    .choose(\"UTC\", \"Europe/London\", \"Asia/Singapore\", \"Atlantic/Madeira\")\n    .map(ZoneId.of)\n\n  /* Combine them together by flat-mapping. */\n\n  val zonedDateTimes: Trials[ZonedDateTime] =\n    for {\n      instant \u003c- instants\n      zoneId \u003c- zoneIds\n    } yield ZonedDateTime.ofInstant(instant, zoneId)\n\n  /* Filter out what you don't want. */\n\n  val notOnASunday: Trials[ZonedDateTime] =\n    zonedDateTimes.filter(_.toOffsetDateTime.getDayOfWeek != DayOfWeek.SUNDAY)\n\n  /* You can alternate between different ways of making the same shape case\n   * data...\n   * ... either with equal probability ... */\n\n  val rectangles: Trials[Rectangle2D.Double] =\n    for {\n      x \u003c- api.doubles\n      y \u003c- api.doubles\n      w \u003c- api.doubles\n      h \u003c- api.doubles\n    } yield new Rectangle2D.Double(x, y, w, h)\n\n  val ellipses: Trials[Ellipse2D.Double] = for {\n    x \u003c- api.doubles\n    y \u003c- api.doubles\n    w \u003c- api.doubles\n    h \u003c- api.doubles\n  } yield new Ellipse2D.Double(x, y, w, h)\n\n  val shapes: Trials[Shape] = api.alternate(rectangles, ellipses)\n\n  /* ... or with weights. */\n\n  val likelyToBePrime: Trials[BigInt] = api.alternateWithWeights(\n    10 -\u003e api\n      .choose(1, 3, 5, 7, 11, 13, 17, 19)\n      .map(\n        BigInt.apply\n      ), // Mostly from this pool of small primes - nice and quick.\n    1 -\u003e api.longs\n      .map(BigInteger.valueOf)\n      .map(\n        _.nextProbablePrime: BigInt\n      ) // Occasionally we want a big prime and will pay the cost of computing it.\n  )\n\n  /* Use helper methods to make a trials from some collection out of a simpler\n   * trials for the collection's elements. */\n\n  val listsOfShapes: Trials[List[Shape]] =\n    shapes.lists\n\n  val sortedSetsOfPrimes: Trials[SortedSet[_ \u003c: BigInt]] =\n    likelyToBePrime.sortedSets\n\n  /* Once you've built up the right kind of trials instance, put it to use:\n   * specify an upper limit for the number of cases you want to examine and feed\n   * them to your test code. When your test code throws an exception, the trials\n   * machinery will try to shrink down whatever test case caused it. */\n\n  \"the extra day in a leap year\" should \"not be tolerated if its not on a Sunday\" in {\n    notOnASunday\n      .withLimit(50)\n      .supplyTo { when =\u003e\n        val localDate = when.toLocalDate\n        try\n          assert(\n            !(localDate.getMonth == Month.FEBRUARY) || localDate.getDayOfMonth != 29\n          )\n        catch {\n          case exception =\u003e\n            println(when) // Watch the shrinkage in action!\n\n            throw exception\n        }\n      }\n  }\n}\n\n```\n\n## Why? ##\n\nYou like writing parameterised tests - so you have a block of test code expressed as a test method or function of lambda\nform, and something that pumps test cases into that code block as one or more arguments.\n\nAt this point, the likes of QuickCheck, FsCheck and Scalacheck come to mind, amongst others. If you are working in\nScala, then you'll probably be thinking of Scalacheck, maybe ZioTest, perhaps Hedgehog...? If in Java, then Jqwik,\nJUnit-QuickCheck, QuickTheories or possibly VavrTest?\n\nAll great things - the author has had the benefit of using Scalacheck for several years on various Scala works, finding\nall kinds of obscure, knotty bugs that would otherwise lay hidden until the fateful day in production. Likewise VavrTest\nhas helped for the Java works. Fun has been had with ZioTest and Hedgehog too...\n\nHowever, one nagging problem with both Scalacheck and VavrTest is in the matter of test case shrinkage - it's all very\nwell when a parameterised test fails for some case, but reproducing and debugging the failure can be a real pain.\n\nFor one thing, not all frameworks allow direct reproduction of the offending test case - so if each individual test\nexecution for a piece of data takes appreciable time, then running the entire parameterised test up to the point of\nfailure can take minutes for more sophisticated tests. What's more, the test case that provokes the test failure may be\nextraordinarily complex; these frameworks all use the notion of building up test cases based on combining randomly\nvarying data into bigger and bigger chunks, which often means that whatever provokes a failure is buried in a complex\ntest case with additional random data that is of no relevance to the failure.\n\nFor example, if we are testing a stable sorting algorithm in Scala, we may find due to the use of weak equality in the\nsort algorithm that it is not stable, so equivalent entries in the list are rearranged in order with respect to each\nother:\n\n```scala\nimport scala.math.Ordering\n\n// Insertion sort, but with a bug...\ndef notSoStableSort[Element](\n                              elements: List[Element]\n                            )(implicit ordering: Ordering[Element]): List[Element] =\n  elements match {\n    case Nil =\u003e Nil\n    case head :: tail =\u003e\n      // Spot the deliberate mistake......vvvv\n      notSoStableSort(tail).span(ordering.lteq(_, head)) match {\n        case (first, second) =\u003e first ++ (head :: second)\n      }\n  }\n\nnotSoStableSort(Nil: List[(Int, Int)])(\n  Ordering.by(_._1)\n) // List() - Hey - worked first time...\nnotSoStableSort(List(1 -\u003e 2))(\n  Ordering.by(_._1)\n) // List((1,2)) - Yeah, check those edge cases!\nnotSoStableSort(List(1 -\u003e 2, -1 -\u003e 9))(\n  Ordering.by(_._1)\n) // List((-1,9), (1,2)) - Fancy a beer, anyone?\nnotSoStableSort(List(1 -\u003e 2, -1 -\u003e 9, 1 -\u003e 3))(\n  Ordering.by(_._1)\n) // List((-1,9), (1,3), (1,2)) ? Uh? I wanted List((-1,9), (1,2), (1,3))!!!!\nnotSoStableSort(List(1 -\u003e 2, 1 -\u003e 3))(\n  Ordering.by(_._1)\n) // List((1,3), (1,2)) ? Huh! I wanted List((1,2), (1,3)) - going to be working overtime...\n\n```\n\nNow this isn't so painful because it's a toy problem and we know exactly where to start debugging, and therefore how to\nminimise the test case (the last one is a minimal case, all we need to do is submit two entries that are not equal\nby `==` but are in terms of the supplied ordering).\n\nThink this is always going to be the case? Take a look at this one (currently unsolved, still using\nScalacheck): https://github.com/sageserpent-open/plutonium/issues/57 - in particular, look at the test failure logs on\nthe ticket. All that gibberish in the logs is *one single test case*. Want to try debugging through that? How would you\nminimise it?\n\nThis is made all the worse by the rarity of this bug - in fact, Scalacheck used to use random seed values back when this\nbug was first encountered, so the the test only failed once in a blue moon. To make this failure reproducible each time\nmeans that the test has to run a *long, long* time. Even more fun if you're in a debugging session watching your\nbreakpoints being hit for several hundred successful cases before you get to the one that finally fails, whichever it\nis...\n\nWhat we want here is something that __automatically shrinks a failing test case down to a minimal test case__ (or at\nleast reasonably close to one), and provides some way of __reproducing this minimal test case directly__ without having\nto slog through a whole bunch of successful cases we aren't interested in.\n\nAfter toiling through quite a few of these monster test failures in the Plutonium, Curium and several commercial\nprojects, the author decided to address this issue.\n\nTo be fair, there are some frameworks out there that also offer automatic test case shrinkage - your mileage may vary.\nScalacheck does this, but with caveats: https://github.com/typelevel/scalacheck/pull/440. ZioTest does this too, give it\na whirl and see how you fare. So does Hedgehog for that matter...\n\nThis brings us to the next pain point for the author, which is the extent to which the framework has opinions about how\nyour code is to be structured. Scalacheck comes not only with generation of test cases, but its own property-checking\nDSL and style of assembling a test suite, which you may or may not buy into. There is an integration into Scalatest so\nthat you can supply test cases to a Scalatest test - perhaps you might like that better? MUnit will let you use\nScalacheck, but you are back to its own DSL ... or perhaps you'd prefer UTest - not sure what you'd do there...\n\n... or maybe you write in Java and use JUnit? What then? VavrTest doesn't at time of writing offer any shrinkage\nsupport.\n\nWhat the author wanted was something that acts like Scalacheck, but also:\n\n1. Offers automatic shrinkage to a minimal or nearly-minimal test case.\n2. Offers direct reproduction of a failing, minimised test case.\n3. Gets out of the way of testing style - doesn't care about whether the tests are pure functional or imperative,\n   doesn't offer a DSL or try to structure your test suite.\n4. Supports Scala and Java as first class citizens.\n\nIn time this broadened to the fuller set of features listed at the start of this readme.\n\n## Example ##\n\nLet's take our sorting implementation above, write some proper parameterised tests and drive them via a `Trials`\ninstance ...\n\n```scala\n\nimport com.sageserpent.americium.Trials.api // Start with the Scala api for `Trials`...\n\n// We're going to sort a list of associations (key-value pairs) by the key...\nval ordering = Ordering.by[(Int, Int), Int](_._1)\n\n// Build up a trials instance for key value pairs by flat-mapping from simpler\n// trials instances for the keys and values...\nval keyValuePairs: Trials[(Int, Int)] = for {\n  key \u003c- api.choose(\n    0 to 100\n  ) // We want to encourage duplicated keys - so a key is always some integer from 0 up to but not including 100.\n  value \u003c-\n    api.integers // A value on the other hand is any integer from right across the permissible range.\n} yield key -\u003e value\n\n// Here's the trials instance we use to drive the tests for sorting...\nval associationLists: Trials[List[(Int, Int)]] =\n  keyValuePairs.lists // This makes a trials of lists out of the simpler trials of key-value pairs.\n\n\"stableSorting\" should \"sort according to the ordering\" in\n  associationLists\n    .filter(\n      _.nonEmpty\n    ) // Filter out the empty case as we can't assert sensibly on it.\n    .withLimit(200) // Only check up to 200 cases inclusive.\n    .supplyTo { nonEmptyAssocationList: List[(Int, Int)] =\u003e\n      // This is a parameterised test, using `nonEmptyAssociationList` as the\n      // test case parameter...\n      val sortedResult = notSoStableSort(nonEmptyAssocationList)(ordering)\n\n      // Using Scalatest assertions here...\n      assert(\n        sortedResult.zip(sortedResult.tail).forall((ordering.lteq _).tupled)\n      )\n    }\n\nit should \"conserve the original elements\" in\n  associationLists.withLimit(200).supplyTo {\n    associationList: List[(Int, Int)] =\u003e\n      val sortedResult = notSoStableSort(associationList)(ordering)\n\n      sortedResult should contain theSameElementsAs associationList\n  }\n\n// Until the bug is fixed, we expect this test to fail...\nit should \"also preserve the original order of the subsequences of elements that are equivalent according to the order\" in\n  associationLists.withLimit(200).supplyTo {\n    associationList: List[(Int, Int)] =\u003e\n      Trials.whenever(\n        associationList.nonEmpty\n      ) // Filter out the empty case as while we can assert on it, the assertion would be trivial.\n      {\n        val sortedResult = notSoStableSort(associationList)(ordering)\n\n        assert(sortedResult.groupBy(_._1) == associationList.groupBy(_._1))\n      }\n  }\n```\n\nRun the tests - the last one will fail with a nicely minimised case:\n\n```org.scalatest.exceptions.TestFailedException: HashMap(46 -\u003e List((46,0), (46,2))) did not equal HashMap(46 -\u003e List((46,2), (46,0)))\nExpected :HashMap(46 -\u003e List((46,2), (46,0)))\nActual   :org.scalatest.exceptions.TestFailedException: HashMap(46 -\u003e List((46,0), (46,2)))\n\nTrial exception with underlying cause:\norg.scalatest.exceptions.TestFailedException: HashMap(46 -\u003e List((46,0), (46,2))) did not equal HashMap(46 -\u003e List((46,2), (46,0)))\nCase:\nList((46,2), (46,0))\nReproduce with recipe:\n[\n.... block of JSON ....\n]\n```\n\nWe also see a JSON recipe for reproduction too further down in the output. We can use this recipe to make a temporary\nbug-reproduction test that focuses solely on the test case causing the problem:\n\n   ```scala\n// Until the bug is fixed, we expect this test to fail...\nit should \"also preserve the original order of the subsequences of elements that are equivalent according to the order - this time with the failure reproduced directly\" ignore\n  associationLists\n    .withRecipe(\n      \"\"\"[\n        |    {\n        |        \"ChoiceOf\" : {\n        |            \"index\" : 1\n        |        }\n        |    },\n        |    {\n        |        \"ChoiceOf\" : {\n        |            \"index\" : 46\n        |        }\n        |    },\n        |    {\n        |        \"FactoryInputOf\" : {\n        |            \"input\" : 0\n        |        }\n        |    },\n        |    {\n        |        \"ChoiceOf\" : {\n        |            \"index\" : 1\n        |        }\n        |    },\n        |    {\n        |        \"ChoiceOf\" : {\n        |            \"index\" : 46\n        |        }\n        |    },\n        |    {\n        |        \"FactoryInputOf\" : {\n        |            \"input\" : 2\n        |        }\n        |    },\n        |    {\n        |        \"ChoiceOf\" : {\n        |            \"index\" : 0\n        |        }\n        |    }\n        |]\"\"\".stripMargin)\n    .supplyTo { associationList: List[(Int, Int)] =\u003e\n      val sortedResult = notSoStableSort(associationList)(ordering)\n\n      assert(sortedResult.groupBy(_._1) == associationList.groupBy(_._1))\n    } \n   ```\n\n## Rhetorical Questions ##\n\n### How did this come about? ###\n\nAs mentioned in the introduction above, working on this issue in the Plutonium\nproject (https://github.com/sageserpent-open/plutonium/issues/57) exposed an infrequent bug via Scalacheck whose failing\ntest cases were frightfully complex and not shrinkable by default. Until that bug can be reproduced via a minimised test\ncase, it won't be fixed.\n\nThe problem is that the test cases have invariants that are constraints on how the test data is built up - simply\nflinging arbitrary values of the right types together can build invalid test cases that cause such tests to fail with\nfalse negatives independently of the system under test. What is needed is something that shrinks a failing test case\nwhile sticking with the logic that enforces the invariant on the test cases being shrunk.\n\nWorking with Scalacheck on\nthis: [ImmutableObjectStorageSpec](https://github.com/sageserpent-open/curium/blob/8455ee0a387c6ab5373283a21f88ab6044d59ee1/src/test/scala/com/sageserpent/plutonium/curium/ImmutableObjectStorageSpec.scala#L227)\nin the Curium project motivated the author to try out some alternatives to Scalacheck; Zio and Hedgehog were explored,\nand they both allow integrated shrinking for free that respects the test case invariants. However they don't quite suit\nthe author's wishlist for parameterised testing in general, so along came Americium.\n\n### The history starts with _\"Start a project for the F# to Scala port of the Test Case Generation library.\"_. Huh? ###\n\nAh - a long time ago there was an F# project that used some utility code that was hived off into a helper assembly - see\nhere: https://github.com/sageserpent-open/NTestCaseBuilder/tree/master/development/solution/SageSerpent.Infrastructure\n\nSome of that code was ported to Scala as a learning exercise and ended up being used by the Plutonium and Curium\nprojects as a shared dumping ground for utilities, including things for testing - see\nhere: https://github.com/sageserpent-open/americium/blob/master/src/main/scala/com/sageserpent/americium/RandomEnrichment.scala\n\nBeing lazy, the author carried on with the ignoble tradition of dumping experimental but useful code into the project,\nthen decided to bury its murky past and re-invent it as a respectable member of society supporting parameterised\ntesting. Now you know its terrible secret...\n\n### Americium? Plutonium? Curium? ###\n\nThe author has a great appreciation of the actinide elements. There is a Neptunium project too, but he changed the name\nof its repository to make its use-case more obvious.\n\n### The introduction mentions Scalacheck Shapeless, do explain... ###\n\nThis the automatic generation of trials for structured types, and is brought to us via the Magnolia library - see it in\naction here:\n[TrialsSpec.scala](https://github.com/sageserpent-open/americium/blob/7219c8982380458397e54be207b6769b991d0ef4/src/test/scala/com/sageserpent/americium/TrialsSpec.scala#L1506)\nand here:\n[TrialsSpec.scala](https://github.com/sageserpent-open/americium/blob/7219c8982380458397e54be207b6769b991d0ef4/src/test/scala-2.13/com/sageserpent/americium/TrialsSpecSpecificToScala2_13.scala#L13)\n\nAsk for a trials of your case class hierarchy types, and it shall be written for you!\n\n### When I use the `.choose` to build a trials instance from an API object, it won't shrink - why? ###\n\nYes, and that is currently by design. When you use the `.choose` method to build a trials instance, you are saying\nthat *you* want to provide the choices and that they are all equally as good - think of the members of an enumeration,\nfor instance, or perhaps some user input choices in a UI. The trials machinery doesn't know anything about the domain\nthese choices come from and won't try to order them according to some ranking of simplicity - they are taken to be\nequally valid.\n\nWhat *does* get shrunk is the complexity of the test cases - so if we have collections, or some kind of recursive\ndefinition, then smaller collections are taken to be simpler, as are cases built with less recursion. Collections shrink\ntowards empty collections.\n\nFurthermore, the streaming factory methods - `.doubles`, `.integers`, `.stream` etc also support shrinking - they have\nan internal parameter that controls the range of the generated values, so as shrinkage proceeds, the values get '\nsmaller' in some sense. For numeric values, that usually means tending towards zero from both positive and negative\nvalues.\n\nThe `.strings` factory method shrinks in the same manner as for collections - the strings get shorter, tending to the\nempty string, although the characters range over the full UTF-16 set.\n\nThis choice isn't written in stone - rather than using `.choose`, use the streaming factory methods that allow custom\nranges, either via overloads of `.integers`, `.longs` and `.characters` or directly in `.stream` using a `CaseFactory`.\nThese also permit some other value to be the one that shrinkage tends to. See here for an\nexample: [TrialsApiTests](https://github.com/sageserpent-open/americium/blob/6fdd3db7f07e398018de80ce8130a5582648a346/src/test/scala/com/sageserpent/americium/java/TrialsApiTests.java#L309)\n.\n\nThat example also shows how strings can be built from the output of `.characters` in Java - use this example\nhere: [TrialsSpec](https://github.com/sageserpent-open/americium/blob/6fdd3db7f07e398018de80ce8130a5582648a346/src/test/scala/com/sageserpent/americium/TrialsSpec.scala#L218)\nif you are writing in Scala. Note that when you do this, you have the ability to shrink your strings based on both\nlength and the character values.\n\n### Are the cases yielded by `.doubles`, `.integers`, `.stream` randomly distributed? ###\n\nYes, and they should span pretty much the whole range of allowed values. As shrinkage kicks in, this range contracts to\nthe 'minimal value' - zero for numeric values, but that can be customised when using `.stream`. See the `CaseFactory`\ninterface if you want to customise the range of allowed values and where the minimal value lies in that range, it\ndoesn't have to sit in the middle.\n\nAs mentioned in the previous section, there are also some convenience overloads of `.integers`, `.longs`\nand `.characters` for this purpose too.\n\nHedgehog supports custom distributions and ranges, and Scalacheck has some heuristics for biasing its otherwise random\ndistributions. You can implement this by supplying your own `CaseFactory` instance that skews the input values, and you\ncan also move the input value for the maximally shrunk case to some favoured value, as shrinkage will home in on it.\n\n### If I write a recursive definition of a trials instance, do I need to protect against infinite recursion with a size parameter? ###\n\nNo, but you do need to stop trivial infinite recursion. Thankfully that is simple, see here:\n[TrialsSpec.scala](https://github.com/sageserpent-open/americium/blob/e69b9fb60cd90796d96ba1126a90f6c1ab2a7a1d/src/test/scala/com/sageserpent/americium/TrialsSpec.scala#L59)\nEither wrap the recursive calls in a following bind in a flatmap, this will cause them to be evaluated lazily, or if you\nneed to lead with a recursive call, wrap it in a call to `.delay`. Both techniques are shown in that example.\n\nActually, I oversimplified - sure, you won't need to stop lazily-evaluated infinite recursion, but it is possible to\nmess up a highly recursive trials instance so that it simply doesn't generate any data, due to what is called the '\ncomplexity limit' kicking in. A potential case has an associated complexity, and if in the process of building up an\nindividual case the complexity exceeds the limit, then this will discard that case from being formulated - this is how\ninfinite recursion is prevented as a nice side benefit. However, one can write recursively formulated trials instances\nthat are 'bushy' - there are several parallel paths of recursion at each step, and this tends to result in complete and\nutter failure to generate anything more than very simple cases. To see how this is worked around, take a look\nhere: [TrialsSpec.scala](https://github.com/sageserpent-open/americium/blob/5ea1b3088adaaa0270a944ee1694950975b2b911/src/test/scala/com/sageserpent/americium/TrialsSpec.scala#L94)\n.\n\n### I can see a reference to Scalacheck in the project dependencies. So is this just a sham? ###\n\nUm ... you mean this one\nhere:  [TrialsLaws](https://github.com/sageserpent-open/americium/blob/afe7fca4215bfa00879b553aa7805bb5f8cf2d64/src/test/scala/com/sageserpent/americium/TrialsLaws.scala#L32)\n?\n\nWell, the Cats laws testing framework is used to prove that `Trials` is a decent, law-abiding monadic type, and that\nframework plugs into Scalacheck, so yes, there is a *test* dependency.\n\nThe author is truly embarrassed by this, but in his defence, notes that if Cats laws testing could be plugged\ninto `Trials`, we would have recursively tested code. Not impossible to do, but requires a careful bootstrap approach to\navoid testing a broken SUT with itself.\n\nAn integration of `Trials` with Cats, or more generally with Scalacheck properties would be great though. Plenty of folk\nlike Scalacheck's properties, so while it's not to the author's taste, why exclude them?\n\n### I want my test to work with multiple test parameters. ###\n\nRight, you mean perhaps that you want to preconfigure an SUT as one test parameter and then have a test plan implemented\nas a command sequence passed on via a second test parameter, or something like that? Maybe you have some stubs that\ninteract with the SUT that you want to preconfigure and pass in via their own test parameters?\n\nFear not - build up as many trials instances as you like for the bits and pieces you want to pass into the test, then\njoin them together with the `.and` method. You get back an object that you can call `.withLimit(...).supplyTo`\nor `.withRecipe(...).supplyTo` as usual, only this time the test passed to `supplyTo` takes multiple parameters, or\nalternatively tuples of parameters - your choice.\n\nIt's easy:\n[Java example](https://github.com/sageserpent-open/americium/blob/afe7fca4215bfa00879b553aa7805bb5f8cf2d64/src/test/scala/com/sageserpent/americium/java/TrialsApiTests.java#L240)\n,\n[Scala example](https://github.com/sageserpent-open/americium/blob/afe7fca4215bfa00879b553aa7805bb5f8cf2d64/src/test/scala/com/sageserpent/americium/RichSeqSpec.scala#L50)\n.\n\n### Where are the controls? ###\n\nNot down the sofa nestling betwixt the cushions - instead, they are to found between `Trials` and `.supplyTo` and are\ncalled `.withLimit` (two overloads) and `.withLimits` (also two overloads).\n\nThe drill is to build your `Trials` instance to produce the right type of cases with any invariants you want, then to\ncall one of `.withLimit(s)` on it, thus configuring how the trials will supply cases to a following call of `.supplyTo`.\n\nSo the pattern is:\n\n`\u003ctrials\u003e.withLimit(s)(\u003cconfiguration\u003e).supplyTo(\u003ctest consumer\u003e)`\n\nFor the simplest approach,\nuse [`.withLimit`](https://github.com/sageserpent-open/americium/blob/9b78c966f38773af3214adab524374af89cdd14b/src/main/scala/com/sageserpent/americium/java/TrialsScaffolding.java#L28)\nand pass in the maximum number of cases you would like to have supplied to your test consumer.\n\nIn Scala, full bells and whistles is provided\nby [`.withLimits`](https://github.com/sageserpent-open/americium/blob/9b78c966f38773af3214adab524374af89cdd14b/src/main/scala/com/sageserpent/americium/TrialsScaffolding.scala#L91)\n.\n\nThis allows a maximum number of cases, the maximum complexity, the maximum number of shrinkage attempts and a 'shrinkage\nstop' to be configured. Other than the mandatory maximum number of cases, all other items are optional.\n\nIn Java, the equivalent is provided by a combination\nof [`.withLimits`](https://github.com/sageserpent-open/americium/blob/9b78c966f38773af3214adab524374af89cdd14b/src/main/scala/com/sageserpent/americium/java/TrialsScaffolding.java#L141)\nand [`OptionalLimits`](https://github.com/sageserpent-open/americium/blob/9b78c966f38773af3214adab524374af89cdd14b/src/main/scala/com/sageserpent/americium/java/TrialsScaffolding.java#L83)\n.\n\nSetting the maximum number of shrinkage attempts to zero disables shrinkage altogether - so the original failing case is\nyielded.\n\nThe shrinkage stop is a way for the user to control shrinkage externally via a callback style interface. Essentially\na [`ShrinkageStop`](https://github.com/sageserpent-open/americium/blob/9b78c966f38773af3214adab524374af89cdd14b/src/main/scala/com/sageserpent/americium/java/TrialsScaffolding.java#L65)\nis a factory for a stateful predicate that you supply that could, say:\n\n1. Monitor the number of invocations - thus counting the number of successful shrinkages.\n2. Check heap usage.\n3. Check against a timeout since the start of shrinkage.\n4. Check the quality of the best shrunk case so far.\n\nWhen it returns true, the shrinkage process is terminated early with the best shrunk case seen so far, regardless of\nwhether the maximum number of shrinkage attempts has been reached or not. In fact, there is a difference between\nconfiguring the maximum number of shrinkage attempts and counting the shrinkages - the former includes a panic mode\nwhere the shrinkage mechanism has not yet managed to shrink further on a previous failing case, but is still retrying,\nwhereas the shrinkage stop predicate is only invoked with freshly shrunk cases where progress has been made.\n\n### How do I directly run a minimised test failure without modifying or duplicating my failing test? ###\n\nAh yes, `.withRecipe` is all well and good - it takes you straight to the minimised test failure, but it means you\neither have to temporarily modify your existing test, or duplicate it, or add some ugly conditional path to choose\neither `.withLimits` or `.withRecipe`. This isn't a problem if you like to preserve the minimised test failures as tests\nin their own right, say for bug reproduction work, but is annoying most of the time.\n\nFortunately, there is a way to force your existing test that uses `.withLimit` / `.withLimits` to focus on just the\nminimised test failure - use a _recipe hash_.\n\nWhenever the `Trials` machinery finds a minimised test failure, it stores a mapping between a hash of the recipe JSON -\nthe recipe hash - and the recipe JSON itself in a database located in the Java temporary directory location.\n\nThis database usually has a default name, but if you want to override this, set the Java property `trials.runDatabase`\naccordingly.\n\nAnyway, the `TrialsException` thrown when `Trials` decides it has found the minimised test failure contains both the\nfull recipe JSON *and* the recipe hash - so make a note of the recipe hash.\n\nYou can then re-execute the same test without modification, setting the Java property `trials.recipeHash` to be the\nrecipe hash you just noted. This will tell the `Trials` machinery to go straight to the minimised test failure without\ngenerating any other cases.\n\nOnce you've debugged and fixed the failure, remove the property setting and the test will go back to the usual mode of\noperation. Easy! This also works with the JUnit5 integration too.\n\n### Suppose a test wants to reject a test case based on the test's own logic?  ###\n\nPresumably you've examined the `Trials.filter` method and found it wanting - maybe there is something about the validity\nof the test case that can only be determined by the act of testing itself?\n\nAn example would be where the system under test parses some test case text according to a mysterious format that only it\ntruly understands - so there is an element of chance in the test itself as to whether the test case is valid or not -\nthe only way to weed out irrelevant test cases is to try the parse and detect any failure prior to proceeding with the\nrest of the test. Of course, one could argue that maybe the parsing stage itself should be moved into the construction\nof the `Trials` instance, but there might be construction dependencies of the system under test that mean it can only be\nbuilt within the context of a running test.\n\nAnother example might be a situation where the test case is a composite 'test-plan' made of commands that are\ninterpreted in the test to set up and drive some system under test that has mutable state - you can't do this on the fly\nin a `Trials` instance as that is deliberately stateless, as are the test cases it yields. Instead, the test plan\ncaptures the intent to run the commands later on within the test, so the test plan itself remains stateless. This works\nnicely, but can lead to a situation where the commands can push the system under test into performing an illegal\noperation based on whatever state it is currently in at some point in the executing test plan. Rather than\nsecond-guessing what the test plan might do and filtering based on that insight, a simpler approach is again to just try\nit out and intercept the cases where it causes the system under test to reject a command.\n\nStill another example might be that the test may adapt its behaviour depending on resource constraints, say the amount\nof memory available - so the decision as to whether or not a test case is in scope may only be made by the test.\n\nThis can be done conveniently using `Trials.whenever`, which acts as a block construct that guards some inner test code\nwith a precondition. If the precondition isn't met, the inner test code isn't executed and the `Trials` machinery is\ninformed that the offending test case should be considered as being filtered out.\n\nUnlike explicit filtration via `Trials.filter`, this 'inlined filtration' is applied after the fact, and strictly\nspeaking doesn't even have to examine the test case to reject it - because the test case is supplied to the test code\nthat calls `Trials.whenever`, the case is implied when the guard precondition doesn't hold, whatever the reason.\n\nThe `Trials` machinery keep tabs on inlined filtration, and will make the same guarantees as to coverage of test cases\nthat it would make if an explicit filtration is used.\n\nHere is\na [Java example](https://github.com/sageserpent-open/americium/blob/8f0582c3654d0c4000e4311c9cc89be7372f8864/src/test/scala/com/sageserpent/americium/java/TrialsApiTests.java#L202)\nand here is\na [Scala example](https://github.com/sageserpent-open/americium/blob/03128c4ae691114294eba6583f42ff5e587fb047/src/test/scala/com/sageserpent/americium/SortingExample.scala#L67)\n.\n\n### How does the JUnit5 integration work? ###\n\nThe core API of `Trials` doesn't try to force a testing style over and above supplying test cases to a lambda that is\nyour test code. However, if you've used the standard `@ParameterizedTest` annotation supplied by JUnit5, then you might\nwant to stick with that style of setting out your tests.\n\nIf so, you're in luck as there is a similar annotation: `@TrialsTest`. This replaces the combination\nof `@ParameterizedTest` and whichever of `@ValueSource` / `@MethodSource` / etc that you would have used - the idea is\nto place the `@TrialsTest` annotation on your parameterised test method instead.\n\nThe connection between the test method parameters and `Trials` is made by the `trials` attribute in the annotation -\nthis lists the names of fields of type `Trials` that you define in your JUnit5 test suite; these may be static or\nnon-static according to taste. Currently, each trials field is mapped one to one with a parameter of the annotated test\nmethod.\n\n(Remember, if you use non-static fields for your `Trials`, then you will have to annotate the test class\nwith `@TestInstance(TestInstance.Lifecycle.PER_CLASS)`, analogously to how `@MethodSource` picks up non-static factory\nmethods)\n\nAdditional attributes in the annotation configure the test case generation analogously to calls to `.withLimits`. Take a\nlook\nhere: [JUnit5 integration](https://github.com/sageserpent-open/americium/blob/159e7203b957dff7517aaa5aa09f00b38be54bdb/src/test/scala/com/sageserpent/americium/java/DemonstrateJUnitIntegration.java#L59)\n, it's pretty straightforward to set up.\n\nAs with `@ParameterizedTest`, test templates are used, thus the use of setup and teardown methods via `@BeforeEach`\nand `@AfterEach` are supported for each test template.\n\nThe test templates are guided by shrinkage, so as soon as an individual test template fails, subsequent test templates\nwill start to shrink down to a minimised test template; the overall JUnit5 test run will report the minimised test case\nalong with details as to how to reproduce it directly.\n\nIf your IDE supports clicking on a test template run to re-execute it, then this is also supported, in so far as the\ntest template is *deterministically* generated - this is down to how JUnit5 handles test templates. What this means is\nthat all test templates in a completely successful overall run can be directly re-executed this way individually, and\nall test templates that lead up to and including the first failure can also be directly executed this way too.\n\nTest templates created by shrinkage aren't deterministically generated, as the shrinkage needs the context of previous\ntest templates in the overall run, so these are labelled as such to avoid confusion.\n\nIf you want to directly execute a minimised failing case, use its recipe hash - this works with the JUnit5 integration\njust as well as for the default lean-and-mean style of testing.\n\n### Why is there a file 'IntelliJCodeStyle.xml' in the project? ###\n\nThe author has a real problem with pull requests that consist of a wholesale reformatting of sources that also harbour\nsome change in functionality within the reformatting noise. If you want to work on this and contribute back, please use\nthe automatic reformatting tools in IntelliJ (or similar) to stick to the existing style. Bear in mind that the chosen\nformatting style isn't the author's favourite, but the simplicity of using automatic formatting on commit by far\noutweighs the meagre joys of having the code look *just so*. Just wait until you do a merge...\n\nThe author uses IntelliJ's built-in Java formatter and the integration with Scalafmt.\n\nIf you know of a better way of sharing reformatting settings / tooling, raise an issue.\n\n### Where is the Scaladoc? ###\n\nSorry, while the API has been in development the author has concentrated on keeping the Javadoc up to date. Hopefully\nthe Java and Scala APIs are similar enough to translate what is in the Javadoc that isn't captured in the Scala method\nnames and signatures. Someday, but until then pull requests are welcome...\n\n### The competition? ###\n\nIn Scala, there is at least:\n\n1. Scalacheck\n2. ZioTest\n3. Hedgehog\n4. Scalaprops\n5. Nyaya\n\nIn Java, there is at least:\n\n1. Jqwik\n2. QuickTheories\n3. JUnit-QuickCheck\n4. VavrTest\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsageserpent-open%2Famericium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsageserpent-open%2Famericium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsageserpent-open%2Famericium/lists"}