diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abe7a35..3217e41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,8 @@ 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 diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..73f9e8e --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,3 @@ +version = 3.8.2 +runner.dialect = scala213source3 +maxColumn = 120 diff --git a/build.sbt b/build.sbt index 798e53c..32aa488 100644 --- a/build.sbt +++ b/build.sbt @@ -11,6 +11,9 @@ 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"), diff --git a/project/plugins.sbt b/project/plugins.sbt index 68c6df6..7908d1e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -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") diff --git a/src/main/scala/weaver/pure/Suite.scala b/src/main/scala/weaver/pure/Suite.scala index 6525f95..551a617 100644 --- a/src/main/scala/weaver/pure/Suite.scala +++ b/src/main/scala/weaver/pure/Suite.scala @@ -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 -} \ No newline at end of file +} diff --git a/src/main/scala/weaver/pure/package.scala b/src/main/scala/weaver/pure/package.scala index 3101cf7..2daa31f 100644 --- a/src/main/scala/weaver/pure/package.scala +++ b/src/main/scala/weaver/pure/package.scala @@ -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) } diff --git a/src/main/scala/weaver/pure/traced/package.scala b/src/main/scala/weaver/pure/traced/package.scala index 13facde..c987f21 100644 --- a/src/main/scala/weaver/pure/traced/package.scala +++ b/src/main/scala/weaver/pure/traced/package.scala @@ -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) } @@ -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) } - + } diff --git a/src/main/scala/weaver/pure/util.scala b/src/main/scala/weaver/pure/util.scala index 3c6f395..80a109e 100644 --- a/src/main/scala/weaver/pure/util.scala +++ b/src/main/scala/weaver/pure/util.scala @@ -4,8 +4,10 @@ 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()) @@ -13,8 +15,9 @@ private [pure] object util { } } - 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) - + } diff --git a/src/test/scala/com/dimitarg/example/ExampleResSuite.scala b/src/test/scala/com/dimitarg/example/ExampleResSuite.scala index d65bf26..83771a9 100644 --- a/src/test/scala/com/dimitarg/example/ExampleResSuite.scala +++ b/src/test/scala/com/dimitarg/example/ExampleResSuite.scala @@ -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) } - + } diff --git a/src/test/scala/com/dimitarg/example/ExampleSharedResSuite.scala b/src/test/scala/com/dimitarg/example/ExampleSharedResSuite.scala index 7e9dc49..403830c 100644 --- a/src/test/scala/com/dimitarg/example/ExampleSharedResSuite.scala +++ b/src/test/scala/com/dimitarg/example/ExampleSharedResSuite.scala @@ -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) } } diff --git a/src/test/scala/com/dimitarg/example/ExampleSuite.scala b/src/test/scala/com/dimitarg/example/ExampleSuite.scala index b136a48..8fc125b 100644 --- a/src/test/scala/com/dimitarg/example/ExampleSuite.scala +++ b/src/test/scala/com/dimitarg/example/ExampleSuite.scala @@ -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") { diff --git a/src/test/scala/com/dimitarg/example/traced/ExampleTracedSuite.scala b/src/test/scala/com/dimitarg/example/traced/ExampleTracedSuite.scala index dc8b90a..7911268 100644 --- a/src/test/scala/com/dimitarg/example/traced/ExampleTracedSuite.scala +++ b/src/test/scala/com/dimitarg/example/traced/ExampleTracedSuite.scala @@ -19,21 +19,27 @@ import com.dimitarg.example.util.IntegrationTestConfig object ExampleTracedSuite extends Suite { - override def suitesStream: Stream[IO,Test] = - Stream.resource(makeEntryPoint.flatMap(_.root("ExampleTracedSuite"))).flatMap { implicit rootSpan => - val service = SomeTracedService.apply[App] - tracedParSuite("Service tests")(serviceTests(service)) ++ - tracedSeqSuite("Some other tests")(someOtherTests) - } + override def suitesStream: Stream[IO, Test] = + Stream + .resource(makeEntryPoint.flatMap(_.root("ExampleTracedSuite"))) + .flatMap { implicit rootSpan => + val service = SomeTracedService.apply[App] + tracedParSuite("Service tests")(serviceTests(service)) ++ + tracedSeqSuite("Some other tests")(someOtherTests) + } def serviceTests(service: SomeTracedService[App]): List[TracedTest] = List( tracedTest("SomeTracedService.foo test") { span => - service.translate(provideSpan(span)).foo - .as(success) + service + .translate(provideSpan(span)) + .foo + .as(success) }, tracedTest("SomeTracedService.bar test") { span => - service.translate(provideSpan(span)).bar - .as(success) + service + .translate(provideSpan(span)) + .bar + .as(success) } ) @@ -41,8 +47,9 @@ object ExampleTracedSuite extends Suite { tracedTest("some other test 1") { _ => expect(1 === 1).pure[IO] }, - tracedTest("some other test 2") { span => - span.put("app.important.info" -> 42) + tracedTest("some other test 2") { span => + span + .put("app.important.info" -> 42) .as(expect(2 === 2)) } ) @@ -53,14 +60,14 @@ object ExampleTracedSuite extends Suite { case IntegrationTestConfig.CI(hcKey) => natchez.honeycomb.Honeycomb.entryPoint[IO]( service = "weaver-test-extra-tests" - ) { builder => + ) { builder => IO.delay { builder .setDataset("weaver-test-extra-tests") .setWriteKey(hcKey) .setGlobalFields( Map( - "service_name" -> "weaver-test-extra-tests", + "service_name" -> "weaver-test-extra-tests" ).asJava ) .build diff --git a/src/test/scala/com/dimitarg/example/util/IntegrationTestConfig.scala b/src/test/scala/com/dimitarg/example/util/IntegrationTestConfig.scala index 7ec4f58..21f145a 100644 --- a/src/test/scala/com/dimitarg/example/util/IntegrationTestConfig.scala +++ b/src/test/scala/com/dimitarg/example/util/IntegrationTestConfig.scala @@ -7,8 +7,8 @@ sealed trait IntegrationTestConfig object IntegrationTestConfig { final case class CI( - honeycombWriteKey: String, - ) extends IntegrationTestConfig { + honeycombWriteKey: String + ) extends IntegrationTestConfig { override def toString = "IntegrationTestConfig.CI()" } @@ -23,7 +23,7 @@ object IntegrationTestConfig { } } yield hcKey.fold[IntegrationTestConfig] { IntegrationTestConfig.NotCI - } { hcKey => + } { hcKey => if (isCi) { IntegrationTestConfig.CI(hcKey) } else { diff --git a/src/test/scala/weaver/pure/ApiTests.scala b/src/test/scala/weaver/pure/ApiTests.scala index f68f2ae..046e794 100644 --- a/src/test/scala/weaver/pure/ApiTests.scala +++ b/src/test/scala/weaver/pure/ApiTests.scala @@ -3,17 +3,21 @@ package weaver.pure import cats.effect.IO import fs2.Stream import scala.util.control.NoStackTrace - + object ApiTest extends Suite { - override def suitesStream: Stream[IO,Test] = parSuite(List( - test("`test` raises IO errors as Expectations failures") { - val tst = test("something")(IO.raiseError(SomeError)) - tst.map { result => - expect(result.run.run.isInvalid) // as opposed to this raising an io error and failing the test + override def suitesStream: Stream[IO, Test] = parSuite( + List( + test("`test` raises IO errors as Expectations failures") { + val tst = test("something")(IO.raiseError(SomeError)) + tst.map { result => + expect( + result.run.run.isInvalid + ) // as opposed to this raising an io error and failing the test + } } - } - )) + ) + ) final case object SomeError extends Exception with NoStackTrace } diff --git a/src/test/scala/weaver/pure/ResourceHandlingTest.scala b/src/test/scala/weaver/pure/ResourceHandlingTest.scala index 9fd98e0..b02c8ff 100644 --- a/src/test/scala/weaver/pure/ResourceHandlingTest.scala +++ b/src/test/scala/weaver/pure/ResourceHandlingTest.scala @@ -8,7 +8,7 @@ import fs2.Stream object ResourceHandlingTest extends Suite { - override def suitesStream: Stream[IO,Test] = + override def suitesStream: Stream[IO, Test] = Stream.resource(SomeService.make).flatMap { service => Stream.eval( test("Stream lifecycle is correctly handled") { @@ -19,20 +19,23 @@ object ResourceHandlingTest extends Suite { } final case class SomeService(released: Ref[IO, Boolean]) { - + val latency = 3.seconds val doSomething: IO[Unit] = IO.sleep(latency) >> released.get.ifM( IO.unit, - IO.raiseError(new RuntimeException("SomeService already released. Cannot do things")) + IO.raiseError( + new RuntimeException("SomeService already released. Cannot do things") + ) ) } object SomeService { - val make: Resource[IO, SomeService] = Resource.make { - Ref.of[IO, Boolean](true) - } { ref => - ref.set(false) - }.map(SomeService(_)) + val make: Resource[IO, SomeService] = Resource + .make { + Ref.of[IO, Boolean](true) + } { ref => + ref.set(false) + } + .map(SomeService(_)) } - diff --git a/src/test/scala/weaver/pure/UtilitiesTest.scala b/src/test/scala/weaver/pure/UtilitiesTest.scala index dcf4ec2..c29022c 100644 --- a/src/test/scala/weaver/pure/UtilitiesTest.scala +++ b/src/test/scala/weaver/pure/UtilitiesTest.scala @@ -8,7 +8,7 @@ import util._ object UtilitiesTest extends Suite { - override def suitesStream: Stream[IO,Test] = Stream( + override def suitesStream: Stream[IO, Test] = Stream( pureTest("outcomeToString") { val test = Test("some test", expect(1 === 2)) @@ -16,10 +16,8 @@ object UtilitiesTest extends Suite { expect(asString.contains("UtilitiesTest.scala")) and expect(asString.contains("expect(1 === 2)")) - - } + } ) - }