Skip to content

Commit

Permalink
Cross-build to scala 3 (#244)
Browse files Browse the repository at this point in the history
* Attempt cross-build to scala 3
* Only use kind-projector plugin under scala 2
* Do not duplicate sbt-tpolecat options
* Add source 3 cross-compilation
* Add scalafmt
* Remove redundant final modifier
* Make UtilitiesTest work under scala 3 expect implementation
  • Loading branch information
dimitarg authored Oct 30, 2024
1 parent 9877049 commit c51c212
Show file tree
Hide file tree
Showing 16 changed files with 178 additions and 110 deletions.
16 changes: 13 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
scala: [2.13.15, 2.12.20]
scala: [2.13.15, 2.12.20, 3.3.4]
java: [temurin@21]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -50,12 +50,12 @@ jobs:
- name: Check that workflows are up to date
run: sbt '++ ${{ matrix.scala }}' githubWorkflowCheck

- run: sbt '++ ${{ matrix.scala }}' scalafmtCheck

- env:
HONEYCOMB_WRITE_KEY: ${{ secrets.HONEYCOMB_WRITE_KEY }}
run: sbt '++ ${{ matrix.scala }}' test

- run: 'bash <(curl -s https://codecov.io/bash)'

- name: Compress target directories
run: tar cf targets.tar target project/target

Expand Down Expand Up @@ -112,6 +112,16 @@ jobs:
tar xf targets.tar
rm targets.tar
- name: Download target directories (3.3.4)
uses: actions/download-artifact@v4
with:
name: target-${{ matrix.os }}-3.3.4-${{ matrix.java }}

- name: Inflate target directories (3.3.4)
run: |
tar xf targets.tar
rm targets.tar
- run: |
git config user.name "Github Actions (dimitarg/weaver-test-extra)"
git config user.email "[email protected]"
Expand Down
3 changes: 3 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
version = 3.8.2
runner.dialect = scala213source3
maxColumn = 120
32 changes: 26 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ name := "weaver-test-extra"
ThisBuild / organization := "io.github.dimitarg"

ThisBuild / scalaVersion := "2.13.15"
ThisBuild / crossScalaVersions := Seq("2.13.15", "2.12.20")
ThisBuild / githubWorkflowScalaVersions := Seq("2.13.15", "2.12.20")
ThisBuild / crossScalaVersions := Seq("2.13.15", "2.12.20", "3.3.4")
ThisBuild / githubWorkflowScalaVersions := Seq("2.13.15", "2.12.20", "3.3.4")

ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("21"))

ThisBuild / githubWorkflowBuild := Seq(
WorkflowStep.Sbt(
commands = List("scalafmtCheck")
),
WorkflowStep.Sbt(
// scoverage plugin not yet supporting scala 2.13.15
// commands = List("coverage", "test"),
Expand All @@ -34,9 +37,10 @@ ThisBuild / githubWorkflowPublishTargetBranches += RefPredicate.Equals(Ref.Branc

ThisBuild / licenses += ("Apache-2.0", url("https://opensource.org/licenses/Apache-2.0"))

ThisBuild / githubWorkflowBuildPostamble := Seq(WorkflowStep.Run(
commands = List("bash <(curl -s https://codecov.io/bash)")
))
// scoverage plugin not yet supporting scala 2.13.15
// ThisBuild / githubWorkflowBuildPostamble := Seq(WorkflowStep.Run(
// commands = List("bash <(curl -s https://codecov.io/bash)")
// ))

ThisBuild / githubWorkflowPublishPreamble := Seq(WorkflowStep.Run(
List(
Expand Down Expand Up @@ -67,7 +71,23 @@ libraryDependencies ++=Seq(

testFrameworks += new TestFramework("weaver.framework.CatsEffect")

addCompilerPlugin("org.typelevel" % "kind-projector" % "0.13.3" cross CrossVersion.full)
libraryDependencies ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) =>
List(
compilerPlugin("org.typelevel" % "kind-projector" % "0.13.3" cross CrossVersion.full)
)
case _ =>
Nil
}
}

ThisBuild / scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 12 | 13)) => Seq("-Xsource:3-cross", "-P:kind-projector:underscore-placeholders")
case _ => Nil
}
}

releasePublishArtifactsAction := PgpKeys.publishSigned.value

