Skip to content

Commit

Permalink
Cross-build scalacheck module on scala 3
Browse files Browse the repository at this point in the history
  • Loading branch information
RustedBones committed Nov 8, 2023
1 parent e3eec4a commit 0f12318
Show file tree
Hide file tree
Showing 23 changed files with 348 additions and 239 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:

- name: Build project
if: matrix.scala == '3'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' shared/test test/test
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' shared/test test/test scalacheck/test

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down
6 changes: 4 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ ThisBuild / developers := List(
)

// scala versions
val scala3 = "3.3.0"
val scala3 = "3.3.1"
val scala213 = "2.13.12"
val scala212 = "2.12.18"
val scalaDefault = scala213
Expand All @@ -116,7 +116,8 @@ val coverageCond = Seq(
val scala3Cond = "matrix.scala == '3'"
val scala3Projects = List(
"shared",
"test"
"test",
"scalacheck"
)
ThisBuild / scalaVersion := scalaDefault
ThisBuild / crossScalaVersions := Seq(scala3, scala213, scala212)
Expand Down Expand Up @@ -305,6 +306,7 @@ lazy val scalacheck = project
commonSettings,
moduleName := "magnolify-scalacheck",
description := "Magnolia add-on for ScalaCheck",
crossScalaVersions := Seq(scala3, scala213, scala212),
libraryDependencies += "org.scalacheck" %% "scalacheck" % scalacheckVersion % Provided
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2019 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package magnolify.scalacheck

import magnolia1.*
import org.scalacheck.{Arbitrary, Gen}

object ArbitraryDerivation {
type Typeclass[T] = Arbitrary[T]

private implicit val monadicGen: Monadic[Gen] = new Monadic[Gen] {
override def point[A](value: A): Gen[A] = Gen.const(value)
override def map[A, B](from: Gen[A])(fn: A => B): Gen[B] = from.map(fn)
override def flatMap[A, B](from: Gen[A])(fn: A => Gen[B]): Gen[B] = from.flatMap(fn)
}

def join[T](caseClass: CaseClass[Arbitrary, T]): Arbitrary[T] = Arbitrary {
caseClass.constructMonadic(_.typeclass.arbitrary)
}

def split[T](sealedTrait: SealedTrait[Arbitrary, T]): Arbitrary[T] = Arbitrary {
Gen.lzy {
Gen.sized { size =>
val subtypes = sealedTrait.subtypes
for {
i <-
if (size >= 0) {
// pick any subtype
Gen.choose(0, subtypes.size - 1)
} else {
// pick a fixed subtype to have a chance to stop recursion
Gen.const(subtypes.size + size)
}
subtypeGen <- Gen.resize(size - 1, sealedTrait.subtypes(i).typeclass.arbitrary)
} yield subtypeGen
}
}
}

implicit def gen[T]: Arbitrary[T] = macro Magnolia.gen[T]

@deprecated("Use gen instead", "0.7.0")
def apply[T]: Arbitrary[T] = macro Magnolia.gen[T]
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@
* limitations under the License.
*/

package magnolify.scalacheck.semiauto
package magnolify.scalacheck

import magnolia1._
import magnolia1.*
import org.scalacheck.Cogen

object CogenDerivation {
type Typeclass[T] = Cogen[T]

def join[T](caseClass: ReadOnlyCaseClass[Typeclass, T]): Typeclass[T] = Cogen { (seed, t) =>
caseClass.parameters.foldLeft(seed) { (seed, p) =>
def join[T](caseClass: CaseClass[Cogen, T]): Cogen[T] = Cogen { (seed, t) =>
caseClass.parameters.foldLeft(seed) { (s, p) =>
// inject index to distinguish cases like `(Some(false), None)` and `(None, Some(0))`
val s = Cogen.cogenInt.perturb(seed, p.index)
p.typeclass.perturb(s, p.dereference(t))
p.typeclass.perturb(Cogen.perturb(s, p.index), p.dereference(t))
}
}

def split[T](sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T] = Cogen { (seed, t: T) =>
def split[T](sealedTrait: SealedTrait[Cogen, T]): Cogen[T] = Cogen { (seed, t) =>
sealedTrait.split(t) { sub =>
// inject index to distinguish case objects instances
val s = Cogen.cogenInt.perturb(seed, sub.index)
sub.typeclass.perturb(s, sub.cast(t))
sub.typeclass.perturb(Cogen.perturb(seed, sub.index), sub.cast(t))
}
}

implicit def apply[T]: Typeclass[T] = macro Magnolia.gen[T]
implicit def gen[T]: Cogen[T] = macro Magnolia.gen[T]
@deprecated("Use gen instead", "0.7.0")
def apply[T]: Cogen[T] = macro Magnolia.gen[T]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package magnolify.scalacheck

import org.scalacheck.{Arbitrary, Cogen}

import scala.reflect.macros.*
object ScalaCheckMacros {
def genArbitraryMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val wtt = weakTypeTag[T]
q"""_root_.magnolify.scalacheck.ArbitraryDerivation.gen[$wtt]"""
}

def genCogenMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val wtt = weakTypeTag[T]
q"""_root_.magnolify.scalacheck.CogenDerivation.gen[$wtt]"""
}

}

trait AutoDerivations {
implicit def genArbitrary[T]: Arbitrary[T] = macro ScalaCheckMacros.genArbitraryMacro[T]
implicit def genCogen[T]: Cogen[T] = macro ScalaCheckMacros.genCogenMacro[T]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2023 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package magnolify.scalacheck

import magnolia1.*
import org.scalacheck.{Arbitrary, Gen}

import scala.deriving.Mirror

object ArbitraryDerivation extends Derivation[Arbitrary]:

private given Monadic[Gen] with
def point[A](value: A): Gen[A] = Gen.const(value)
def map[A, B](from: Gen[A])(fn: A => B): Gen[B] = from.map(fn)
def flatMap[A, B](from: Gen[A])(fn: A => Gen[B]): Gen[B] = from.flatMap(fn)

def join[T](caseClass: CaseClass[Arbitrary, T]): Arbitrary[T] = Arbitrary {
caseClass.constructMonadic(_.typeclass.arbitrary)
}

def split[T](sealedTrait: SealedTrait[Arbitrary, T]): Arbitrary[T] = Arbitrary {
Gen.lzy {
Gen.sized { size =>
val subtypes = sealedTrait.subtypes
for {
i <-
if (size >= 0) {
// pick any subtype
Gen.choose(0, subtypes.size - 1)
} else {
// pick a fixed subtype to have a chance to stop recursion
Gen.const(subtypes.size + size)
}
subtypeGen <- Gen.resize(size - 1, sealedTrait.subtypes(i).typeclass.arbitrary)
} yield subtypeGen
}
}
}

inline def gen[T](using Mirror.Of[T]): Arbitrary[T] = derivedMirror[T]
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2023 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package magnolify.scalacheck

import magnolia1.*
import org.scalacheck.Cogen

import scala.deriving.Mirror

object CogenDerivation extends Derivation[Cogen]:

def join[T](caseClass: CaseClass[Cogen, T]): Cogen[T] = Cogen[T] { (seed, t) =>
caseClass.params.foldLeft(seed) { (s, p) =>
// inject index to distinguish cases like `(Some(false), None)` and `(None, Some(0))`
p.typeclass.perturb(Cogen.perturb(s, p.index), p.deref(t))
}
}

def split[T](sealedTrait: SealedTrait[Cogen, T]): Cogen[T] = Cogen[T] { (seed, t) =>
sealedTrait.choose(t) { sub =>
// inject index to distinguish case objects instances
sub.typeclass.perturb(Cogen.perturb(seed, sub.subtype.index), sub.cast(t))
}
}

inline def gen[T](using Mirror.Of[T]): Cogen[T] = derivedMirror[T]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package magnolify.scalacheck

import org.scalacheck.{Arbitrary, Cogen}

import scala.deriving.Mirror
trait AutoDerivations:

inline given genArbitrary[T](using Mirror.Of[T]): Arbitrary[T] = ArbitraryDerivation.derivedMirror[T]
inline given genCogen[T](using Mirror.Of[T]): Cogen[T] = CogenDerivation.derivedMirror[T]
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,4 @@

package magnolify.scalacheck

import org.scalacheck._

import scala.reflect.macros._

package object auto {
def genArbitraryMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val wtt = weakTypeTag[T]
q"""_root_.magnolify.scalacheck.semiauto.ArbitraryDerivation.apply[$wtt]"""
}

def genCogenMacro[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
val wtt = weakTypeTag[T]
q"""_root_.magnolify.scalacheck.semiauto.CogenDerivation.apply[$wtt]"""
}

implicit def genArbitrary[T]: Arbitrary[T] = macro genArbitraryMacro[T]
implicit def genCogen[T]: Cogen[T] = macro genCogenMacro[T]
}
package object auto extends AutoDerivations

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package magnolify.scalacheck

import org.scalacheck.{Arbitrary, Cogen}

package object semiauto {

@deprecated("Use Arbitrary.gen[T] instead", "0.7.0")
val ArbitraryDerivation = magnolify.scalacheck.ArbitraryDerivation
@deprecated("Use Gogen.gen[T] instead", "0.7.0")
val CogenDerivation = magnolify.scalacheck.CogenDerivation

implicit def genArbitrary(a: Arbitrary.type): magnolify.scalacheck.ArbitraryDerivation.type =
magnolify.scalacheck.ArbitraryDerivation
implicit def genCogen(c: Cogen.type): magnolify.scalacheck.CogenDerivation.type =
magnolify.scalacheck.CogenDerivation
}
Loading

0 comments on commit 0f12318

Please sign in to comment.