├── project ├── build.properties └── plugins.sbt ├── version.sbt ├── .gitignore ├── CHANGELOG.md ├── core └── src │ ├── test │ └── scala │ │ └── cats │ │ └── tests │ │ └── BasicTest.scala │ └── main │ └── scala │ └── cats │ └── tests │ ├── ValidatedValues.scala │ ├── CatsEquality.scala │ └── CatsSuite.scala ├── .scalafmt.conf ├── docs └── docs │ └── index.md ├── README.md ├── LICENSE └── .github └── workflows ├── ci.yml └── clean.yml /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.5.5 2 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / version := "2.1.6-SNAPSHOT" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | # vim 4 | *.sw? 5 | 6 | # Ignore [ce]tags files 7 | tags 8 | 9 | .bloop 10 | .metals -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # changelog 2 | 3 | This file summarizes **notable** changes for each release, but does not describe internal changes unless they are particularly exciting. This change log is ordered chronologically, so each release contains all changes described below it. 4 | 5 | ---- 6 | 7 | ## Unreleased Changes -------------------------------------------------------------------------------- /core/src/test/scala/cats/tests/BasicTest.scala: -------------------------------------------------------------------------------- 1 | package cats.tests 2 | 3 | import cats.data.Validated 4 | import cats.kernel.laws.discipline.EqTests 5 | import org.scalatest.exceptions.TestFailedException 6 | 7 | class BasicTest extends CatsSuite with ValidatedValues { 8 | checkAll("Int", EqTests[Int].eqv) 9 | 10 | test("Equals Checks"){ 11 | val s = "" 12 | // Using === 13 | s === s 14 | } 15 | 16 | test("Validated Values") { 17 | Validated.valid("foo").value shouldEqual "foo" 18 | assertThrows[TestFailedException](Validated.invalid("boom").value) 19 | } 20 | } -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | # tune this file as appropriate to your style! see: https://olafurpg.github.io/scalafmt/#Configuration 2 | 3 | maxColumn = 100 4 | 5 | continuationIndent.callSite = 2 6 | 7 | newlines { 8 | sometimesBeforeColonInMethodReturnType = false 9 | } 10 | 11 | align { 12 | arrowEnumeratorGenerator = false 13 | ifWhileOpenParen = false 14 | openParenCallSite = false 15 | openParenDefnSite = false 16 | 17 | tokens = ["%", "%%"] 18 | } 19 | 20 | docstrings = JavaDoc 21 | 22 | rewrite { 23 | rules = [SortImports, RedundantBraces] 24 | redundantBraces.maxLines = 1 25 | } 26 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.1") 2 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") 3 | addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.20") 4 | addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") 5 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.7") 6 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.9.2") 7 | addSbtPlugin("com.47deg" % "sbt-microsites" % "1.3.4") 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.3") 9 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0") 10 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.6.0") 11 | addSbtPlugin("com.codecommit" % "sbt-github-actions" % "0.12.0") 12 | -------------------------------------------------------------------------------- /docs/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | --- 5 | 6 | # cats-testkit-scalatest - Cats Testkit for Scalatest [![Build Status](https://travis-ci.org/typelevel/cats-testkit-scalatest.svg?branch=master)](https://travis-ci.org/typelevel/cats-testkit-scalatest) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.typelevel/cats-testkit-scalatest_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/cats-testkit-scalatest_2.12) 7 | 8 | ## Quick Start 9 | 10 | To use cats-testkit-scalatest in an existing SBT project with Scala 2.11 or a later version, add the following dependencies to your 11 | `build.sbt` depending on your needs: 12 | 13 | ```scala 14 | libraryDependencies ++= Seq( 15 | "org.typelevel" %% "cats-testkit-scalatest" % "" 16 | ) 17 | ``` -------------------------------------------------------------------------------- /core/src/main/scala/cats/tests/ValidatedValues.scala: -------------------------------------------------------------------------------- 1 | package cats.tests 2 | 3 | import cats.data.Validated 4 | import cats.data.Validated.{Invalid, Valid} 5 | import org.scalactic.source.Position 6 | import org.scalatest.exceptions.TestFailedException 7 | 8 | trait ValidatedValues { 9 | implicit class ValueForValidated[E, A](result: Validated[E, A])(implicit pos: Position) { 10 | 11 | /** 12 | * @return the value of this Validated if it is Valid, else fail the test 13 | */ 14 | def value: A = result match { 15 | case Invalid(e: Throwable) => throw new TestFailedException(_ => Some("Validation failed"), Some(e), pos) 16 | case Invalid(e) => throw new TestFailedException(_ => Some(s"Validation failed with $e"), None, pos) 17 | case Valid(a) => a 18 | } 19 | } 20 | } 21 | object ValidatedValues extends ValidatedValues 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cats-testkit-scalatest - Cats Testkit for Scalatest ![Continuous Integration](https://github.com/typelevel/cats-testkit-scalatest/workflows/Continuous%20Integration/badge.svg) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.typelevel/cats-testkit-scalatest_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/cats-testkit-scalatest_2.12) ![Code of Consuct](https://img.shields.io/badge/Code%20of%20Conduct-Scala-blue.svg) 2 | 3 | ## [Head on over to the microsite](https://typelevel.github.io/cats-testkit-scalatest) 4 | 5 | ## Quick Start 6 | 7 | To use cats-testkit-scalatest in an existing SBT project with Scala 2.11 or a later version, add the following dependencies to your 8 | `build.sbt` depending on your needs: 9 | 10 | ```scala 11 | libraryDependencies ++= Seq( 12 | "org.typelevel" %% "cats-testkit-scalatest" % "" 13 | ) 14 | ``` 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Typelevel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /core/src/main/scala/cats/tests/CatsEquality.scala: -------------------------------------------------------------------------------- 1 | package cats 2 | package tests 3 | 4 | import org.scalactic._ 5 | import TripleEqualsSupport.AToBEquivalenceConstraint 6 | import TripleEqualsSupport.BToAEquivalenceConstraint 7 | 8 | // The code in this file was taken and only slightly modified from 9 | // https://github.com/bvenners/equality-integration-demo 10 | // Thanks for the great examples, Bill! 11 | 12 | final class CatsEquivalence[T](T: Eq[T]) extends Equivalence[T] { 13 | def areEquivalent(a: T, b: T): Boolean = T.eqv(a, b) 14 | } 15 | 16 | trait LowPriorityStrictCatsConstraints extends TripleEquals { 17 | implicit def lowPriorityCatsCanEqual[A, B](implicit B: Eq[B], ev: A <:< B): CanEqual[A, B] = 18 | new AToBEquivalenceConstraint[A, B](new CatsEquivalence(B), ev) 19 | } 20 | 21 | trait StrictCatsEquality extends LowPriorityStrictCatsConstraints { 22 | override def convertToEqualizer[T](left: T): Equalizer[T] = super.convertToEqualizer[T](left) 23 | implicit override def convertToCheckingEqualizer[T](left: T): CheckingEqualizer[T] = new CheckingEqualizer(left) 24 | override def unconstrainedEquality[A, B](implicit equalityOfA: Equality[A]): CanEqual[A, B] = 25 | super.unconstrainedEquality[A, B] 26 | implicit def catsCanEqual[A, B](implicit A: Eq[A], ev: B <:< A): CanEqual[A, B] = 27 | new BToAEquivalenceConstraint[A, B](new CatsEquivalence(A), ev) 28 | } 29 | 30 | object StrictCatsEquality extends StrictCatsEquality -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Continuous Integration 9 | 10 | on: 11 | pull_request: 12 | branches: ['**'] 13 | push: 14 | branches: ['**'] 15 | 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | jobs: 20 | build: 21 | name: Build and Test 22 | strategy: 23 | matrix: 24 | os: [ubuntu-latest] 25 | scala: [2.12.14, 2.13.6, 3.0.1] 26 | java: [adopt@1.8] 27 | runs-on: ${{ matrix.os }} 28 | steps: 29 | - name: Checkout current branch (full) 30 | uses: actions/checkout@v2 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Setup Java and Scala 35 | uses: olafurpg/setup-scala@v12 36 | with: 37 | java-version: ${{ matrix.java }} 38 | 39 | - name: Cache sbt 40 | uses: actions/cache@v2 41 | with: 42 | path: | 43 | ~/.sbt 44 | ~/.ivy2/cache 45 | ~/.coursier/cache/v1 46 | ~/.cache/coursier/v1 47 | ~/AppData/Local/Coursier/Cache/v1 48 | ~/Library/Caches/Coursier/v1 49 | key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} 50 | 51 | - if: matrix.scala == '2.12.14' 52 | uses: ruby/setup-ruby@v1 53 | with: 54 | ruby-version: 2.6 55 | 56 | - if: matrix.scala == '2.12.14' 57 | run: gem install sass 58 | 59 | - if: matrix.scala == '2.12.14' 60 | run: gem install jekyll -v 3.2.1 61 | 62 | - name: Check that workflows are up to date 63 | run: sbt --client '++${{ matrix.scala }}; githubWorkflowCheck' 64 | 65 | - name: Validate unit tests and binary compatibility 66 | run: sbt --client '++${{ matrix.scala }}; test; mimaReportBinaryIssues' 67 | 68 | - if: matrix.scala == '2.12.14' 69 | run: sbt --client '++${{ matrix.scala }}; docs/makeMicrosite' -------------------------------------------------------------------------------- /.github/workflows/clean.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Clean 9 | 10 | on: push 11 | 12 | jobs: 13 | delete-artifacts: 14 | name: Delete Artifacts 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - name: Delete artifacts 20 | run: | 21 | # Customize those three lines with your repository and credentials: 22 | REPO=${GITHUB_API_URL}/repos/${{ github.repository }} 23 | 24 | # A shortcut to call GitHub API. 25 | ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } 26 | 27 | # A temporary file which receives HTTP response headers. 28 | TMPFILE=/tmp/tmp.$$ 29 | 30 | # An associative array, key: artifact name, value: number of artifacts of that name. 31 | declare -A ARTCOUNT 32 | 33 | # Process all artifacts on this repository, loop on returned "pages". 34 | URL=$REPO/actions/artifacts 35 | while [[ -n "$URL" ]]; do 36 | 37 | # Get current page, get response headers in a temporary file. 38 | JSON=$(ghapi --dump-header $TMPFILE "$URL") 39 | 40 | # Get URL of next page. Will be empty if we are at the last page. 41 | URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') 42 | rm -f $TMPFILE 43 | 44 | # Number of artifacts on this page: 45 | COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) 46 | 47 | # Loop on all artifacts on this page. 48 | for ((i=0; $i < $COUNT; i++)); do 49 | 50 | # Get name of artifact and count instances of this name. 51 | name=$(jq <<<$JSON -r ".artifacts[$i].name?") 52 | ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) 53 | 54 | id=$(jq <<<$JSON -r ".artifacts[$i].id?") 55 | size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) 56 | printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size 57 | ghapi -X DELETE $REPO/actions/artifacts/$id 58 | done 59 | done -------------------------------------------------------------------------------- /core/src/main/scala/cats/tests/CatsSuite.scala: -------------------------------------------------------------------------------- 1 | package cats 2 | package tests 3 | 4 | import cats.instances._ 5 | import cats.platform.Platform 6 | import cats.syntax._ 7 | import org.scalactic.anyvals.{PosInt, PosZDouble, PosZInt} 8 | import org.scalatest.funsuite.AnyFunSuiteLike 9 | import org.scalatest.matchers.should.Matchers 10 | import org.scalatest.prop.Configuration 11 | import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks 12 | import org.typelevel.discipline.scalatest.FunSuiteDiscipline 13 | 14 | trait TestSettings extends Configuration with Matchers { 15 | 16 | lazy val checkConfiguration: PropertyCheckConfiguration = 17 | PropertyCheckConfiguration( 18 | minSuccessful = if (Platform.isJvm) PosInt(50) else PosInt(5), 19 | maxDiscardedFactor = if (Platform.isJvm) PosZDouble(5.0) else PosZDouble(50.0), 20 | minSize = PosZInt(0), 21 | sizeRange = if (Platform.isJvm) PosZInt(10) else PosZInt(5), 22 | workers = if (Platform.isJvm) PosInt(2) else PosInt(1) 23 | ) 24 | 25 | lazy val slowCheckConfiguration: PropertyCheckConfiguration = 26 | if (Platform.isJvm) checkConfiguration 27 | else PropertyCheckConfiguration(minSuccessful = 1, sizeRange = 1) 28 | } 29 | 30 | /** 31 | * An opinionated stack of traits to improve consistency and reduce 32 | * boilerplate in Cats tests. 33 | */ 34 | trait CatsSuite 35 | extends AnyFunSuiteLike 36 | with Matchers 37 | with ScalaCheckDrivenPropertyChecks 38 | with FunSuiteDiscipline 39 | with TestSettings 40 | with AllInstances 41 | with AllInstancesBinCompat0 42 | with AllInstancesBinCompat1 43 | with AllInstancesBinCompat2 44 | with AllInstancesBinCompat3 45 | with AllInstancesBinCompat4 46 | with AllInstancesBinCompat5 47 | with AllSyntax 48 | with AllSyntaxBinCompat0 49 | with AllSyntaxBinCompat1 50 | with AllSyntaxBinCompat2 51 | with AllSyntaxBinCompat3 52 | with AllSyntaxBinCompat4 53 | with AllSyntaxBinCompat5 54 | with StrictCatsEquality { 55 | 56 | implicit override val generatorDrivenConfig: PropertyCheckConfiguration = 57 | checkConfiguration 58 | 59 | // disable Eq syntax (by making `catsSyntaxEq` not implicit), since it collides 60 | // with scalactic's equality 61 | override def catsSyntaxEq[A: Eq](a: A): EqOps[A] = new EqOps[A](a) 62 | 63 | def even(i: Int): Boolean = i % 2 == 0 64 | 65 | val evenPf: PartialFunction[Int, Int] = { case i if even(i) => i } 66 | } 67 | 68 | trait SlowCatsSuite extends CatsSuite { 69 | implicit override val generatorDrivenConfig: PropertyCheckConfiguration = 70 | slowCheckConfiguration 71 | } 72 | --------------------------------------------------------------------------------