Expand Down
1 change: 1 addition & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ addSbtPlugin("org.typelevel" % "sbt-tpolecat" % "0.5.2")
addSbtPlugin("com.github.sbt" % "sbt-release" % "1.4.0")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
2 changes: 1 addition & 1 deletion src/main/scala/weaver/pure/Suite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ trait Suite extends EffectSuite[IO] with BaseCatsSuite with Expectations.Helpers
override implicit protected def effectCompat: EffectCompat[IO] = CatsUnsafeRun

override def getSuite: EffectSuite[IO] = this
}
}
16 changes: 11 additions & 5 deletions src/main/scala/weaver/pure/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ import cats.effect.IO
import fs2.Stream

package object pure extends Expectations.Helpers {

import util._

def test(name: String)(run: IO[Expectations])(implicit loc: SourceLocation): IO[Test] = {
def test(
name: String
)(run: IO[Expectations])(implicit loc: SourceLocation): IO[Test] = {
failureToExpectations(run)
.map(x => Test(name, x))
}

def pureTest(name: String)(run: => Expectations)(implicit loc: SourceLocation): Test = Test(name, run)
def pureTest(name: String)(run: => Expectations)(implicit
loc: SourceLocation
): Test = Test(name, run)

def parSuite(tests: List[IO[Test]]): Stream[IO, Test] = Stream.evals(tests.parTraverse(identity))
def parSuite(tests: List[IO[Test]]): Stream[IO, Test] =
Stream.evals(tests.parTraverse(identity))

def seqSuite(tests: List[IO[Test]]): Stream[IO, Test] = Stream.evals(tests.sequence)
def seqSuite(tests: List[IO[Test]]): Stream[IO, Test] =
Stream.evals(tests.sequence)
}
29 changes: 19 additions & 10 deletions src/main/scala/weaver/pure/traced/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ package object traced {

type TracedTest = ReaderT[IO, Span[IO], Test]

def tracedTest(name: String)(run: Span[IO] => IO[Expectations])(implicit loc: SourceLocation): TracedTest =
def tracedTest(name: String)(
run: Span[IO] => IO[Expectations]
)(implicit loc: SourceLocation): TracedTest =
ReaderT { parent =>
parent.span(name).use{ span =>
weaver.pure.test(name)(run(span))
parent.span(name).use { span =>
weaver.pure
.test(name)(run(span))
.flatTap { test =>
traceExpectationFailures(span, test)
}
Expand All @@ -28,18 +31,24 @@ package object traced {
_ => IO.unit
)

def tracedParSuite(name: String)(suite: List[TracedTest])(implicit rootSpan: Span[IO]): Stream[IO,Test] =
def tracedParSuite(name: String)(suite: List[TracedTest])(implicit
rootSpan: Span[IO]
): Stream[IO, Test] =
tracedSuite(weaver.pure.parSuite)(name)(suite)(rootSpan)

def tracedSeqSuite(name: String)(suite: List[TracedTest])(implicit rootSpan: Span[IO]): Stream[IO,Test] =

def tracedSeqSuite(name: String)(suite: List[TracedTest])(implicit
rootSpan: Span[IO]
): Stream[IO, Test] =
tracedSuite(weaver.pure.seqSuite)(name)(suite)(rootSpan)

def tracedSuite(toStream: List[IO[Test]] => Stream[IO, Test])(name: String)(suite: List[TracedTest]): Span[IO] => Stream[IO,Test] =
parent =>

def tracedSuite(
toStream: List[IO[Test]] => Stream[IO, Test]
)(name: String)(suite: List[TracedTest]): Span[IO] => Stream[IO, Test] =
parent =>
Stream.resource(parent.span(name)).flatMap { suiteSpan =>
val testsWithSpan = suite.map(_.run(suiteSpan))
toStream(testsWithSpan)

}

}
11 changes: 7 additions & 4 deletions src/main/scala/weaver/pure/util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import weaver.{Test => WeaverTest, Expectations, SourceLocation, TestOutcome}

import cats.effect.IO

private [pure] object util {
def failureToExpectations(x: IO[Expectations])(implicit loc: SourceLocation): IO[Expectations] = {
private[pure] object util {
def failureToExpectations(
x: IO[Expectations]
)(implicit loc: SourceLocation): IO[Expectations] = {
x.handleErrorWith { t =>
IO.delay(t.printStackTrace()).map { _ =>
failure(t.toString())
}
}
}

def toTestOutcome(test: Test): TestOutcome = WeaverTest.pure(test.name.name)(() => test.run)
def toTestOutcome(test: Test): TestOutcome =
WeaverTest.pure(test.name.name)(() => test.run)

def outcomeToString(x: TestOutcome): String = x.formatted(TestOutcome.Verbose)

}
37 changes: 18 additions & 19 deletions src/test/scala/com/dimitarg/example/ExampleResSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,39 @@ import weaver.pure._

object ExampleResSuite extends Suite {

// shared resource
// shared resource
final case class TextFile(lines: List[String])

// describe how to acquire shared resource
val sharedResource: Resource[IO, TextFile] = for {
_ <- Resource.make(
IO(ExecutionContext.fromExecutorService(Executors.newCachedThreadPool()))
)( x =>
IO(x.shutdown)
)
IO(ExecutionContext.fromExecutorService(Executors.newCachedThreadPool()))
)(x => IO(x.shutdown))
xs = fs2.io.readInputStream(
IO(getClass().getResourceAsStream("/foo.txt")),
1024, closeAfterUse = true
)
IO(getClass().getResourceAsStream("/foo.txt")),
1024,
closeAfterUse = true
)
lines <- Resource.eval(
xs.through(fs2.text.utf8.decode).through(fs2.text.lines).compile.toList
xs.through(fs2.text.utf8.decode).through(fs2.text.lines).compile.toList
)
} yield TextFile(lines)


// suite which uses shared resource
val suites: TextFile => Stream[IO, Test] = textFile => Stream(
pureTest("the file has one line") {
expect(textFile.lines.size == 1)
},
pureTest("the file has the expected content") {
expect(textFile.lines == List("Hello, there!"))
}
)
val suites: TextFile => Stream[IO, Test] = textFile =>
Stream(
pureTest("the file has one line") {
expect(textFile.lines.size == 1)
},
pureTest("the file has the expected content") {
expect(textFile.lines == List("Hello, there!"))
}
)

// construct `suitesStream` by acquiring resource and passing that to your `suite` via `flatMap`
override def suitesStream: Stream[IO, Test] =
Stream.resource(sharedResource).flatMap { res =>
suites(res)
}

}
38 changes: 21 additions & 17 deletions src/test/scala/com/dimitarg/example/ExampleSharedResSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,43 @@ final case class SharedResource(foo: FooResource, bar: BarResource)

object FooSuite {

val all: FooResource => Stream[IO, Test] = foo => Stream(
pureTest("the foo foos") {
val all: FooResource => Stream[IO, Test] = foo =>
Stream(
pureTest("the foo foos") {
expect(foo == FooResource())
}
)
}
)
}

object BarSuite {
val all: BarResource => Stream[IO, Test] = bar => Stream(
pureTest("a barsuite test") {
expect(bar.value == 42)
}
)
val all: BarResource => Stream[IO, Test] = bar =>
Stream(
pureTest("a barsuite test") {
expect(bar.value == 42)
}
)
}

object ExampleSharedResSuite extends Suite {

val mkSharedResource: Resource[IO, SharedResource] = for {
_ <- Resource.eval(IO.pure(println("acquiring shared resource")))
res <- Resource.eval(IO.pure(
SharedResource(FooResource(), BarResource(42))
))
res <- Resource.eval(
IO.pure(
SharedResource(FooResource(), BarResource(42))
)
)
} yield res

val suiteUsingAllResources: SharedResource => Stream[IO, Test] = res => Stream(
pureTest("some test"){
val suiteUsingAllResources: SharedResource => Stream[IO, Test] = res =>
Stream(pureTest("some test") {
expect(res.bar.value == 42)
})
})

override def suitesStream: Stream[IO, Test] =
Stream.resource(mkSharedResource).flatMap { r =>
suiteUsingAllResources(r) ++
FooSuite.all(r.foo) ++
BarSuite.all(r.bar)
FooSuite.all(r.foo) ++
BarSuite.all(r.bar)
}
}
2 changes: 1 addition & 1 deletion src/test/scala/com/dimitarg/example/ExampleSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ object ExampleSuite extends Suite {

override def suitesStream: Stream[IO, Test] = Stream(
pureTest("a pure test") {
val x = 1
val x = 1
expect(x == 1)
},
pureTest("another pure test") {
Expand Down
Loading

0 comments on commit c51c212

Please sign in to comment.