├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md └── src ├── ScalacheckShapeless.scala ├── config.scala ├── derive ├── GenExtra.scala ├── Instances.scala ├── MkArbitrary.scala ├── MkCogen.scala ├── MkShrink.scala ├── Recursive.scala └── Singletons.scala └── test ├── ArbitraryTests.scala ├── CogenTests.scala ├── PropTests.scala ├── ShrinkTests.scala ├── SingletonsTests.scala ├── SingletonsTestsDefinitions.scala ├── SizeTests.scala ├── SizeTestsDefinitions.scala ├── TestsDefinitions.scala ├── Util.scala └── config.scala /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: 7 | - "v*" 8 | pull_request: 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | - uses: coursier/cache-action@v6 18 | - uses: coursier/setup-action@v1 19 | with: 20 | apps: scala-cli:1.4.1 21 | - name: Test 22 | run: scala-cli test . --power --cross --require-tests 23 | 24 | publish: 25 | if: github.event_name == 'push' 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 0 31 | - uses: coursier/cache-action@v6 32 | - uses: coursier/setup-action@v1 33 | with: 34 | apps: scala-cli:1.4.1 35 | - name: Release 36 | run: scala-cli publish . --power --cross 37 | env: 38 | PUBLISH_SECRET_KEY: ${{ secrets.PUBLISH_SECRET_KEY }} 39 | PUBLISH_SECRET_KEY_PASSWORD: ${{ secrets.PUBLISH_SECRET_KEY_PASSWORD }} 40 | PUBLISH_USER: ${{ secrets.PUBLISH_USER }} 41 | PUBLISH_PASSWORD: ${{ secrets.PUBLISH_PASSWORD }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bsp/ 2 | /.scala-build/ 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We are committed to providing a friendly, safe and welcoming 4 | environment for all, regardless of level of experience, gender, gender 5 | identity and expression, sexual orientation, disability, personal 6 | appearance, body size, race, ethnicity, age, religion, nationality, or 7 | other such characteristics. 8 | 9 | Everyone is expected to follow the [Scala Code of Conduct] when 10 | discussing the project on the available communication channels. If you 11 | are being harassed, please contact us immediately so that we can 12 | support you. 13 | 14 | ## Moderation 15 | 16 | For any questions, concerns, or moderation requests please contact 17 | [Alexandre Archambault], or 18 | [any of the moderators](https://typelevel.org/code-of-conduct.html#contact) 19 | of the [Scala Code of Conduct]. 20 | 21 | [Alexandre Archambault]: mailto:alexandre.archambault+github@gmail.com 22 | [Scala Code of Conduct]: https://typelevel.org/code-of-conduct.html 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scalacheck-shapeless 2 | 3 | Generation of arbitrary case classes / ADTs instances with [scalacheck](https://github.com/typelevel/scalacheck) and [shapeless](https://github.com/milessabin/shapeless) 4 | 5 | [![Build status](https://github.com/alexarchambault/scalacheck-shapeless/workflows/CI/badge.svg)](https://github.com/alexarchambault/scalacheck-shapeless/actions?query=workflow%3ACI) 6 | [![Maven Central](https://img.shields.io/maven-central/v/com.github.alexarchambault/scalacheck-shapeless_1.18_2.13.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.alexarchambault/scalacheck-shapeless_1.18_2.13) 7 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/alexarchambault/scalacheck-shapeless?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 8 | 9 | ## Usage 10 | 11 | Add to your `build.sbt` 12 | ```scala 13 | libraryDependencies += "com.github.alexarchambault" %% "scalacheck-shapeless_1.18" % "1.3.2" 14 | ``` 15 | 16 | scalacheck-shapeless depends on shapeless 2.3 and scalacheck 1.18. It is built against scala 2.12, and 2.13. 17 | 18 | Import the content of `org.scalacheck.ScalacheckShapeless` close to where you want 19 | `Arbitrary` type classes to be automatically available for case classes 20 | / sealed hierarchies, 21 | ```scala 22 | import org.scalacheck.ScalacheckShapeless._ 23 | 24 | // If you defined: 25 | 26 | // case class Foo(i: Int, s: String, blah: Boolean) 27 | // case class Bar(foo: Foo, other: String) 28 | 29 | // sealed trait Base 30 | // case class BaseIntString(i: Int, s: String) extends Base 31 | // case class BaseDoubleBoolean(d: Double, b: Boolean) extends Base 32 | 33 | // then you can now do 34 | 35 | implicitly[Arbitrary[Foo]] 36 | implicitly[Arbitrary[Bar]] 37 | implicitly[Arbitrary[Base]] 38 | ``` 39 | 40 | and in particular, while writing property-based tests, 41 | ```scala 42 | property("some property about Foo") { 43 | forAll { foo: Foo => 44 | // Ensure foo has the required property 45 | } 46 | } 47 | ``` 48 | without having to define yourself an `Arbitrary` for `Foo`. 49 | 50 | ## See also 51 | 52 | - [cats-check](https://github.com/non/cats-check), a library providing cats type class instances for ScalaCheck type classes, 53 | - [scalacheck-datetime](https://github.com/47deg/scalacheck-datetime), a library to deal with datetimes with scalacheck, 54 | - [scalacheck-extensions](https://github.com/cvogt/scalacheck-extensions), a macro-based automatic `Arbitrary` generation (discontinued?). 55 | 56 | ## License 57 | 58 | Released under the Apache 2 license. See LICENSE file for more details. 59 | 60 | ## Code of Conduct 61 | 62 | See the [Code of Conduct](CODE_OF_CONDUCT.md) 63 | -------------------------------------------------------------------------------- /src/ScalacheckShapeless.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import derive._ 4 | 5 | trait ScalacheckShapeless 6 | extends SingletonInstances 7 | with HListInstances 8 | with CoproductInstances0 9 | with DerivedInstances 10 | with FieldTypeInstances 11 | with EnumerationInstances 12 | 13 | object ScalacheckShapeless extends ScalacheckShapeless 14 | 15 | @deprecated("Use ScalacheckShapeless instead", "1.1.6") 16 | object Shapeless extends ScalacheckShapeless 17 | -------------------------------------------------------------------------------- /src/config.scala: -------------------------------------------------------------------------------- 1 | //> using scala "2.13.14", "2.12.19" 2 | //> using platform "jvm", "scala-js", "native" 3 | //> using lib "org.scalacheck::scalacheck::1.18.0" 4 | //> using lib "com.chuusai::shapeless::2.3.12" 5 | //> using jvm "8" 6 | 7 | //> using publish.organization "com.github.alexarchambault" 8 | //> using publish.name "scalacheck-shapeless_1.18" 9 | //> using publish.computeVersion "git:tag" 10 | //> using publish.url "https://github.com/alexarchambault/scalacheck-shapeless" 11 | //> using publish.license "Apache 2.0:http://opensource.org/licenses/Apache-2.0" 12 | //> using publish.developer "alexarchambault|Alexandre Archambault||https://github.com/alexarchambault" 13 | //> using publish.versionControl "github:alexarchambault/scalacheck-shapeless" 14 | //> using publish.repository "central" 15 | 16 | //> using publish.ci.user "env:PUBLISH_USER" 17 | //> using publish.ci.password "env:PUBLISH_PASSWORD" 18 | //> using publish.ci.secretKey "env:PUBLISH_SECRET_KEY" 19 | //> using publish.ci.secretKeyPassword "env:PUBLISH_SECRET_KEY_PASSWORD" 20 | -------------------------------------------------------------------------------- /src/derive/GenExtra.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck.derive 2 | 3 | import org.scalacheck.Gen 4 | 5 | object GenExtra { 6 | 7 | implicit class GenOps[T](val gen: Gen[T]) extends AnyVal { 8 | def getOrFail[U](implicit ev: T <:< Option[U]): Gen[U] = 9 | gen.flatMap { 10 | ev(_) match { 11 | case None => Gen.fail 12 | case Some(u) => Gen.const(u) 13 | } 14 | } 15 | 16 | def failOnStackOverflow: Gen[T] = 17 | Gen.gen { (p, seed) => 18 | try gen.doApply(p, seed) 19 | catch { 20 | // likely not fine on Scala JS 21 | case _: StackOverflowError => 22 | Gen.r(None, seed.next) 23 | } 24 | } 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/derive/Instances.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck.derive 2 | 3 | import org.scalacheck.{Arbitrary, Cogen, Gen, Shrink} 4 | import shapeless.{Coproduct, HList, LowPriority, Strict, Witness} 5 | import shapeless.labelled._ 6 | 7 | trait SingletonInstances { 8 | 9 | implicit def arbitrarySingletonType[S] 10 | (implicit 11 | w: Witness.Aux[S] 12 | ): Arbitrary[S] = 13 | Arbitrary(Gen.const(w.value)) 14 | 15 | /** 16 | * Derives `Cogen[T]` instances for `T` a singleton type, like 17 | * `Witness.``"str"``.T` or `Witness.``true``.T` for example. 18 | * 19 | * The generated `Cogen[T]` behaves like `Cogen[Unit]`, as like 20 | * `Unit`, singleton types only have one instance. 21 | */ 22 | implicit def cogenSingletonType[S] 23 | (implicit 24 | w: Witness.Aux[S] 25 | ): Cogen[S] = 26 | Cogen.cogenUnit 27 | // Extra contramap, that inserts a `next` call on the returned seeds, 28 | // so that case objects are returned the same Cogen here and when derived through Generic. 29 | .contramap[Unit](identity) 30 | .contramap[S](_ => ()) 31 | } 32 | 33 | trait FieldTypeInstances { 34 | 35 | implicit def arbitraryFieldType[K, H] 36 | (implicit 37 | underlying: Arbitrary[H] 38 | ): Arbitrary[FieldType[K, H]] = 39 | Arbitrary( 40 | underlying 41 | .arbitrary 42 | .map(field[K](_)) 43 | ) 44 | 45 | implicit def cogenFieldType[K, H] 46 | (implicit 47 | underlying: Cogen[H] 48 | ): Cogen[FieldType[K, H]] = 49 | underlying 50 | .contramap(h => h: H) 51 | 52 | implicit def shrinkFieldType[K, H] 53 | (implicit 54 | underlying: Shrink[H] 55 | ): Shrink[FieldType[K, H]] = 56 | Shrink.xmap[H, FieldType[K, H]](field[K](_), h => h: H)(underlying) 57 | } 58 | 59 | trait HListInstances { 60 | 61 | implicit def hlistArbitrary[L <: HList] 62 | (implicit 63 | arb: MkHListArbitrary[L] 64 | ): Arbitrary[L] = 65 | arb.arbitrary 66 | 67 | implicit def hlistCogen[L <: HList] 68 | (implicit 69 | arb: MkHListCogen[L] 70 | ): Cogen[L] = 71 | arb.cogen 72 | 73 | implicit def hlistShrink[L <: HList] 74 | (implicit 75 | arb: MkHListShrink[L] 76 | ): Shrink[L] = 77 | arb.shrink 78 | } 79 | 80 | trait CoproductInstances { 81 | 82 | implicit def coproductArbitrary[C <: Coproduct] 83 | (implicit 84 | arb: MkCoproductArbitrary[C] 85 | ): Arbitrary[C] = 86 | arb.arbitrary 87 | 88 | implicit def coproductCogen[C <: Coproduct] 89 | (implicit 90 | arb: MkCoproductCogen[C] 91 | ): Cogen[C] = 92 | arb.cogen 93 | 94 | @deprecated("Kept for binary compatibility", "1.1.7") 95 | def coproductShrink[C <: Coproduct] 96 | (implicit 97 | arb: MkCoproductShrink[C] 98 | ): Shrink[C] = 99 | arb.shrink 100 | } 101 | 102 | trait CoproductInstances0 extends CoproductInstances { 103 | 104 | implicit def coproductShrink0[C <: Coproduct] 105 | (implicit 106 | arb: MkCoproductShrink0[C] 107 | ): Shrink[C] = 108 | arb.shrink 109 | } 110 | 111 | trait DerivedInstances { 112 | 113 | implicit def derivedArbitrary[T] 114 | (implicit 115 | ev: LowPriority, 116 | underlying: Strict[MkArbitrary[T]] 117 | ): Arbitrary[T] = 118 | underlying.value.arbitrary 119 | 120 | implicit def derivedShrink[T] 121 | (implicit 122 | ev: LowPriority.Ignoring[Witness.`"shrinkAny"`.T], 123 | underlying: Strict[MkShrink[T]] 124 | ): Shrink[T] = 125 | underlying.value.shrink 126 | 127 | implicit def derivedCogen[T] 128 | (implicit 129 | ev: LowPriority, 130 | underlying: Strict[MkCogen[T]] 131 | ): Cogen[T] = 132 | underlying.value.cogen 133 | } 134 | 135 | trait EnumerationInstances { 136 | 137 | implicit def arbitraryEnumerationValue[E <: Enumeration] 138 | (implicit 139 | w: Witness.Aux[E] 140 | ): Arbitrary[E#Value] = 141 | Arbitrary(Gen.oneOf(w.value.values.toSeq)) 142 | } 143 | -------------------------------------------------------------------------------- /src/derive/MkArbitrary.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | package derive 3 | 4 | import shapeless._ 5 | import GenExtra._ 6 | 7 | /** 8 | * Derives `Arbitrary[T]` instances for `T` an `HList`, a `Coproduct`, 9 | * a case class or an ADT (or more generally, a type represented 10 | * `Generic`ally as an `HList` or a `Coproduct`). 11 | * 12 | * Use like 13 | * val arbitrary: Arbitrary[T] = MkArbitrary[T].arbitrary 14 | * or look up for an implicit `MkArbitrary[T]`. 15 | */ 16 | trait MkArbitrary[T] { 17 | /** `Arbitrary[T]` instance built by this `MkArbitrary[T]` */ 18 | def arbitrary: Arbitrary[T] 19 | } 20 | 21 | abstract class MkArbitraryLowPriority { 22 | 23 | implicit def genericNonRecursiveCoproduct[S, C <: Coproduct] 24 | (implicit 25 | gen: Generic.Aux[S, C], 26 | mkArb: Lazy[MkCoproductArbitrary[C]] 27 | ): MkArbitrary[S] = 28 | MkArbitrary.instance( 29 | Arbitrary( 30 | Gen.lzy(mkArb.value.arbitrary.arbitrary) 31 | .map(gen.from) 32 | // see the discussion in https://github.com/alexarchambault/scalacheck-shapeless/issues/50 33 | .failOnStackOverflow 34 | ) 35 | ) 36 | } 37 | 38 | object MkArbitrary extends MkArbitraryLowPriority { 39 | def apply[T](implicit mkArb: MkArbitrary[T]): MkArbitrary[T] = mkArb 40 | 41 | def instance[T](arb: => Arbitrary[T]): MkArbitrary[T] = 42 | new MkArbitrary[T] { 43 | def arbitrary = arb 44 | } 45 | 46 | implicit def genericProduct[P, L <: HList] 47 | (implicit 48 | gen: Generic.Aux[P, L], 49 | mkArb: Lazy[MkHListArbitrary[L]] 50 | ): MkArbitrary[P] = 51 | instance( 52 | Arbitrary(Gen.lzy(mkArb.value.arbitrary.arbitrary).map(gen.from)) 53 | ) 54 | 55 | implicit def genericRecursiveCoproduct[S, C <: Coproduct] 56 | (implicit 57 | rec: Recursive[S], 58 | gen: Generic.Aux[S, C], 59 | mkArb: Lazy[MkRecursiveCoproductArbitrary[C]] 60 | ): MkArbitrary[S] = 61 | instance( 62 | Arbitrary( 63 | Gen.lzy(mkArb.value.arbitrary.arbitrary) 64 | .flatMap { 65 | _.valueOpt match { 66 | case None => 67 | rec.default 68 | case Some(c) => 69 | Gen.const(gen.from(c)) 70 | } 71 | } 72 | ) 73 | ) 74 | 75 | @deprecated("Kept for binary compatibility purposes only.", "1.1.7") 76 | def genericCoproduct[S, C <: Coproduct]( 77 | gen: Generic.Aux[S, C], 78 | mkArb: Lazy[MkCoproductArbitrary[C]] 79 | ): MkArbitrary[S] = 80 | instance( 81 | Arbitrary( 82 | Gen.lzy(mkArb.value.arbitrary.arbitrary) 83 | .map(gen.from) 84 | ) 85 | ) 86 | } 87 | 88 | 89 | trait MkHListArbitrary[L <: HList] { 90 | /** `Arbitrary[T]` instance built by this `MkArbitraryHList[T]` */ 91 | def arbitrary: Arbitrary[L] 92 | } 93 | 94 | object MkHListArbitrary { 95 | def apply[L <: HList](implicit mkArb: MkHListArbitrary[L]): MkHListArbitrary[L] = mkArb 96 | 97 | def instance[L <: HList](arb: => Arbitrary[L]): MkHListArbitrary[L] = 98 | new MkHListArbitrary[L] { 99 | def arbitrary = arb 100 | } 101 | 102 | implicit val hnil: MkHListArbitrary[HNil] = 103 | instance(Arbitrary(Gen.const(HNil))) 104 | 105 | implicit def hcons[H, T <: HList, N <: Nat] 106 | (implicit 107 | headArbitrary: Strict[Arbitrary[H]], 108 | tailArbitrary: MkHListArbitrary[T], 109 | length: ops.hlist.Length.Aux[T, N], 110 | n: ops.nat.ToInt[N] 111 | ): MkHListArbitrary[H :: T] = 112 | instance( 113 | Arbitrary { 114 | Gen.sized { size0 => 115 | if (size0 < 0) 116 | // unlike positive values, don't split negative sizes any further, and let subsequent Gen handle them 117 | for { 118 | head <- Gen.resize(size0, Gen.lzy(headArbitrary.value.arbitrary)) 119 | tail <- Gen.resize(size0, Gen.lzy(tailArbitrary.arbitrary.arbitrary)) 120 | } yield head :: tail 121 | else { 122 | // take a fraction of approximately 1 / (n + 1) from size for the head, leave the 123 | // remaining for the tail 124 | 125 | val size = size0 max 0 126 | val remainder = size % (n() + 1) 127 | val fromRemainderGen = 128 | if (remainder > 0) 129 | Gen.choose(1, n()).map(r => if (r <= remainder) 1 else 0) 130 | else 131 | Gen.const(0) 132 | 133 | for { 134 | fromRemainder <- fromRemainderGen 135 | headSize = size / (n() + 1) + fromRemainder 136 | head <- Gen.resize(headSize, Gen.lzy(headArbitrary.value.arbitrary)) 137 | tail <- Gen.resize(size - headSize, Gen.lzy(tailArbitrary.arbitrary.arbitrary)) 138 | } yield head :: tail 139 | } 140 | } 141 | } 142 | ) 143 | } 144 | 145 | trait MkRecursiveCoproductArbitrary[C <: Coproduct] { 146 | /** `Arbitrary[T]` instance built by this `MkRecursiveCoproductArbitrary[T]` */ 147 | def arbitrary: Arbitrary[Recursive.Value[C]] 148 | } 149 | 150 | object MkRecursiveCoproductArbitrary { 151 | def apply[C <: Coproduct](implicit mkArb: MkRecursiveCoproductArbitrary[C]): MkRecursiveCoproductArbitrary[C] = mkArb 152 | 153 | def instance[C <: Coproduct](arb: => Arbitrary[Recursive.Value[C]]): MkRecursiveCoproductArbitrary[C] = 154 | new MkRecursiveCoproductArbitrary[C] { 155 | def arbitrary = arb 156 | } 157 | 158 | implicit val cnil: MkRecursiveCoproductArbitrary[CNil] = 159 | instance(Arbitrary(Gen.fail)) 160 | 161 | implicit def ccons[H, T <: Coproduct, N <: Nat] 162 | (implicit 163 | headArbitrary: Strict[Arbitrary[H]], 164 | tailArbitrary: MkRecursiveCoproductArbitrary[T], 165 | length: ops.coproduct.Length.Aux[T, N], 166 | n: ops.nat.ToInt[N] 167 | ): MkRecursiveCoproductArbitrary[H :+: T] = 168 | instance( 169 | Arbitrary { 170 | Gen.sized { 171 | case n if n < 0 => Gen.const(Recursive.Value(None)) 172 | case size => 173 | val nextSize = size - 1 174 | Gen.frequency( 175 | 1 -> Gen.resize(nextSize, Gen.lzy(headArbitrary.value.arbitrary)).map(h => Recursive.Value(Some(Inl(h)))), 176 | n() -> Gen.resize(nextSize, Gen.lzy(tailArbitrary.arbitrary.arbitrary)).map(_.map(Inr(_))) 177 | ) 178 | } 179 | } 180 | ) 181 | } 182 | 183 | trait MkCoproductArbitrary[C <: Coproduct] { 184 | /** `Arbitrary[T]` instance built by this `MkCoproductArbitrary[T]` */ 185 | def arbitrary: Arbitrary[C] 186 | } 187 | 188 | object MkCoproductArbitrary { 189 | def apply[C <: Coproduct](implicit mkArb: MkCoproductArbitrary[C]): MkCoproductArbitrary[C] = mkArb 190 | 191 | def instance[C <: Coproduct](arb: => Arbitrary[C]): MkCoproductArbitrary[C] = 192 | new MkCoproductArbitrary[C] { 193 | def arbitrary = arb 194 | } 195 | 196 | implicit val cnil: MkCoproductArbitrary[CNil] = 197 | instance(Arbitrary(Gen.fail)) 198 | 199 | implicit def ccons[H, T <: Coproduct, N <: Nat] 200 | (implicit 201 | headArbitrary: Strict[Arbitrary[H]], 202 | tailArbitrary: MkCoproductArbitrary[T], 203 | length: ops.coproduct.Length.Aux[T, N], 204 | n: ops.nat.ToInt[N] 205 | ): MkCoproductArbitrary[H :+: T] = 206 | instance( 207 | Arbitrary { 208 | Gen.sized { size => 209 | /* 210 | * Unlike MkCoproductArbitrary above, try to generate a value no matter what (no Gen.fail). 211 | * This can blow the stack for recursive types, so should be avoided for those. 212 | */ 213 | val nextSize = (size - 1) max 0 214 | Gen.frequency( 215 | 1 -> Gen.resize(nextSize, Gen.lzy(headArbitrary.value.arbitrary)).map(Inl(_)), 216 | n() -> Gen.resize(nextSize, Gen.lzy(tailArbitrary.arbitrary.arbitrary)).map(Inr(_)) 217 | ) 218 | } 219 | } 220 | ) 221 | } 222 | -------------------------------------------------------------------------------- /src/derive/MkCogen.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | package derive 3 | 4 | import org.scalacheck.rng.Seed 5 | 6 | import shapeless._ 7 | 8 | /** 9 | * Derives `Cogen[T]` instances for `T` an `HList`, a `Coproduct`, 10 | * a case class or an ADT (or more generally, a type represented 11 | * `Generic`ally as an `HList` or a `Coproduct`). 12 | * 13 | * Use like 14 | * val cogen: Cogen[T] = MkCogen[T].cogen 15 | * or look up for an implicit `MkCogen[T]`. 16 | */ 17 | trait MkCogen[T] { 18 | /** `Cogen[T]` instance built by this `MkCogen[T]` */ 19 | def cogen: Cogen[T] 20 | } 21 | 22 | object MkCogen { 23 | def apply[T](implicit mkCogen: MkCogen[T]): MkCogen[T] = mkCogen 24 | 25 | def instance[T](cogen0: => Cogen[T]): MkCogen[T] = 26 | new MkCogen[T] { 27 | def cogen = cogen0 28 | } 29 | 30 | implicit def genericProduct[P, L <: HList] 31 | (implicit 32 | gen: Generic.Aux[P, L], 33 | cogen: Lazy[MkHListCogen[L]] 34 | ): MkCogen[P] = instance(Cogen { (seed: Seed, p: P) => 35 | cogen.value.cogen.perturb(seed, gen.to(p)) 36 | }) 37 | 38 | implicit def genericCoproduct[S, C <: Coproduct] 39 | (implicit 40 | gen: Generic.Aux[S, C], 41 | cogen: Lazy[MkCoproductCogen[C]] 42 | ): MkCogen[S] = instance(Cogen { (seed: Seed, s: S) => 43 | cogen.value.cogen.perturb(seed, gen.to(s)) 44 | }) 45 | } 46 | 47 | trait MkHListCogen[L <: HList] { 48 | /** `Cogen[T]` instance built by this `MkCogen[T]` */ 49 | def cogen: Cogen[L] 50 | } 51 | 52 | object MkHListCogen { 53 | def apply[L <: HList](implicit mkCogen: MkHListCogen[L]): MkHListCogen[L] = mkCogen 54 | 55 | def instance[L <: HList](cogen0: => Cogen[L]): MkHListCogen[L] = 56 | new MkHListCogen[L] { 57 | def cogen = cogen0 58 | } 59 | 60 | implicit lazy val hnil: MkHListCogen[HNil] = 61 | instance(Cogen.cogenUnit.contramap(_ => ())) 62 | 63 | implicit def hcons[H, T <: HList] 64 | (implicit 65 | headCogen: Strict[Cogen[H]], 66 | tailCogen: MkHListCogen[T] 67 | ): MkHListCogen[H :: T] = 68 | instance( 69 | Cogen({case (seed, h :: t) => 70 | tailCogen.cogen.perturb(headCogen.value.perturb(seed, h), t) 71 | }: (Seed, H :: T) => Seed) 72 | ) 73 | } 74 | 75 | trait MkCoproductCogen[C <: Coproduct] { 76 | /** `Cogen[T]` instance built by this `MkCogen[T]` */ 77 | def cogen: Cogen[C] 78 | } 79 | 80 | object MkCoproductCogen { 81 | def apply[C <: Coproduct](implicit mkCogen: MkCoproductCogen[C]): MkCoproductCogen[C] = mkCogen 82 | 83 | def instance[C <: Coproduct](cogen0: => Cogen[C]): MkCoproductCogen[C] = 84 | new MkCoproductCogen[C] { 85 | def cogen = cogen0 86 | } 87 | 88 | implicit lazy val cnil: MkCoproductCogen[CNil] = 89 | instance(Cogen.cogenUnit.contramap(_ => ())) 90 | 91 | implicit def ccons[H, T <: Coproduct] 92 | (implicit 93 | headCogen: Strict[Cogen[H]], 94 | tailCogen: MkCoproductCogen[T] 95 | ): MkCoproductCogen[H :+: T] = 96 | instance( 97 | Cogen({ 98 | case (seed, Inl(h)) => 99 | headCogen.value.perturb(seed, h) 100 | case (seed, Inr(t)) => 101 | tailCogen.cogen.perturb(seed.next, t) 102 | }: (Seed, H :+: T) => Seed) 103 | ) 104 | } 105 | -------------------------------------------------------------------------------- /src/derive/MkShrink.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | package derive 3 | 4 | import shapeless._ 5 | 6 | /** 7 | * Derives `Shrink[T]` instances for `T` an `HList`, a `Coproduct`, 8 | * a case class or an ADT (or more generally, a type represented 9 | * `Generic`ally as an `HList` or a `Coproduct`). 10 | * 11 | * The instances derived here are more specific than the default ones 12 | * derived for any type by `Shrink.shrinkAny`. 13 | * 14 | * Use like 15 | * val shrink: Shrink[T] = MkShrink[T].shrink 16 | * or look up for an implicit `MkShrink[T]`. 17 | */ 18 | trait MkShrink[T] { 19 | /** `Shrink[T]` instance built by this `MkShrink[T]` */ 20 | def shrink: Shrink[T] 21 | } 22 | 23 | object MkShrink { 24 | def apply[T](implicit mkShrink: MkShrink[T]): MkShrink[T] = mkShrink 25 | 26 | def instance[T](shrink0: => Shrink[T]): MkShrink[T] = 27 | new MkShrink[T] { 28 | def shrink = shrink0 29 | } 30 | 31 | private def lazyxmap[T, U](from: T => U, to: U => T)(st: => Shrink[T]): Shrink[U] = Shrink[U] { u: U => 32 | st.shrink(to(u)).map(from) 33 | } 34 | 35 | implicit def genericProduct[P, L <: HList] 36 | (implicit 37 | gen: Generic.Aux[P, L], 38 | shrink: Lazy[MkHListShrink[L]] 39 | ): MkShrink[P] = 40 | instance( 41 | lazyxmap(gen.from, gen.to)(shrink.value.shrink) 42 | ) 43 | 44 | @deprecated("Kept for binary compatibility", "1.1.7") 45 | def genericCoproduct[S, C <: Coproduct] 46 | (implicit 47 | gen: Generic.Aux[S, C], 48 | shrink: Lazy[MkCoproductShrink[C]] 49 | ): MkShrink[S] = 50 | instance( 51 | lazyxmap(gen.from, gen.to)(shrink.value.shrink) 52 | ) 53 | 54 | implicit def genericCoproduct0[S, C <: Coproduct] 55 | (implicit 56 | gen: Generic.Aux[S, C], 57 | shrink: Lazy[MkCoproductShrink0[C]] 58 | ): MkShrink[S] = 59 | instance( 60 | lazyxmap(gen.from, gen.to)(shrink.value.shrink) 61 | ) 62 | } 63 | 64 | trait MkHListShrink[L <: HList] { 65 | /** `Shrink[T]` instance built by this `MkHListShrink[T]` */ 66 | def shrink: Shrink[L] 67 | } 68 | 69 | object MkHListShrink { 70 | def apply[L <: HList](implicit mkShrink: MkHListShrink[L]): MkHListShrink[L] = mkShrink 71 | 72 | def instance[L <: HList](shrink0: => Shrink[L]): MkHListShrink[L] = 73 | new MkHListShrink[L] { 74 | def shrink = shrink0 75 | } 76 | 77 | implicit val hnil: MkHListShrink[HNil] = 78 | instance(Shrink.shrinkAny) 79 | 80 | implicit def hcons[H, T <: HList] 81 | (implicit 82 | headShrink: Strict[Shrink[H]], 83 | tailShrink: MkHListShrink[T] 84 | ): MkHListShrink[H :: T] = 85 | instance( 86 | Shrink { 87 | case h :: t => 88 | headShrink.value.shrink(h).map(_ :: t) #::: 89 | tailShrink.shrink.shrink(t).map(h :: _) 90 | } 91 | ) 92 | } 93 | 94 | @deprecated("See MkCoproductShrink0 instead, which has no quadratic implicit lookups", "1.1.7") 95 | trait MkCoproductShrink[C <: Coproduct] { 96 | /** `Shrink[T]` instance built by this `MkCoproductShrink[T]` */ 97 | def shrink: Shrink[C] 98 | } 99 | 100 | object MkCoproductShrink { 101 | def apply[T <: Coproduct](implicit mkShrink: MkCoproductShrink[T]): MkCoproductShrink[T] = mkShrink 102 | 103 | def instance[T <: Coproduct](shrink0: => Shrink[T]): MkCoproductShrink[T] = 104 | new MkCoproductShrink[T] { 105 | def shrink = shrink0 106 | } 107 | 108 | implicit val cnil: MkCoproductShrink[CNil] = 109 | instance(Shrink.shrinkAny) 110 | 111 | implicit def ccons[H, T <: Coproduct] 112 | (implicit 113 | headShrink: Strict[Shrink[H]], 114 | tailShrink: MkCoproductShrink[T], 115 | headSingletons: Strict[Singletons[H]], 116 | tailSingletons: Strict[Singletons[T]] 117 | ): MkCoproductShrink[H :+: T] = 118 | instance( 119 | Shrink { 120 | case Inl(h) => 121 | if (headSingletons.value().contains(h)) Stream.empty 122 | else tailSingletons.value().toStream.map(Inr(_)) ++ headShrink.value.shrink(h).map(Inl(_)) 123 | case Inr(t) => 124 | if (tailSingletons.value().contains(t)) Stream.empty 125 | else headSingletons.value().toStream.map(Inl(_)) ++ tailShrink.shrink.shrink(t).map(Inr(_)) 126 | } 127 | ) 128 | } 129 | 130 | abstract class MkCoproductShrink0[C <: Coproduct] { 131 | /** `Shrink[T]` instance built by this `MkCoproductShrink0[T]` */ 132 | final def shrink: Shrink[C] = 133 | Shrink(apply(_).getOrElse(Stream.empty)) 134 | 135 | /** 136 | * Shrink a value. 137 | * 138 | * @return A [[scala.Stream]] of shrunk values, wrapped in [[scala.Some]], or [[scala.None]] if the value cannot be shrunk any more. 139 | */ 140 | def apply(c: C): Option[Stream[C]] 141 | def singletons: Singletons[C] 142 | } 143 | 144 | object MkCoproductShrink0 { 145 | def apply[T <: Coproduct](implicit mkShrink: MkCoproductShrink0[T]): MkCoproductShrink0[T] = mkShrink 146 | 147 | def instance[T <: Coproduct](singletons0: Singletons[T])(shrink0: T => Option[Stream[T]]): MkCoproductShrink0[T] = 148 | new MkCoproductShrink0[T] { 149 | def apply(t: T) = shrink0(t) 150 | def singletons = singletons0 151 | } 152 | 153 | implicit val cnil: MkCoproductShrink0[CNil] = 154 | instance(Singletons.empty)(_ => Some(Stream.empty)) 155 | 156 | implicit def ccons[H, T <: Coproduct] 157 | (implicit 158 | headShrink: Strict[Shrink[H]], 159 | tailShrink: MkCoproductShrink0[T], 160 | headSingletons: Strict[Singletons[H]] 161 | ): MkCoproductShrink0[H :+: T] = { 162 | 163 | val singletons = Singletons.instance(headSingletons.value().map(Inl(_): H :+: T) ++ tailShrink.singletons().map(Inr(_): H :+: T)) 164 | lazy val headSingletonsSet = headSingletons.value().toSet 165 | 166 | instance[H :+: T](singletons) { 167 | case Inl(h) => 168 | if (headSingletonsSet(h)) 169 | None 170 | else 171 | Some(tailShrink.singletons().toStream.map(Inr(_)) ++ headShrink.value.shrink(h).map(Inl(_))) 172 | case Inr(t) => 173 | tailShrink(t).map(_.map(Inr(_)) ++ headSingletons.value().toStream.map(Inl(_))) 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/derive/Recursive.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck.derive 2 | 3 | import org.scalacheck.Gen 4 | 5 | sealed abstract class Recursive[T] { 6 | /** 7 | * If scalacheck's size parameter prevents generating an arbitrary `T`, generate a value 8 | * with this `Gen[T]` instead. 9 | */ 10 | def default: Gen[T] 11 | } 12 | 13 | object Recursive { 14 | /** 15 | * Flags type `T` as recursive. Resulting value should be marked as implicit. 16 | * 17 | * Makes the generation of recursive type instances deterministic: these don't fail on 18 | * `StackOverflowError`, but via scalacheck's size parameter. In that case, `default0` is used, 19 | * and can either decide to fail or generate fallback values. 20 | */ 21 | def apply[T](default0: Gen[T] = Gen.fail): Recursive[T] = 22 | new Recursive[T] { 23 | def default = default0 24 | } 25 | 26 | case class Value[+T](valueOpt: Option[T]) extends AnyVal { 27 | def map[U](f: T => U): Value[U] = 28 | Value(valueOpt.map(f)) 29 | } 30 | } -------------------------------------------------------------------------------- /src/derive/Singletons.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck.derive 2 | 3 | import shapeless._ 4 | import shapeless.labelled._ 5 | 6 | /** 7 | * Type class providing the instances of `T` that can be built out of 8 | * singletons only. 9 | * 10 | * Used by the derived Shrink instances for ADTs in particular. 11 | */ 12 | trait Singletons[T] { 13 | /** 14 | * Instances of `T` that can be built out of singletons, or 15 | * an empty sequence if none were found. 16 | */ 17 | def apply(): Seq[T] 18 | } 19 | 20 | trait LowPrioritySingletons { 21 | /** 22 | * Fallback case if `T` cannot be built out of singletons. 23 | */ 24 | implicit def singletonsNotFound[T]: Singletons[T] = 25 | Singletons.empty 26 | } 27 | 28 | object Singletons extends LowPrioritySingletons { 29 | def apply[T](implicit s: Singletons[T]): Singletons[T] = s 30 | 31 | def instance[T](s: => Seq[T]): Singletons[T] = 32 | new Singletons[T] { 33 | def apply() = s 34 | } 35 | 36 | def empty[T]: Singletons[T] = instance(Seq.empty) 37 | 38 | implicit def genericProduct[P, L <: HList] 39 | (implicit 40 | gen: Generic.Aux[P, L], 41 | reprSingletons: Lazy[HListSingletons[L]] 42 | ): Singletons[P] = 43 | instance(reprSingletons.value().map(gen.from)) 44 | 45 | implicit def genericCoproduct[S, C <: Coproduct] 46 | (implicit 47 | gen: Generic.Aux[S, C], 48 | reprSingletons: Lazy[CoproductSingletons[C]] 49 | ): Singletons[S] = 50 | instance(reprSingletons.value().map(gen.from)) 51 | 52 | implicit def hlist[L <: HList] 53 | (implicit 54 | underlying: HListSingletons[L] 55 | ): Singletons[L] = 56 | instance(underlying()) 57 | 58 | implicit def coproduct[C <: Coproduct] 59 | (implicit 60 | underlying: CoproductSingletons[C] 61 | ): Singletons[C] = 62 | instance(underlying()) 63 | 64 | implicit def fieldType[K, H] 65 | (implicit 66 | underlying: Singletons[H] 67 | ): Singletons[FieldType[K, H]] = 68 | instance(underlying().map(field[K](_))) 69 | } 70 | 71 | trait HListSingletons[L <: HList] { 72 | /** 73 | * Instances of `L` that can be built out of singletons, or 74 | * an empty sequence if none were found. 75 | */ 76 | def apply(): Seq[L] 77 | } 78 | 79 | object HListSingletons { 80 | def apply[L <: HList](implicit s: HListSingletons[L]): HListSingletons[L] = s 81 | 82 | def instance[L <: HList](s: => Seq[L]): HListSingletons[L] = 83 | new HListSingletons[L] { 84 | def apply() = s 85 | } 86 | 87 | 88 | implicit val hnil: HListSingletons[HNil] = 89 | instance(Seq(HNil)) 90 | 91 | @deprecated("Kept for binary compatibility", "1.1.7") 92 | def hconsFound[H, T <: HList] 93 | (implicit 94 | headSingletons: Strict[Singletons[H]], 95 | tailSingletons: HListSingletons[T] 96 | ): HListSingletons[H :: T] = 97 | instance { 98 | for { 99 | h <- headSingletons.value() 100 | t <- tailSingletons() 101 | } yield h :: t 102 | } 103 | } 104 | 105 | trait CoproductSingletons[C <: Coproduct] { 106 | /** 107 | * Instances of `C` that can be built out of singletons, or 108 | * an empty sequence if none were found. 109 | */ 110 | def apply(): Seq[C] 111 | } 112 | 113 | object CoproductSingletons { 114 | def apply[C <: Coproduct](implicit s: CoproductSingletons[C]): CoproductSingletons[C] = s 115 | 116 | def instance[C <: Coproduct](s: => Seq[C]): CoproductSingletons[C] = 117 | new CoproductSingletons[C] { 118 | def apply() = s 119 | } 120 | 121 | implicit val cnil: CoproductSingletons[CNil] = 122 | instance(Seq.empty) 123 | 124 | implicit def ccons[H, T <: Coproduct] 125 | (implicit 126 | headSingletons: Strict[Singletons[H]], 127 | tailSingletons: CoproductSingletons[T] 128 | ): CoproductSingletons[H :+: T] = 129 | instance(headSingletons.value().map(Inl(_)) ++ tailSingletons().map(Inr(_))) 130 | } 131 | -------------------------------------------------------------------------------- /src/test/ArbitraryTests.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import org.scalacheck.derive._ 4 | 5 | import shapeless.{test => _, _} 6 | import shapeless.labelled.FieldType 7 | import shapeless.record._ 8 | import shapeless.union._ 9 | import shapeless.test.illTyped 10 | 11 | import utest._ 12 | 13 | import Util._ 14 | 15 | object ArbitraryTests extends TestSuite { 16 | import TestsDefinitions._ 17 | import ScalacheckShapeless._ 18 | 19 | 20 | lazy val expectedSimpleArb = 21 | MkArbitrary.genericProduct( 22 | Generic[Simple], 23 | MkHListArbitrary.hcons( 24 | Arbitrary.arbInt, 25 | MkHListArbitrary.hcons( 26 | Arbitrary.arbString, 27 | MkHListArbitrary.hcons( 28 | Arbitrary.arbBool, 29 | MkHListArbitrary.hnil, 30 | ops.hlist.Length[HNil], 31 | ops.nat.ToInt[Nat._0] 32 | ), 33 | ops.hlist.Length[Boolean :: HNil], 34 | ops.nat.ToInt[Nat._1] 35 | ), 36 | ops.hlist.Length[String :: Boolean :: HNil], 37 | ops.nat.ToInt[Nat._2] 38 | ) 39 | ).arbitrary 40 | 41 | lazy val expectedIntStringBoolArb = 42 | MkHListArbitrary.hcons( 43 | Arbitrary.arbInt, 44 | MkHListArbitrary.hcons( 45 | Arbitrary.arbString, 46 | MkHListArbitrary.hcons( 47 | Arbitrary.arbBool, 48 | MkHListArbitrary.hnil, 49 | ops.hlist.Length[HNil], 50 | ops.nat.ToInt[Nat._0] 51 | ), 52 | ops.hlist.Length[Boolean :: HNil], 53 | ops.nat.ToInt[Nat._1] 54 | ), 55 | ops.hlist.Length[String :: Boolean :: HNil], 56 | ops.nat.ToInt[Nat._2] 57 | ).arbitrary 58 | 59 | lazy val expectedIntStringBoolCoproductArb = 60 | MkCoproductArbitrary.ccons( 61 | Arbitrary.arbInt, 62 | MkCoproductArbitrary.ccons( 63 | Arbitrary.arbString, 64 | MkCoproductArbitrary.ccons( 65 | Arbitrary.arbBool, 66 | MkCoproductArbitrary.cnil, 67 | ops.coproduct.Length[CNil], 68 | ops.nat.ToInt[Nat._0] 69 | ), 70 | ops.coproduct.Length[Boolean :+: CNil], 71 | ops.nat.ToInt[Nat._1] 72 | ), 73 | ops.coproduct.Length[String :+: Boolean :+: CNil], 74 | ops.nat.ToInt[Nat._2] 75 | ).arbitrary 76 | 77 | lazy val expectedComposedArb = 78 | MkArbitrary.genericProduct( 79 | Generic[Composed], 80 | MkHListArbitrary.hcons( 81 | expectedSimpleArb, 82 | MkHListArbitrary.hcons( 83 | Arbitrary.arbString, 84 | MkHListArbitrary.hnil, 85 | ops.hlist.Length[HNil], 86 | ops.nat.ToInt[Nat._0] 87 | ), 88 | ops.hlist.Length[String :: HNil], 89 | ops.nat.ToInt[Nat._1] 90 | ) 91 | ).arbitrary 92 | 93 | lazy val expectedTwiceComposedArb = 94 | MkArbitrary.genericProduct( 95 | Generic[TwiceComposed], 96 | MkHListArbitrary.hcons( 97 | expectedSimpleArb, 98 | MkHListArbitrary.hcons( 99 | expectedComposedArb, 100 | MkHListArbitrary.hcons( 101 | Arbitrary.arbInt, 102 | MkHListArbitrary.hnil, 103 | ops.hlist.Length[HNil], 104 | ops.nat.ToInt[Nat._0] 105 | ), 106 | ops.hlist.Length[Int :: HNil], 107 | ops.nat.ToInt[Nat._1] 108 | ), 109 | ops.hlist.Length[Composed :: Int :: HNil], 110 | ops.nat.ToInt[Nat._2] 111 | ) 112 | ).arbitrary 113 | 114 | lazy val expectedComposedOptListArb = 115 | MkArbitrary.genericProduct( 116 | Generic[ComposedOptList], 117 | MkHListArbitrary.hcons( 118 | Arbitrary.arbOption(expectedSimpleArb), 119 | MkHListArbitrary.hcons( 120 | Arbitrary.arbString, 121 | MkHListArbitrary.hcons( 122 | Arbitrary.arbContainer[List, TwiceComposed](expectedTwiceComposedArb, implicitly, identity), 123 | MkHListArbitrary.hnil, 124 | ops.hlist.Length[HNil], 125 | ops.nat.ToInt[Nat._0] 126 | ), 127 | ops.hlist.Length[List[TwiceComposed] :: HNil], 128 | ops.nat.ToInt[Nat._1] 129 | ), 130 | ops.hlist.Length[String :: List[TwiceComposed] :: HNil], 131 | ops.nat.ToInt[Nat._2] 132 | ) 133 | ).arbitrary 134 | 135 | lazy val expectedBaseArb = 136 | MkArbitrary.genericNonRecursiveCoproduct( 137 | Generic[Base], 138 | MkCoproductArbitrary.ccons( 139 | MkArbitrary.genericProduct( 140 | Generic[BaseDB], 141 | MkHListArbitrary.hcons( 142 | Arbitrary.arbDouble, 143 | MkHListArbitrary.hcons( 144 | Arbitrary.arbBool, 145 | MkHListArbitrary.hnil, 146 | ops.hlist.Length[HNil], 147 | ops.nat.ToInt[Nat._0] 148 | ), 149 | ops.hlist.Length[Boolean :: HNil], 150 | ops.nat.ToInt[Nat._1] 151 | ) 152 | ).arbitrary, 153 | MkCoproductArbitrary.ccons( 154 | MkArbitrary.genericProduct( 155 | Generic[BaseIS], 156 | MkHListArbitrary.hcons( 157 | Arbitrary.arbInt, 158 | MkHListArbitrary.hcons( 159 | Arbitrary.arbString, 160 | MkHListArbitrary.hnil, 161 | ops.hlist.Length[HNil], 162 | ops.nat.ToInt[Nat._0] 163 | ), 164 | ops.hlist.Length[String :: HNil], 165 | ops.nat.ToInt[Nat._1] 166 | ) 167 | ).arbitrary, 168 | MkCoproductArbitrary.ccons( 169 | MkArbitrary.genericProduct( 170 | Generic[BaseLast], 171 | MkHListArbitrary.hcons( 172 | expectedSimpleArb, 173 | MkHListArbitrary.hnil, 174 | ops.hlist.Length[HNil], 175 | ops.nat.ToInt[Nat._0] 176 | ) 177 | ).arbitrary, 178 | MkCoproductArbitrary.cnil, 179 | ops.coproduct.Length[CNil], 180 | ops.nat.ToInt[Nat._0] 181 | ), 182 | ops.coproduct.Length[BaseLast :+: CNil], 183 | ops.nat.ToInt[Nat._1] 184 | ), 185 | ops.coproduct.Length[BaseIS :+: BaseLast :+: CNil], 186 | ops.nat.ToInt[Nat._2] 187 | ) 188 | ).arbitrary 189 | 190 | lazy val expectedCCWithSingletonArb = 191 | MkArbitrary.genericProduct( 192 | Generic[CCWithSingleton], 193 | MkHListArbitrary.hcons( 194 | Arbitrary.arbInt, 195 | MkHListArbitrary.hcons( 196 | ScalacheckShapeless.arbitrarySingletonType[Witness.`"aa"`.T], 197 | MkHListArbitrary.hnil, 198 | ops.hlist.Length[HNil], 199 | ops.nat.ToInt[Nat._0] 200 | ), 201 | ops.hlist.Length[Witness.`"aa"`.T :: HNil], 202 | ops.nat.ToInt[Nat._1] 203 | ) 204 | ).arbitrary 205 | 206 | lazy val expectedBaseWithSingletonMainArb = 207 | MkArbitrary.genericProduct( 208 | Generic[BaseWithSingleton.Main], 209 | MkHListArbitrary.hcons( 210 | ScalacheckShapeless.arbitrarySingletonType[Witness.`"aa"`.T], 211 | MkHListArbitrary.hnil, 212 | ops.hlist.Length[HNil], 213 | ops.nat.ToInt[Nat._0] 214 | ) 215 | ).arbitrary 216 | 217 | lazy val expectedBaseWithSingletonDummyArb = 218 | MkArbitrary.genericProduct( 219 | Generic[BaseWithSingleton.Dummy], 220 | MkHListArbitrary.hcons( 221 | Arbitrary.arbInt, 222 | MkHListArbitrary.hnil, 223 | ops.hlist.Length[HNil], 224 | ops.nat.ToInt[Nat._0] 225 | ) 226 | ).arbitrary 227 | 228 | lazy val expectedBaseWithSingletonArb = 229 | MkArbitrary.genericNonRecursiveCoproduct( 230 | Generic[BaseWithSingleton], 231 | MkCoproductArbitrary.ccons( 232 | expectedBaseWithSingletonDummyArb, 233 | MkCoproductArbitrary.ccons( 234 | expectedBaseWithSingletonMainArb, 235 | MkCoproductArbitrary.cnil, 236 | ops.coproduct.Length[CNil], 237 | ops.nat.ToInt[Nat._0] 238 | ), 239 | ops.coproduct.Length[BaseWithSingleton.Main :+: CNil], 240 | ops.nat.ToInt[Nat._1] 241 | ) 242 | ).arbitrary 243 | 244 | lazy val expectedT1TreeArbitrary: Arbitrary[T1.Tree] = 245 | MkArbitrary.genericRecursiveCoproduct( 246 | T1.Tree.recursive, 247 | Generic[T1.Tree], 248 | MkRecursiveCoproductArbitrary.ccons( 249 | MkArbitrary.genericProduct( 250 | Generic[T1.Leaf.type], 251 | Lazy( 252 | MkHListArbitrary.hnil 253 | ) 254 | ).arbitrary, 255 | MkRecursiveCoproductArbitrary.ccons( 256 | MkArbitrary.genericProduct( 257 | Generic[T1.Node], 258 | MkHListArbitrary.hcons( 259 | expectedT1TreeArbitrary, 260 | MkHListArbitrary.hcons( 261 | expectedT1TreeArbitrary, 262 | MkHListArbitrary.hcons( 263 | Arbitrary.arbInt, 264 | MkHListArbitrary.hnil, 265 | ops.hlist.Length[HNil], 266 | ops.nat.ToInt[Nat._0] 267 | ), 268 | ops.hlist.Length[Int :: HNil], 269 | ops.nat.ToInt[Nat._1] 270 | ), 271 | ops.hlist.Length[T1.Tree :: Int :: HNil], 272 | ops.nat.ToInt[Nat._2] 273 | ) 274 | ).arbitrary, 275 | MkRecursiveCoproductArbitrary.cnil, 276 | ops.coproduct.Length[CNil], 277 | ops.nat.ToInt[Nat._0] 278 | ), 279 | ops.coproduct.Length[T1.Node :+: CNil], 280 | ops.nat.ToInt[Nat._1] 281 | ) 282 | ).arbitrary 283 | 284 | lazy val expectedT2TreeArbitrary: Arbitrary[T2.Tree] = 285 | MkArbitrary.genericRecursiveCoproduct( 286 | T2.Tree.recursive, 287 | Generic[T2.Tree], 288 | MkRecursiveCoproductArbitrary.ccons( 289 | MkArbitrary.genericProduct( 290 | Generic[T2.Leaf.type], 291 | MkHListArbitrary.hnil 292 | ).arbitrary, 293 | MkRecursiveCoproductArbitrary.ccons( 294 | MkArbitrary.genericProduct( 295 | Generic[T2.Node], 296 | MkHListArbitrary.hcons( 297 | expectedT2TreeArbitrary, 298 | MkHListArbitrary.hcons( 299 | expectedT2TreeArbitrary, 300 | MkHListArbitrary.hcons( 301 | Arbitrary.arbInt, 302 | MkHListArbitrary.hnil, 303 | ops.hlist.Length[HNil], 304 | ops.nat.ToInt[Nat._0] 305 | ), 306 | ops.hlist.Length[Int :: HNil], 307 | ops.nat.ToInt[Nat._1] 308 | ), 309 | ops.hlist.Length[T2.Tree :: Int :: HNil], 310 | ops.nat.ToInt[Nat._2] 311 | ) 312 | ).arbitrary, 313 | MkRecursiveCoproductArbitrary.cnil, 314 | ops.coproduct.Length[CNil], 315 | ops.nat.ToInt[Nat._0] 316 | ), 317 | ops.coproduct.Length[T2.Node :+: CNil], 318 | ops.nat.ToInt[Nat._1] 319 | ) 320 | ).arbitrary 321 | 322 | lazy val expectedBazArbitrary = 323 | MkArbitrary.genericProduct( 324 | Generic[Baz.type], 325 | MkHListArbitrary.hnil 326 | ).arbitrary 327 | 328 | lazy val expectedFooArbitrary = 329 | MkArbitrary.genericNonRecursiveCoproduct( 330 | Generic[Foo], 331 | MkCoproductArbitrary.ccons( 332 | expectedBazArbitrary, 333 | MkCoproductArbitrary.cnil, 334 | ops.coproduct.Length[CNil], 335 | ops.nat.ToInt[Nat._0] 336 | ) 337 | ).arbitrary 338 | 339 | lazy val expectedFArbitrary = 340 | MkArbitrary.genericProduct( 341 | Generic[F.type], 342 | MkHListArbitrary.hnil 343 | ).arbitrary 344 | 345 | lazy val expectedEArbitrary = 346 | MkArbitrary.genericProduct( 347 | Generic[E], 348 | MkHListArbitrary.hcons( 349 | Arbitrary.arbDouble, 350 | MkHListArbitrary.hcons( 351 | Arbitrary.arbOption(Arbitrary.arbFloat), 352 | MkHListArbitrary.hnil, 353 | ops.hlist.Length[HNil], 354 | ops.nat.ToInt[Nat._0] 355 | ), 356 | ops.hlist.Length[Option[Float] :: HNil], 357 | ops.nat.ToInt[Nat._1] 358 | ) 359 | ).arbitrary 360 | 361 | lazy val expectedDArbitrary = 362 | MkArbitrary.genericNonRecursiveCoproduct( 363 | Generic[D], 364 | MkCoproductArbitrary.ccons( 365 | expectedBazArbitrary, 366 | MkCoproductArbitrary.ccons( 367 | expectedEArbitrary, 368 | MkCoproductArbitrary.ccons( 369 | expectedFArbitrary, 370 | MkCoproductArbitrary.cnil, 371 | ops.coproduct.Length[CNil], 372 | ops.nat.ToInt[Nat._0] 373 | ), 374 | ops.coproduct.Length[F.type :+: CNil], 375 | ops.nat.ToInt[Nat._1] 376 | ), 377 | ops.coproduct.Length[E :+: F.type :+: CNil], 378 | ops.nat.ToInt[Nat._2] 379 | ) 380 | ).arbitrary 381 | 382 | lazy val expectedCArbitrary = 383 | MkArbitrary.genericProduct( 384 | Generic[C.type], 385 | MkHListArbitrary.hnil 386 | ).arbitrary 387 | 388 | lazy val expectedBArbitrary = 389 | MkArbitrary.genericProduct( 390 | Generic[B], 391 | MkHListArbitrary.hcons( 392 | Arbitrary.arbInt, 393 | MkHListArbitrary.hcons( 394 | Arbitrary.arbString, 395 | MkHListArbitrary.hnil, 396 | ops.hlist.Length[HNil], 397 | ops.nat.ToInt[Nat._0] 398 | ), 399 | ops.hlist.Length[String :: HNil], 400 | ops.nat.ToInt[Nat._1] 401 | ) 402 | ).arbitrary 403 | 404 | lazy val expectedAArbitrary = 405 | MkArbitrary.genericNonRecursiveCoproduct( 406 | Generic[A], 407 | MkCoproductArbitrary.ccons( 408 | expectedBArbitrary, 409 | MkCoproductArbitrary.ccons( 410 | expectedBazArbitrary, 411 | MkCoproductArbitrary.ccons( 412 | expectedCArbitrary, 413 | MkCoproductArbitrary.ccons( 414 | expectedEArbitrary, 415 | MkCoproductArbitrary.ccons( 416 | expectedFArbitrary, 417 | MkCoproductArbitrary.cnil, 418 | ops.coproduct.Length[CNil], 419 | ops.nat.ToInt[Nat._0] 420 | ), 421 | ops.coproduct.Length[F.type :+: CNil], 422 | ops.nat.ToInt[Nat._1] 423 | ), 424 | ops.coproduct.Length[E :+: F.type :+: CNil], 425 | ops.nat.ToInt[Nat._2] 426 | ), 427 | ops.coproduct.Length[C.type :+: E :+: F.type :+: CNil], 428 | ops.nat.ToInt[Nat._3] 429 | ), 430 | ops.coproduct.Length[Baz.type :+: C.type :+: E :+: F.type :+: CNil], 431 | ops.nat.ToInt[Nat._4] 432 | ) 433 | ).arbitrary 434 | 435 | lazy val expectetLArbitrary = 436 | MkHListArbitrary.hcons( 437 | Arbitrary.arbInt, 438 | MkHListArbitrary.hcons( 439 | Arbitrary.arbString, 440 | MkHListArbitrary.hnil, 441 | ops.hlist.Length.hnilLength[HNil], 442 | ops.nat.ToInt[Nat._0] 443 | ), 444 | ops.hlist.Length.hlistLength[String, HNil, Nat._0], 445 | ops.nat.ToInt[Nat._1] 446 | ).arbitrary 447 | 448 | lazy val expectetRecArbitrary: Arbitrary[Rec] = 449 | MkHListArbitrary.hcons[FieldType[Witness.`'i`.T, Int], Record.`'s -> String`.T, Nat._1]( 450 | arbitraryFieldType[Witness.`'i`.T, Int](Arbitrary.arbInt), 451 | MkHListArbitrary.hcons[FieldType[Witness.`'s`.T, String], HNil, Nat._0]( 452 | arbitraryFieldType[Witness.`'s`.T, String](Arbitrary.arbString), 453 | MkHListArbitrary.hnil, 454 | ops.hlist.Length.hnilLength[HNil], 455 | ops.nat.ToInt[Nat._0] 456 | ), 457 | ops.hlist.Length.hlistLength[FieldType[Witness.`'s`.T, String], HNil, Nat._0], 458 | ops.nat.ToInt[Nat._1] 459 | ).arbitrary 460 | 461 | lazy val expectetC0Arbitrary = 462 | MkCoproductArbitrary.ccons( 463 | Arbitrary.arbInt, 464 | MkCoproductArbitrary.ccons( 465 | Arbitrary.arbString, 466 | MkCoproductArbitrary.cnil, 467 | ops.coproduct.Length.cnilLength, 468 | ops.nat.ToInt[Nat._0] 469 | ), 470 | ops.coproduct.Length.coproductLength[String, CNil, Nat._0], 471 | ops.nat.ToInt[Nat._1] 472 | ).arbitrary 473 | 474 | lazy val expectedUnionArbitrary: Arbitrary[Un] = 475 | MkCoproductArbitrary.ccons[FieldType[Witness.`'i`.T, Int], Union.`'s -> String`.T, Nat._1]( 476 | arbitraryFieldType[Witness.`'i`.T, Int](Arbitrary.arbInt), 477 | MkCoproductArbitrary.ccons[FieldType[Witness.`'s`.T, String], CNil, Nat._0]( 478 | arbitraryFieldType[Witness.`'s`.T, String](Arbitrary.arbString), 479 | MkCoproductArbitrary.cnil, 480 | ops.coproduct.Length.cnilLength, 481 | ops.nat.ToInt[Nat._0] 482 | ), 483 | ops.coproduct.Length.coproductLength[FieldType[Witness.`'s`.T, String], CNil, Nat._0], 484 | ops.nat.ToInt[Nat._1] 485 | ).arbitrary 486 | 487 | 488 | val tests = TestSuite { 489 | 490 | test("compareSuccess") { 491 | val arb = Arbitrary.arbInt.arbitrary 492 | compareArbitrary(arb, arb) 493 | } 494 | 495 | test("compareFailure") { 496 | val arb = Arbitrary.arbInt 497 | val result = 498 | try { 499 | compareArbitrary(arb.arbitrary, arb.arbitrary.map(_ + 1)) 500 | false 501 | } 502 | catch { 503 | case _: java.lang.AssertionError => true 504 | } 505 | 506 | assert(result) 507 | } 508 | 509 | test("empty") { 510 | val expectedArb = 511 | MkArbitrary.genericProduct( 512 | Generic[Empty.type], 513 | Lazy(MkHListArbitrary.hnil) 514 | ).arbitrary 515 | 516 | val gen = Arbitrary.arbitrary[Empty.type] 517 | compareArbitrary(expectedArb.arbitrary, gen) 518 | } 519 | 520 | test("emptyCC") { 521 | val expectedArb = 522 | MkArbitrary.genericProduct( 523 | Generic[EmptyCC], 524 | Lazy(MkHListArbitrary.hnil) 525 | ).arbitrary 526 | 527 | val gen = Arbitrary.arbitrary[EmptyCC] 528 | compareArbitrary(expectedArb.arbitrary, gen) 529 | } 530 | 531 | test("simple") { 532 | val gen = Arbitrary.arbitrary[Simple] 533 | compareArbitrary(expectedSimpleArb.arbitrary, gen) 534 | } 535 | 536 | test("simpleHList") { 537 | val gen = Arbitrary.arbitrary[Int :: String :: Boolean :: HNil] 538 | compareArbitrary(expectedIntStringBoolArb.arbitrary, gen) 539 | } 540 | 541 | test("simpleCoproduct") { 542 | val gen = Arbitrary.arbitrary[Int :+: String :+: Boolean :+: CNil] 543 | compareArbitrary(expectedIntStringBoolCoproductArb.arbitrary, gen) 544 | } 545 | 546 | test("composed") { 547 | val gen = Arbitrary.arbitrary[Composed] 548 | compareArbitrary(expectedComposedArb.arbitrary, gen) 549 | } 550 | 551 | test("twiceComposed") { 552 | val gen = Arbitrary.arbitrary[TwiceComposed] 553 | compareArbitrary(expectedTwiceComposedArb.arbitrary, gen) 554 | } 555 | 556 | test("composedOptList") { 557 | val gen = Arbitrary.arbitrary[ComposedOptList] 558 | compareArbitrary(expectedComposedOptListArb.arbitrary, gen) 559 | } 560 | 561 | test("base") { 562 | val gen = Arbitrary.arbitrary[Base] 563 | compareArbitrary(expectedBaseArb.arbitrary, gen) 564 | } 565 | 566 | test("tree1") { 567 | val gen = Arbitrary.arbitrary[T1.Tree] 568 | compareArbitrary(expectedT1TreeArbitrary.arbitrary, gen) 569 | } 570 | 571 | test("tree2") { 572 | val gen = Arbitrary.arbitrary[T2.Tree] 573 | compareArbitrary(expectedT2TreeArbitrary.arbitrary, gen) 574 | } 575 | 576 | test("a") { 577 | val gen = Arbitrary.arbitrary[A] 578 | compareArbitrary(expectedAArbitrary.arbitrary, gen) 579 | } 580 | 581 | test("d") { 582 | val gen = Arbitrary.arbitrary[D] 583 | compareArbitrary(expectedDArbitrary.arbitrary, gen) 584 | } 585 | 586 | test("list") { 587 | val expected = Arbitrary.arbContainer[List, Int](Arbitrary.arbInt, implicitly, identity) 588 | val gen = Arbitrary.arbitrary[List[Int]] 589 | compareArbitrary(expected.arbitrary, gen) 590 | } 591 | 592 | test("option") { 593 | val expected = Arbitrary.arbOption(Arbitrary.arbInt) 594 | val gen = Arbitrary.arbitrary[Option[Int]] 595 | compareArbitrary(expected.arbitrary, gen) 596 | } 597 | 598 | test("singleton") { 599 | test("simple") { 600 | val expected = Arbitrary(Gen.const(2: Witness.`2`.T)) 601 | val gen = Arbitrary.arbitrary[Witness.`2`.T] 602 | compareArbitrary(expected.arbitrary, gen) 603 | } 604 | 605 | test("caseClass") { 606 | val gen = Arbitrary.arbitrary[CCWithSingleton] 607 | compareArbitrary(expectedCCWithSingletonArb.arbitrary, gen) 608 | } 609 | 610 | test("ADT") { 611 | val gen = Arbitrary.arbitrary[BaseWithSingleton] 612 | compareArbitrary(expectedBaseWithSingletonArb.arbitrary, gen) 613 | } 614 | } 615 | 616 | test("shapeless") { 617 | test("hlist") { 618 | val gen = Arbitrary.arbitrary[L] 619 | compareArbitrary(expectetLArbitrary.arbitrary, gen) 620 | } 621 | 622 | test("record") { 623 | val gen = Arbitrary.arbitrary[Rec] 624 | compareArbitrary(expectetRecArbitrary.arbitrary, gen) 625 | } 626 | 627 | test("copproduct") { 628 | val gen = Arbitrary.arbitrary[C0] 629 | compareArbitrary(expectetC0Arbitrary.arbitrary, gen) 630 | } 631 | 632 | test("union") { 633 | val gen = Arbitrary.arbitrary[Un] 634 | compareArbitrary(expectedUnionArbitrary.arbitrary, gen) 635 | } 636 | } 637 | 638 | test("enumeration") { 639 | val expected = Arbitrary(Gen.oneOf(WeekDay.values.toSeq)) 640 | val gen = Arbitrary.arbitrary[WeekDay.Value] 641 | compareArbitrary(expected.arbitrary, gen) 642 | } 643 | } 644 | 645 | object NoTC { 646 | import NoTCDefinitions._ 647 | 648 | illTyped(""" 649 | Arbitrary.arbitrary[NoArbitraryType] 650 | """) 651 | 652 | illTyped(""" 653 | Arbitrary.arbitrary[ShouldHaveNoArb] 654 | """) 655 | 656 | illTyped(""" 657 | Arbitrary.arbitrary[ShouldHaveNoArbEither] 658 | """) 659 | 660 | illTyped(""" 661 | Arbitrary.arbitrary[BaseNoArb] 662 | """) 663 | } 664 | 665 | } 666 | -------------------------------------------------------------------------------- /src/test/CogenTests.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import org.scalacheck.derive._ 4 | 5 | import shapeless.{test => _, _} 6 | import shapeless.labelled.FieldType 7 | import shapeless.record.Record 8 | import shapeless.union.Union 9 | 10 | import utest._ 11 | 12 | import Util._ 13 | 14 | object CogenTests extends TestSuite { 15 | import TestsDefinitions._ 16 | import ScalacheckShapeless._ 17 | 18 | lazy val expectedIntStringBoolCogen = 19 | expectedIntStringBoolMkHListCogen.cogen 20 | lazy val expectedIntStringBoolMkHListCogen = 21 | MkHListCogen.hcons( 22 | Cogen.cogenInt, 23 | MkHListCogen.hcons( 24 | Cogen.cogenString, 25 | MkHListCogen.hcons( 26 | Cogen.cogenBoolean, 27 | MkHListCogen.hnil 28 | ) 29 | ) 30 | ) 31 | 32 | lazy val expectedIntStringBoolCoproductCogen = 33 | MkCoproductCogen.ccons( 34 | Cogen.cogenInt, 35 | MkCoproductCogen.ccons( 36 | Cogen.cogenString, 37 | MkCoproductCogen.ccons( 38 | Cogen.cogenBoolean, 39 | MkCoproductCogen.cnil 40 | ) 41 | ) 42 | ).cogen 43 | 44 | lazy val expectedSimpleCogen = 45 | MkCogen.genericProduct( 46 | Generic[Simple], 47 | expectedIntStringBoolMkHListCogen 48 | ).cogen 49 | 50 | lazy val expectedRecCogen = 51 | MkHListCogen.hcons[FieldType[Witness.`'i`.T, Int], Record.`'s -> String`.T]( 52 | cogenFieldType[Witness.`'i`.T, Int](Cogen.cogenInt), 53 | MkHListCogen.hcons[FieldType[Witness.`'s`.T, String], HNil]( 54 | cogenFieldType[Witness.`'s`.T, String](Cogen.cogenString), 55 | MkHListCogen.hnil 56 | ) 57 | ).cogen 58 | 59 | lazy val expectedUnionCogen = 60 | MkCoproductCogen.ccons[FieldType[Witness.`'i`.T, Int], Union.`'s -> String`.T]( 61 | cogenFieldType[Witness.`'i`.T, Int](Cogen.cogenInt), 62 | MkCoproductCogen.ccons[FieldType[Witness.`'s`.T, String], CNil]( 63 | cogenFieldType[Witness.`'s`.T, String](Cogen.cogenString), 64 | MkCoproductCogen.cnil 65 | ) 66 | ).cogen 67 | 68 | lazy val expectedTree1Cogen: Cogen[T1.Tree] = 69 | MkCogen.genericCoproduct( 70 | Generic[T1.Tree], 71 | Lazy(MkCoproductCogen.ccons( 72 | MkCogen.genericProduct( 73 | Generic[T1.Leaf.type], 74 | Lazy(MkHListCogen.hnil) 75 | ).cogen, 76 | MkCoproductCogen.ccons( 77 | MkCogen.genericProduct( 78 | Generic[T1.Node], 79 | Lazy(MkHListCogen.hcons( 80 | expectedTree1Cogen, 81 | MkHListCogen.hcons( 82 | expectedTree1Cogen, 83 | MkHListCogen.hcons( 84 | Cogen.cogenInt, 85 | MkHListCogen.hnil 86 | )))) 87 | ).cogen, 88 | MkCoproductCogen.cnil 89 | ))) 90 | ).cogen 91 | 92 | val tests = TestSuite { 93 | 94 | test("compareSuccess") { 95 | val cogen = Cogen.cogenInt 96 | compareCogen(cogen, cogen) 97 | } 98 | 99 | test("compareFailure") { 100 | val cogen = Cogen.cogenInt 101 | val result = 102 | try { 103 | compareCogen(cogen, cogen.contramap[Int](_ + 1)) 104 | false 105 | } 106 | catch { 107 | case _: java.lang.AssertionError => true 108 | } 109 | 110 | assert(result) 111 | } 112 | 113 | test("empty") { 114 | val expectedCogen = 115 | MkCogen.genericProduct( 116 | Generic[Empty.type], 117 | Lazy(MkHListCogen.hnil) 118 | ).cogen 119 | 120 | val cogen = Cogen[Empty.type] 121 | compareCogen(expectedCogen, cogen) 122 | } 123 | 124 | test("emptyAsSingleton") { 125 | val expectedCogen = 126 | cogenSingletonType[Empty.type] 127 | 128 | val cogen = Cogen[Empty.type] 129 | compareCogen(expectedCogen, cogen) 130 | } 131 | 132 | test("emptyCC") { 133 | val expectedCogen = 134 | MkCogen.genericProduct( 135 | Generic[EmptyCC], 136 | Lazy(MkHListCogen.hnil) 137 | ).cogen 138 | 139 | val cogen = Cogen[EmptyCC] 140 | compareCogen(expectedCogen, cogen) 141 | } 142 | 143 | test("simple") { 144 | val cogen = Cogen[Simple] 145 | compareCogen(expectedSimpleCogen, cogen) 146 | } 147 | 148 | test("simpleHList") { 149 | val cogen = Cogen[Int :: String :: Boolean :: HNil] 150 | compareCogen(expectedIntStringBoolCogen, cogen) 151 | } 152 | 153 | test("simpleCoproduct") { 154 | val cogen = Cogen[Int :+: String :+: Boolean :+: CNil] 155 | compareCogen(expectedIntStringBoolCoproductCogen, cogen) 156 | } 157 | 158 | test("simpleRec") { 159 | val cogen = Cogen[Rec] 160 | compareCogen(expectedRecCogen, cogen) 161 | } 162 | 163 | test("simpleUnion") { 164 | val cogen = Cogen[Un] 165 | compareCogen(expectedUnionCogen, cogen) 166 | } 167 | 168 | test("tree1") { 169 | val cogen = Cogen[T1.Tree] 170 | compareCogen(expectedTree1Cogen, cogen) 171 | } 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/test/PropTests.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import utest._ 4 | import Util._ 5 | import ScalacheckShapeless._ 6 | import org.scalacheck.derive.{MkArbitrary, Recursive} 7 | import org.scalacheck.TestsDefinitions.{T1, T1NoRecursiveTC} 8 | 9 | object PropTests extends TestSuite { 10 | 11 | val tests = TestSuite { 12 | test("oneElementAdt") { 13 | sealed trait Foo 14 | case object Bar extends Foo 15 | 16 | val prop = Prop.forAll { (f: Int => Foo) => f(0); true } 17 | 18 | prop.validate() 19 | } 20 | 21 | test("twoElementAdt") { 22 | sealed trait Or[+A, +B] extends Product with Serializable 23 | case class Left[A](a: A) extends Or[A, Nothing] 24 | case class Right[B](b: B) extends Or[Nothing, B] 25 | 26 | val prop = Prop.forAll { (f: Int => Float Or Boolean) => f(0); true } 27 | 28 | prop.validate() 29 | } 30 | 31 | test("recursiveADT") { 32 | case class Node[T](value: T, left: Option[Node[T]], right: Option[Node[T]]) 33 | 34 | // deriving the Arbitrary[Option[…]] ourselves, so that it safely 35 | // unties the recursion 36 | implicit def rec[T]: Recursive[Option[Node[T]]] = 37 | Recursive[Option[Node[T]]](Gen.const(None)) 38 | implicit def arb[T: Arbitrary]: Arbitrary[Option[Node[T]]] = 39 | MkArbitrary[Option[Node[T]]].arbitrary 40 | 41 | val prop = Prop.forAll { (f: Int => Node[Int]) => f(0); true } 42 | 43 | prop.validate() 44 | } 45 | 46 | test("recursiveADT1") { 47 | val prop = Prop.forAll { (f: Int => T1.Tree) => f(0); true } 48 | 49 | prop.validate(10000) 50 | } 51 | 52 | test("recursiveADT2") { 53 | val prop = Prop.forAll { (f: Int => T1NoRecursiveTC.Tree) => f(0); true } 54 | 55 | // FIXME Doesn't fail with scalacheck 1.14.0, because of its retry mechanism 56 | // scalacheck retries to generate stuff when it gets stackoverflows 57 | // prop.mustFail(10000) 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/ShrinkTests.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import org.scalacheck.ScalacheckShapeless._ 4 | import org.scalacheck.derive._ 5 | import shapeless.{test => _, _} 6 | import shapeless.labelled._ 7 | import shapeless.record.Record 8 | import shapeless.union.Union 9 | 10 | import utest._ 11 | 12 | import Util._ 13 | 14 | object ShrinkTests extends TestSuite { 15 | import TestsDefinitions._ 16 | 17 | lazy val expectedListIntShrink = 18 | Shrink.shrinkContainer[List, Int](identity, Shrink.shrinkIntegral[Int], implicitly) 19 | 20 | lazy val expectedOptionIntShrink = 21 | Shrink.shrinkOption(Shrink.shrinkIntegral[Int]) 22 | 23 | lazy val expectedIntStringBoolShrink = 24 | expectedIntStringBoolMkHListShrink.shrink 25 | lazy val expectedIntStringBoolMkHListShrink = 26 | MkHListShrink.hcons( 27 | Strict(Shrink.shrinkIntegral[Int]), 28 | MkHListShrink.hcons( 29 | Strict(Shrink.shrinkString), 30 | MkHListShrink.hcons( 31 | Strict(Shrink.shrinkAny[Boolean]), 32 | MkHListShrink.hnil 33 | ) 34 | ) 35 | ) 36 | 37 | lazy val expectedIntStringBoolCoproductShrink = 38 | MkCoproductShrink0.ccons( 39 | Strict(Shrink.shrinkIntegral[Int]), 40 | MkCoproductShrink0.ccons( 41 | Strict(Shrink.shrinkString), 42 | MkCoproductShrink0.ccons( 43 | Strict(Shrink.shrinkAny[Boolean]), 44 | MkCoproductShrink0.cnil, 45 | Strict(Singletons[Boolean]) 46 | ), 47 | Strict(Singletons[String]) 48 | ), 49 | Strict(Singletons[Int]) 50 | ).shrink 51 | 52 | lazy val expectedSimpleShrink = 53 | MkShrink.genericProduct( 54 | Generic[Simple], 55 | Lazy( 56 | expectedIntStringBoolMkHListShrink 57 | ) 58 | ).shrink 59 | 60 | lazy val expectedRecShrink = 61 | MkHListShrink.hcons[FieldType[Witness.`'i`.T, Int], Record.`'s -> String`.T]( 62 | shrinkFieldType[Witness.`'i`.T, Int](Shrink.shrinkIntegral[Int]), 63 | MkHListShrink.hcons[FieldType[Witness.`'s`.T, String], HNil]( 64 | shrinkFieldType[Witness.`'s`.T, String](Shrink.shrinkString), 65 | MkHListShrink.hnil 66 | ) 67 | ).shrink 68 | 69 | lazy val expectedUnionShrink = 70 | MkCoproductShrink0.ccons[FieldType[Witness.`'i`.T, Int], Union.`'s -> String`.T]( 71 | shrinkFieldType[Witness.`'i`.T, Int](Shrink.shrinkIntegral[Int]), 72 | MkCoproductShrink0.ccons[FieldType[Witness.`'s`.T, String], CNil]( 73 | shrinkFieldType[Witness.`'s`.T, String](Shrink.shrinkString), 74 | MkCoproductShrink0.cnil, 75 | Singletons[FieldType[Witness.`'s`.T, String]] 76 | ), 77 | Singletons[FieldType[Witness.`'i`.T, Int]] 78 | ).shrink 79 | 80 | 81 | val tests = TestSuite { 82 | 83 | test("listInt") { 84 | val shrink = implicitly[Shrink[List[Int]]] 85 | compareShrink(shrink, expectedListIntShrink) 86 | } 87 | 88 | test("optionInt") { 89 | val shrink = implicitly[Shrink[Option[Int]]] 90 | compareShrink(shrink, expectedOptionIntShrink) 91 | } 92 | 93 | test("simple") { 94 | val shrink = implicitly[Shrink[Simple]] 95 | compareShrink(shrink, expectedSimpleShrink) 96 | } 97 | 98 | test("simpleHList") { 99 | val shrink = implicitly[Shrink[Int :: String :: Boolean :: HNil]] 100 | compareShrink(shrink, expectedIntStringBoolShrink) 101 | } 102 | 103 | test("simpleCoproduct") { 104 | val shrink = implicitly[Shrink[Int :+: String :+: Boolean :+: CNil]] 105 | compareShrink(shrink, expectedIntStringBoolCoproductShrink) 106 | } 107 | 108 | test("simpleRecord") { 109 | val shrink = implicitly[Shrink[Rec]] 110 | compareShrink(shrink, expectedRecShrink) 111 | } 112 | 113 | test("simpleUnion") { 114 | val shrink = implicitly[Shrink[Un]] 115 | compareShrink(shrink, expectedUnionShrink) 116 | } 117 | 118 | test("adt") { 119 | val shrink = implicitly[Shrink[A]] 120 | 121 | test("caseClass") { 122 | * - { 123 | val res = shrink.shrink(B(2, "b")).toVector 124 | assert(res.contains(C)) 125 | assert(res.contains(F)) 126 | assert(res.contains(Baz)) 127 | } 128 | 129 | * - { 130 | val res = shrink.shrink(E(1.3, Some(1.2f))).toVector 131 | assert(res.contains(C)) 132 | assert(res.contains(F)) 133 | assert(res.contains(Baz)) 134 | } 135 | } 136 | 137 | test("caseObject") { 138 | * - { 139 | val res = shrink.shrink(C).toVector 140 | assert(res.isEmpty) 141 | } 142 | 143 | * - { 144 | val res = shrink.shrink(F).toVector 145 | assert(res.isEmpty) 146 | } 147 | 148 | * - { 149 | val res = shrink.shrink(Baz).toVector 150 | assert(res.isEmpty) 151 | } 152 | } 153 | } 154 | 155 | test("maybe") { 156 | val shrink = implicitly[Shrink[Maybe]] 157 | assert(shrink.shrink(Yes).isEmpty) 158 | assert(shrink.shrink(No).isEmpty) 159 | } 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/test/SingletonsTests.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import shapeless.{test => _, _} 4 | 5 | import utest._ 6 | 7 | import Util.validateSingletons 8 | 9 | object SingletonsTests extends TestSuite { 10 | import SingletonsTestsDefinitions._ 11 | 12 | val tests = TestSuite { 13 | test("hnil") { 14 | validateSingletons[HNil](HNil) 15 | } 16 | 17 | test("caseObject") { 18 | validateSingletons[CaseObj.type](CaseObj) 19 | } 20 | 21 | test("emptyCaseClass") { 22 | validateSingletons[Empty](Empty()) 23 | } 24 | 25 | test("adt") { 26 | validateSingletons[Base](BaseEmpty(), BaseObj) 27 | } 28 | 29 | test("adtNotAllSingletons") { 30 | validateSingletons[BaseMore](BaseMoreEmpty(), BaseMoreObj) 31 | } 32 | 33 | test("nonSingletonCaseClass") { 34 | validateSingletons[NonSingleton]() 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/SingletonsTestsDefinitions.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | object SingletonsTestsDefinitions { 4 | 5 | // Running into SI-7046 for Base if these are put directly into SingletonsTests 6 | 7 | case object CaseObj 8 | case class Empty() 9 | 10 | sealed trait Base 11 | case object BaseObj extends Base 12 | case class BaseEmpty() extends Base 13 | 14 | sealed trait BaseMore 15 | case object BaseMoreObj extends BaseMore 16 | case class BaseMoreEmpty() extends BaseMore 17 | case class BaseMoreNonSingleton(i: Int) extends BaseMore 18 | 19 | case class NonSingleton(s: String) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/SizeTests.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import utest._ 4 | 5 | import org.scalacheck.rng.Seed 6 | 7 | object SizeTests0 { 8 | import org.scalacheck.ScalacheckShapeless._ 9 | 10 | import SizeTestsDefinitions._ 11 | 12 | val arbTree = Arbitrary(Arbitrary.arbitrary[Tree]) 13 | 14 | } 15 | 16 | object SizeTests extends TestSuite { 17 | 18 | import SizeTestsDefinitions._ 19 | 20 | assert(Leaf.depth == 0) 21 | assert(Branch(Leaf, Leaf).depth == 1) 22 | assert(Branch(Branch(Leaf, Leaf), Leaf).depth == 2) 23 | 24 | 25 | def stream[T: Arbitrary](p: Gen.Parameters, seed: rng.Seed): Stream[Option[T]] = { 26 | val r = Arbitrary.arbitrary[T].doPureApply(p, seed) 27 | r.retrieve #:: stream[T](p, r.seed) 28 | } 29 | 30 | // manually calculated, grows approx. like log(size) 31 | val maxDepths = Seq( 32 | 10 -> 5, 33 | 100 -> 8, 34 | 300 -> 10 35 | ) 36 | 37 | val tests = TestSuite { 38 | test("tree") { 39 | val seed = Seed.random() 40 | val inspect = 10000 41 | 42 | for ((size, expectedMaxDepth) <- maxDepths) { 43 | val maxDepth = stream[Tree](Gen.Parameters.default.withSize(size), seed)(SizeTests0.arbTree) 44 | .map(_.get) // the corresponding generator doesn't fail thanks to the implicit derive.Recursive[Tree] in Tree's companion 45 | .map(_.depth) 46 | .take(inspect) 47 | .max 48 | 49 | assert(maxDepth == expectedMaxDepth) 50 | } 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/test/SizeTestsDefinitions.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | object SizeTestsDefinitions { 4 | // see https://github.com/rickynils/scalacheck/issues/305 5 | sealed trait Tree { 6 | def depth: Int = { 7 | 8 | var max = 0 9 | val m = new scala.collection.mutable.Queue[(Int, Branch)] 10 | 11 | def handle(t: Tree, s: Int) = 12 | t match { 13 | case Leaf => max = max max s 14 | case b: Branch => 15 | m += (s + 1) -> b 16 | } 17 | 18 | handle(this, 0) 19 | 20 | while (m.nonEmpty) { 21 | val (s, b) = m.dequeue() 22 | handle(b.left, s) 23 | handle(b.right, s) 24 | } 25 | 26 | max 27 | } 28 | } 29 | case object Leaf extends Tree 30 | case class Branch(left: Tree, right: Tree) extends Tree 31 | 32 | object Tree { 33 | implicit val recursive = derive.Recursive[Tree](Gen.const(Leaf)) 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/TestsDefinitions.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import org.scalacheck.derive.Recursive 4 | 5 | import shapeless._ 6 | import shapeless.record._ 7 | import shapeless.union._ 8 | 9 | object TestsDefinitions { 10 | 11 | case class Simple(i: Int, s: String, blah: Boolean) 12 | 13 | case object Empty 14 | case class EmptyCC() 15 | case class Composed(foo: Simple, other: String) 16 | case class TwiceComposed(foo: Simple, bar: Composed, v: Int) 17 | case class ComposedOptList(fooOpt: Option[Simple], other: String, l: List[TwiceComposed]) 18 | 19 | sealed trait Base 20 | case class BaseIS(i: Int, s: String) extends Base 21 | case class BaseDB(d: Double, b: Boolean) extends Base 22 | case class BaseLast(c: Simple) extends Base 23 | 24 | case class CCWithSingleton(i: Int, s: Witness.`"aa"`.T) 25 | 26 | sealed trait BaseWithSingleton 27 | object BaseWithSingleton { 28 | case class Main(s: Witness.`"aa"`.T) extends BaseWithSingleton 29 | case class Dummy(i: Int) extends BaseWithSingleton 30 | } 31 | 32 | object T1 { 33 | sealed abstract class Tree 34 | object Tree { 35 | implicit val recursive = Recursive[Tree](Leaf) 36 | } 37 | final case class Node(left: Tree, right: Tree, v: Int) extends Tree 38 | case object Leaf extends Tree 39 | } 40 | 41 | object T2 { 42 | sealed abstract class Tree 43 | object Tree { 44 | implicit val recursive = Recursive[Tree](Leaf) 45 | } 46 | case class Node(left: Tree, right: Tree, v: Int) extends Tree 47 | case object Leaf extends Tree 48 | } 49 | 50 | object T1NoRecursiveTC { 51 | sealed abstract class Tree 52 | final case class Node(left: Tree, right: Tree, v: Int) extends Tree 53 | case object Leaf extends Tree 54 | } 55 | 56 | // cvogt's hierarchy 57 | sealed trait A 58 | sealed case class B(i: Int, s: String) extends A 59 | case object C extends A 60 | sealed trait D extends A 61 | final case class E(a: Double, b: Option[Float]) extends D 62 | case object F extends D 63 | sealed abstract class Foo extends D 64 | case object Baz extends Foo 65 | // Not supporting this one 66 | // final class Bar extends Foo 67 | // final class Baz(i1: Int)(s1: String) extends Foo 68 | 69 | type L = Int :: String :: HNil 70 | type Rec = Record.`'i -> Int, 's -> String`.T 71 | 72 | type C0 = Int :+: String :+: CNil 73 | type Un = Union.`'i -> Int, 's -> String`.T 74 | 75 | object WeekDay extends Enumeration { 76 | val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value 77 | } 78 | 79 | object NoTCDefinitions { 80 | trait NoArbitraryType 81 | case class ShouldHaveNoArb(n: NoArbitraryType, i: Int) 82 | case class ShouldHaveNoArbEither(s: String, i: Int, n: NoArbitraryType) 83 | 84 | sealed trait BaseNoArb 85 | case class BaseNoArbIS(i: Int, s: String) extends BaseNoArb 86 | case class BaseNoArbDB(d: Double, b: Boolean) extends BaseNoArb 87 | case class BaseNoArbN(n: NoArbitraryType) extends BaseNoArb 88 | } 89 | 90 | sealed trait Maybe 91 | case object Yes extends Maybe 92 | case object No extends Maybe 93 | } 94 | -------------------------------------------------------------------------------- /src/test/Util.scala: -------------------------------------------------------------------------------- 1 | package org.scalacheck 2 | 3 | import org.scalacheck.Gen.Parameters 4 | import org.scalacheck.Test.PropException 5 | import org.scalacheck.rng.Seed 6 | 7 | import org.scalacheck.derive.Singletons 8 | 9 | import utest._ 10 | 11 | object Util { 12 | 13 | implicit class PropExtensions(val prop: Prop) extends AnyVal { 14 | def validate(minSuccessfulTests: Int = 1000): Unit = { 15 | val result = Test.check( 16 | Test.Parameters.default 17 | .withMinSuccessfulTests(minSuccessfulTests), 18 | prop 19 | ) 20 | if (!result.passed) 21 | Console.err.println(result) 22 | result.status match { 23 | case p: PropException => 24 | throw p.e 25 | case _ => 26 | } 27 | assert(result.passed) 28 | } 29 | def mustFail(minSuccessfulTests: Int = 1000): Unit = { 30 | val result = Test.check( 31 | Test.Parameters.default 32 | .withMinSuccessfulTests(minSuccessfulTests), 33 | prop 34 | ) 35 | assert(!result.passed) 36 | } 37 | } 38 | 39 | def streamOf[T](parameters: Parameters, seed: Seed)(arbitrary: Gen[T]): Stream[Option[T]] = { 40 | def helper(seed: Seed): Stream[Option[T]] = { 41 | val r = arbitrary.doApply(parameters, seed) 42 | r.retrieve #:: helper(r.seed) 43 | } 44 | 45 | helper(seed) 46 | } 47 | 48 | def compareArbitraryHelper[T]( 49 | parameters: Parameters, 50 | seed: Seed )( 51 | first: Gen[T], 52 | second: Gen[T] )( 53 | len: Int 54 | ): Unit = { 55 | val generated = 56 | streamOf(parameters, seed)(first) 57 | .zip(streamOf(parameters, seed)(second)) 58 | .take(len) 59 | 60 | assert(generated.forall{case (a, b) => a == b}) 61 | } 62 | 63 | /** Ask each `Gen[T]` a sequence of values, given the same parameters and initial seed, 64 | * and throw an exception if both sequences aren't equal. */ 65 | def compareArbitrary[T](first: Gen[T], second: Gen[T]): Unit = 66 | compareArbitraryHelper(Parameters.default, Seed.random())(first, second)(100) 67 | 68 | 69 | def streamOf[T](gen: Gen[T]): Stream[Option[T]] = { 70 | val elem = gen.sample 71 | elem #:: streamOf(gen) 72 | } 73 | 74 | def compareCogenHelper[T: Arbitrary]( 75 | seed: Seed )( 76 | first: Cogen[T], 77 | second: Cogen[T] )( 78 | len: Int, 79 | min: Int 80 | ): Unit = { 81 | val values = streamOf(Arbitrary.arbitrary[T]) 82 | .take(len) 83 | .collect{case Some(t) => t } 84 | 85 | assert(values.lengthCompare(min) >= 0) 86 | 87 | val firstSeeds = values.scanLeft(seed)(first.perturb) 88 | .map(_.long) 89 | .map(_._1) 90 | val secondSeeds = values.scanLeft(seed)(second.perturb) 91 | .map(_.long) 92 | .map(_._1) 93 | val seeds = firstSeeds zip secondSeeds 94 | 95 | assert(seeds.forall{case (a, b) => a == b}) 96 | } 97 | 98 | def compareCogen[T: Arbitrary](first: Cogen[T], second: Cogen[T]): Unit = 99 | compareCogenHelper(Seed.random())(first, second)(160, 40) 100 | 101 | def compareShrink[T: Arbitrary](first: Shrink[T], second: Shrink[T]): Unit = 102 | Prop.forAll { 103 | t: T => 104 | first.shrink(t) == second.shrink(t) 105 | }.validate() 106 | 107 | def validateSingletons[T: Singletons](expected: T*) = { 108 | val found = Singletons[T].apply() 109 | assert(found == expected) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/config.scala: -------------------------------------------------------------------------------- 1 | //> using lib "com.lihaoyi::utest::0.8.3" 2 | --------------------------------------------------------------------------------