├── .github
└── workflows
│ ├── ci.yml
│ └── clean.yml
├── .gitignore
├── .scalafmt.conf
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.sbt
├── docs
└── index.md
├── project
├── build.properties
└── plugins.sbt
└── scalatest
└── src
├── main
└── scala
│ └── org
│ └── typelevel
│ └── discipline
│ └── scalatest
│ └── Discipline.scala
└── test
└── scala
└── scalatest
└── org
└── typelevel
└── discipline
└── scalatest
├── DisciplineTest.scala
└── laws.scala
/.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: ['**', '!update/**', '!pr/**']
13 | push:
14 | branches: ['**', '!update/**', '!pr/**']
15 | tags: [v*]
16 |
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 |
20 |
21 | concurrency:
22 | group: ${{ github.workflow }} @ ${{ github.ref }}
23 | cancel-in-progress: true
24 |
25 | jobs:
26 | build:
27 | name: Test
28 | strategy:
29 | matrix:
30 | os: [ubuntu-22.04]
31 | scala: [2.12, 3, 2.13]
32 | java: [temurin@8]
33 | project: [rootJS, rootJVM, rootNative]
34 | runs-on: ${{ matrix.os }}
35 | timeout-minutes: 60
36 | steps:
37 | - name: Checkout current branch (full)
38 | uses: actions/checkout@v4
39 | with:
40 | fetch-depth: 0
41 |
42 | - name: Setup sbt
43 | uses: sbt/setup-sbt@v1
44 |
45 | - name: Setup Java (temurin@8)
46 | id: setup-java-temurin-8
47 | if: matrix.java == 'temurin@8'
48 | uses: actions/setup-java@v4
49 | with:
50 | distribution: temurin
51 | java-version: 8
52 | cache: sbt
53 |
54 | - name: sbt update
55 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
56 | run: sbt +update
57 |
58 | - name: Check that workflows are up to date
59 | run: sbt githubWorkflowCheck
60 |
61 | - name: Check headers and formatting
62 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'
63 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck
64 |
65 | - name: scalaJSLink
66 | if: matrix.project == 'rootJS'
67 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/scalaJSLinkerResult
68 |
69 | - name: nativeLink
70 | if: matrix.project == 'rootNative'
71 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/nativeLink
72 |
73 | - name: Test
74 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test
75 |
76 | - name: Check binary compatibility
77 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'
78 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues
79 |
80 | - name: Generate API documentation
81 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04'
82 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc
83 |
84 | - name: Make target directories
85 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
86 | run: mkdir -p scalatest/.native/target scalatest/.js/target scalatest/.jvm/target project/target
87 |
88 | - name: Compress target directories
89 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
90 | run: tar cf targets.tar scalatest/.native/target scalatest/.js/target scalatest/.jvm/target project/target
91 |
92 | - name: Upload target directories
93 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
94 | uses: actions/upload-artifact@v4
95 | with:
96 | name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }}
97 | path: targets.tar
98 |
99 | publish:
100 | name: Publish Artifacts
101 | needs: [build]
102 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
103 | strategy:
104 | matrix:
105 | os: [ubuntu-22.04]
106 | java: [temurin@8]
107 | runs-on: ${{ matrix.os }}
108 | steps:
109 | - name: Checkout current branch (full)
110 | uses: actions/checkout@v4
111 | with:
112 | fetch-depth: 0
113 |
114 | - name: Setup sbt
115 | uses: sbt/setup-sbt@v1
116 |
117 | - name: Setup Java (temurin@8)
118 | id: setup-java-temurin-8
119 | if: matrix.java == 'temurin@8'
120 | uses: actions/setup-java@v4
121 | with:
122 | distribution: temurin
123 | java-version: 8
124 | cache: sbt
125 |
126 | - name: sbt update
127 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
128 | run: sbt +update
129 |
130 | - name: Download target directories (2.12, rootJS)
131 | uses: actions/download-artifact@v4
132 | with:
133 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJS
134 |
135 | - name: Inflate target directories (2.12, rootJS)
136 | run: |
137 | tar xf targets.tar
138 | rm targets.tar
139 |
140 | - name: Download target directories (2.12, rootJVM)
141 | uses: actions/download-artifact@v4
142 | with:
143 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootJVM
144 |
145 | - name: Inflate target directories (2.12, rootJVM)
146 | run: |
147 | tar xf targets.tar
148 | rm targets.tar
149 |
150 | - name: Download target directories (2.12, rootNative)
151 | uses: actions/download-artifact@v4
152 | with:
153 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-rootNative
154 |
155 | - name: Inflate target directories (2.12, rootNative)
156 | run: |
157 | tar xf targets.tar
158 | rm targets.tar
159 |
160 | - name: Download target directories (3, rootJS)
161 | uses: actions/download-artifact@v4
162 | with:
163 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJS
164 |
165 | - name: Inflate target directories (3, rootJS)
166 | run: |
167 | tar xf targets.tar
168 | rm targets.tar
169 |
170 | - name: Download target directories (3, rootJVM)
171 | uses: actions/download-artifact@v4
172 | with:
173 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJVM
174 |
175 | - name: Inflate target directories (3, rootJVM)
176 | run: |
177 | tar xf targets.tar
178 | rm targets.tar
179 |
180 | - name: Download target directories (3, rootNative)
181 | uses: actions/download-artifact@v4
182 | with:
183 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootNative
184 |
185 | - name: Inflate target directories (3, rootNative)
186 | run: |
187 | tar xf targets.tar
188 | rm targets.tar
189 |
190 | - name: Download target directories (2.13, rootJS)
191 | uses: actions/download-artifact@v4
192 | with:
193 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJS
194 |
195 | - name: Inflate target directories (2.13, rootJS)
196 | run: |
197 | tar xf targets.tar
198 | rm targets.tar
199 |
200 | - name: Download target directories (2.13, rootJVM)
201 | uses: actions/download-artifact@v4
202 | with:
203 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootJVM
204 |
205 | - name: Inflate target directories (2.13, rootJVM)
206 | run: |
207 | tar xf targets.tar
208 | rm targets.tar
209 |
210 | - name: Download target directories (2.13, rootNative)
211 | uses: actions/download-artifact@v4
212 | with:
213 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-rootNative
214 |
215 | - name: Inflate target directories (2.13, rootNative)
216 | run: |
217 | tar xf targets.tar
218 | rm targets.tar
219 |
220 | - name: Import signing key
221 | if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == ''
222 | env:
223 | PGP_SECRET: ${{ secrets.PGP_SECRET }}
224 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
225 | run: echo $PGP_SECRET | base64 -d -i - | gpg --import
226 |
227 | - name: Import signing key and strip passphrase
228 | if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != ''
229 | env:
230 | PGP_SECRET: ${{ secrets.PGP_SECRET }}
231 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
232 | run: |
233 | echo "$PGP_SECRET" | base64 -d -i - > /tmp/signing-key.gpg
234 | echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg
235 | (echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1)
236 |
237 | - name: Publish
238 | env:
239 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
240 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
241 | SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }}
242 | run: sbt tlCiRelease
243 |
244 | dependency-submission:
245 | name: Submit Dependencies
246 | if: github.event.repository.fork == false && github.event_name != 'pull_request'
247 | strategy:
248 | matrix:
249 | os: [ubuntu-22.04]
250 | java: [temurin@8]
251 | runs-on: ${{ matrix.os }}
252 | steps:
253 | - name: Checkout current branch (full)
254 | uses: actions/checkout@v4
255 | with:
256 | fetch-depth: 0
257 |
258 | - name: Setup sbt
259 | uses: sbt/setup-sbt@v1
260 |
261 | - name: Setup Java (temurin@8)
262 | id: setup-java-temurin-8
263 | if: matrix.java == 'temurin@8'
264 | uses: actions/setup-java@v4
265 | with:
266 | distribution: temurin
267 | java-version: 8
268 | cache: sbt
269 |
270 | - name: sbt update
271 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
272 | run: sbt +update
273 |
274 | - name: Submit Dependencies
275 | uses: scalacenter/sbt-dependency-submission@v2
276 | with:
277 | modules-ignore: rootjs_2.12 rootjs_3 rootjs_2.13 docs_2.12 docs_3 docs_2.13 rootjvm_2.12 rootjvm_3 rootjvm_2.13 rootnative_2.12 rootnative_3 rootnative_2.13
278 | configs-ignore: test scala-tool scala-doc-tool test-internal
279 |
280 | site:
281 | name: Generate Site
282 | strategy:
283 | matrix:
284 | os: [ubuntu-22.04]
285 | java: [temurin@11]
286 | runs-on: ${{ matrix.os }}
287 | steps:
288 | - name: Checkout current branch (full)
289 | uses: actions/checkout@v4
290 | with:
291 | fetch-depth: 0
292 |
293 | - name: Setup sbt
294 | uses: sbt/setup-sbt@v1
295 |
296 | - name: Setup Java (temurin@8)
297 | id: setup-java-temurin-8
298 | if: matrix.java == 'temurin@8'
299 | uses: actions/setup-java@v4
300 | with:
301 | distribution: temurin
302 | java-version: 8
303 | cache: sbt
304 |
305 | - name: sbt update
306 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false'
307 | run: sbt +update
308 |
309 | - name: Setup Java (temurin@11)
310 | id: setup-java-temurin-11
311 | if: matrix.java == 'temurin@11'
312 | uses: actions/setup-java@v4
313 | with:
314 | distribution: temurin
315 | java-version: 11
316 | cache: sbt
317 |
318 | - name: sbt update
319 | if: matrix.java == 'temurin@11' && steps.setup-java-temurin-11.outputs.cache-hit == 'false'
320 | run: sbt +update
321 |
322 | - name: Generate site
323 | run: sbt docs/tlSite
324 |
325 | - name: Publish site
326 | if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
327 | uses: peaceiris/actions-gh-pages@v4.0.0
328 | with:
329 | github_token: ${{ secrets.GITHUB_TOKEN }}
330 | publish_dir: site/target/docs/site
331 | keep_files: true
332 |
--------------------------------------------------------------------------------
/.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/.*/' -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
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | .idea/
3 | # vim
4 | *.sw?
5 |
6 | # Ignore [ce]tags files
7 | tags
8 |
9 | .bloop
10 | .metals
11 | .bsp/
12 | .vscode
13 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | version=3.9.4
2 | runner.dialect = scala213source3
3 | align.openParenCallSite = true
4 | align.openParenDefnSite = true
5 | maxColumn = 120
6 | continuationIndent.defnSite = 2
7 | assumeStandardLibraryStripMargin = true
8 | danglingParentheses.preset = true
9 | rewrite.rules = [AvoidInfix, SortImports, RedundantBraces, RedundantParens, SortModifiers]
10 | docstrings.style = Asterisk
11 | docstrings.wrapMaxColumn = 77
12 |
--------------------------------------------------------------------------------
/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 | ## v1.0.0-M1 (2019-08-03)
8 |
9 | * Release cycle decoupled from discipline-core.
10 | * Now supports being mixed into any `TestRegistration`.
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Ross A. Baker
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # discipline-scalatest
2 |
3 | ScalaTest binding for Typelevel Discipline
4 |
5 | ## [Head on over to the microsite](https://typelevel.org/discipline-scalatest)
6 |
7 | ## Quick Start
8 |
9 | ```scala
10 | libraryDependencies ++= Seq(
11 | "org.typelevel" %% "discipline-scalatest" % ""
12 | )
13 | ```
14 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | ThisBuild / tlBaseVersion := "2.3"
2 |
3 | ThisBuild / crossScalaVersions := Seq("2.12.20", "3.3.6", "2.13.16")
4 | ThisBuild / tlVersionIntroduced := Map("3" -> "2.1.5")
5 |
6 | ThisBuild / startYear := Some(2019)
7 | ThisBuild / licenses := Seq(License.MIT)
8 | ThisBuild / developers := List(
9 | tlGitHubDev("larsrh", "Lars Hupel"),
10 | tlGitHubDev("rossabaker", "Ross A. Baker"),
11 | tlGitHubDev("travisbrown", "Travis Brown")
12 | )
13 |
14 | lazy val root = tlCrossRootProject.aggregate(scalatest)
15 |
16 | lazy val scalatest = crossProject(JSPlatform, JVMPlatform, NativePlatform)
17 | .crossType(CrossType.Pure)
18 | .in(file("scalatest"))
19 | .settings(
20 | name := "discipline-scalatest",
21 | libraryDependencies ++= Seq(
22 | "org.typelevel" %%% "discipline-core" % "1.7.0",
23 | "org.scalatestplus" %%% "scalacheck-1-18" % "3.2.19.0",
24 | "org.scalatest" %%% "scalatest-funspec" % "3.2.19",
25 | "org.scalatest" %%% "scalatest-flatspec" % "3.2.19",
26 | "org.scalatest" %%% "scalatest-funsuite" % "3.2.19",
27 | "org.scalatest" %%% "scalatest-wordspec" % "3.2.19"
28 | )
29 | )
30 | .nativeSettings(
31 | tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "2.3.0").toMap
32 | )
33 |
34 | lazy val docs = project
35 | .in(file("site"))
36 | .settings(
37 | tlSiteHelium ~= {
38 | import laika.helium.config.*
39 | def textLink(p: (String, URL)) = TextLink.external(p._2.toString(), p._1)
40 |
41 | _.site.mainNavigation(
42 | appendLinks = Seq(
43 | ThemeNavigationSection(
44 | "Related Projects",
45 | textLink(TypelevelProject.Cats),
46 | textLink(TypelevelProject.Discipline)
47 | )
48 | )
49 | )
50 | }
51 | )
52 | .dependsOn(scalatest.jvm)
53 | .enablePlugins(TypelevelSitePlugin)
54 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # discipline-scalatest
2 |
3 | ScalaTest binding for Typelevel Discipline
4 |
5 | ## Quick Start
6 |
7 | ```scala
8 | libraryDependencies ++= Seq(
9 | "org.typelevel" %% "discipline-scalatest" % "@VERSION@"
10 | )
11 | ```
12 |
13 | ## Mixing in `Discipline`
14 |
15 | Suppose we have the following laws for truth. More useful laws are published in projects like [`cats-laws`](https://github.com/typelevel/cats), [`cats-effect-laws`](https://github.com/typelevel/cats-effect), and [`spire-laws`](https://gihtub.com/typelevel/spire).
16 |
17 | ```scala mdoc
18 | import org.scalacheck.Prop
19 | import org.typelevel.discipline.Laws
20 |
21 | object TruthLaws extends Laws {
22 | def truth = new DefaultRuleSet(
23 | name = "truth",
24 | parent = None,
25 | "true" -> Prop(_ => Prop.Result(status = Prop.True))
26 | )
27 | }
28 | ```
29 |
30 | discipline-scalatest provides a `FunSuiteDiscipline` mixin (as well as similar traits for `FlatSpec` and `FunSpec`), whose `checkAll` helper lets us easily check the laws in ScalaTest:
31 |
32 | ```scala mdoc
33 | import org.scalatest.funsuite.AnyFunSuite
34 | import org.scalatestplus.scalacheck.Checkers
35 | import org.typelevel.discipline.scalatest.FunSuiteDiscipline
36 |
37 | class TruthSuite extends AnyFunSuite with FunSuiteDiscipline with Checkers {
38 | checkAll("Truth", TruthLaws.truth)
39 | }
40 | ```
41 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=1.11.1
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.18.2")
2 | addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.8.0")
3 | addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % "0.8.0")
4 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.7")
5 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")
6 |
--------------------------------------------------------------------------------
/scalatest/src/main/scala/org/typelevel/discipline/scalatest/Discipline.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019 Typelevel
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | * this software and associated documentation files (the "Software"), to deal in
6 | * the Software without restriction, including without limitation the rights to
7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 | * the Software, and to permit persons to whom the Software is furnished to do so,
9 | * subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in all
12 | * copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | */
21 |
22 | package org.typelevel.discipline
23 | package scalatest
24 |
25 | import org.scalactic.Prettifier
26 | import org.scalactic.source.Position
27 | import org.scalatest.flatspec.AnyFlatSpecLike
28 | import org.scalatest.funspec.AnyFunSpecLike
29 | import org.scalatest.funsuite.AnyFunSuiteLike
30 | import org.scalatest.prop.Configuration
31 | import org.scalatest.wordspec.AnyWordSpec
32 | import org.scalatestplus.scalacheck.Checkers
33 |
34 | trait Discipline { self: Configuration =>
35 |
36 | /**
37 | * Convert from our configuration type to the one required by `Checkers`.
38 | *
39 | * We don't extend `Checkers` because we want to leave the user as much
40 | * control as possible over available testing styles. Unfortunately we need
41 | * this conversion because ScalaTest defines `PropertyCheckConfiguration`
42 | * as a case class in the `Configuration` trait.
43 | */
44 | final protected[this] def convertConfiguration(
45 | config: PropertyCheckConfiguration
46 | ): Checkers.PropertyCheckConfiguration =
47 | Checkers.PropertyCheckConfiguration(
48 | config.minSuccessful,
49 | config.maxDiscardedFactor,
50 | config.minSize,
51 | config.sizeRange,
52 | config.workers
53 | )
54 |
55 | def checkAll(name: String, ruleSet: Laws#RuleSet)(implicit
56 | config: PropertyCheckConfiguration,
57 | prettifier: Prettifier,
58 | pos: Position
59 | ): Unit
60 | }
61 |
62 | trait FlatSpecDiscipline extends Discipline { self: AnyFlatSpecLike with Configuration =>
63 | final def checkAll(name: String,
64 | ruleSet: Laws#RuleSet
65 | )(implicit config: PropertyCheckConfiguration, prettifier: Prettifier, pos: Position): Unit =
66 | ruleSet.all.properties.toList match {
67 | case first :: rest =>
68 | name should first._1 in Checkers.check(first._2)(convertConfiguration(config), prettifier, pos)
69 |
70 | for ((id, prop) <- rest)
71 | it should id in Checkers.check(prop)(convertConfiguration(config), prettifier, pos)
72 |
73 | case Nil =>
74 | }
75 | }
76 |
77 | trait FunSpecDiscipline extends Discipline { self: AnyFunSpecLike with Configuration =>
78 | final def checkAll(name: String,
79 | ruleSet: Laws#RuleSet
80 | )(implicit config: PropertyCheckConfiguration, prettifier: Prettifier, pos: Position): Unit =
81 | describe(name) {
82 | for ((id, prop) <- ruleSet.all.properties)
83 | it(id) {
84 | Checkers.check(prop)(convertConfiguration(config), prettifier, pos)
85 | }
86 | }
87 | }
88 |
89 | trait FunSuiteDiscipline extends Discipline { self: AnyFunSuiteLike with Configuration =>
90 | final def checkAll(name: String,
91 | ruleSet: Laws#RuleSet
92 | )(implicit config: PropertyCheckConfiguration, prettifier: Prettifier, pos: Position): Unit =
93 | for ((id, prop) <- ruleSet.all.properties)
94 | test(s"${name}.${id}") {
95 | Checkers.check(prop)(convertConfiguration(config), prettifier, pos)
96 | }
97 | }
98 |
99 | trait WordSpecDiscipline extends Discipline { self: AnyWordSpec with Configuration =>
100 |
101 | def checkAll(name: String,
102 | ruleSet: Laws#RuleSet
103 | )(implicit config: PropertyCheckConfiguration, prettifier: Prettifier, pos: Position): Unit =
104 | for ((id, prop) <- ruleSet.all.properties)
105 | registerTest(s"${name}.${id}") {
106 | Checkers.check(prop)(convertConfiguration(config), prettifier, pos)
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/scalatest/src/test/scala/scalatest/org/typelevel/discipline/scalatest/DisciplineTest.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019 Typelevel
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | * this software and associated documentation files (the "Software"), to deal in
6 | * the Software without restriction, including without limitation the rights to
7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 | * the Software, and to permit persons to whom the Software is furnished to do so,
9 | * subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in all
12 | * copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | */
21 |
22 | package org.typelevel.discipline
23 | package scalatest
24 |
25 | import org.scalatest.flatspec.AnyFlatSpec
26 | import org.scalatest.funspec.AnyFunSpec
27 | import org.scalatest.funsuite.AnyFunSuite
28 | import org.scalatest.wordspec.AnyWordSpec
29 | import org.scalatestplus.scalacheck.Checkers
30 |
31 | trait DummyBase extends Discipline with Checkers {
32 | checkAll("Dummy", DummyLaws.dummy)
33 | }
34 |
35 | class DummyFlatSpec extends AnyFlatSpec with DummyBase with FlatSpecDiscipline
36 | class DummyFunSpec extends AnyFunSpec with DummyBase with FunSpecDiscipline
37 | class DummyFunSuite extends AnyFunSuite with DummyBase with FunSuiteDiscipline
38 | class DummyWordSpec extends AnyWordSpec with DummyBase with WordSpecDiscipline
39 |
--------------------------------------------------------------------------------
/scalatest/src/test/scala/scalatest/org/typelevel/discipline/scalatest/laws.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019 Typelevel
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | * this software and associated documentation files (the "Software"), to deal in
6 | * the Software without restriction, including without limitation the rights to
7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 | * the Software, and to permit persons to whom the Software is furnished to do so,
9 | * subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in all
12 | * copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 | */
21 |
22 | package org.typelevel.discipline.scalatest
23 |
24 | import org.scalacheck.Prop
25 | import org.typelevel.discipline.Laws
26 |
27 | object Dummy {
28 | def prop = Prop(_ => Prop.Result(status = Prop.True))
29 | def prop2 = Prop(true)
30 | }
31 |
32 | object DummyLaws extends Laws {
33 | def dummy =
34 | new DefaultRuleSet(
35 | name = "dummy",
36 | parent = None,
37 | "true" -> Dummy.prop,
38 | "alsoTrue" -> Dummy.prop2
39 | )
40 | }
41 |
--------------------------------------------------------------------------------