diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08d77787..e9faddda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,11 +94,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/0.1') - run: mkdir -p modules/mtl/native/target modules/lightstep-grpc/target modules/log/jvm/target modules/noop/jvm/target modules/mock/target modules/examples/target target modules/log/native/target .js/target modules/core/native/target modules/docs/target modules/lightstep-http/target modules/datadog/target modules/xray/.jvm/target modules/opentracing/target modules/noop/native/target modules/xray/.js/target modules/core/js/target modules/noop/js/target modules/core/jvm/target .jvm/target modules/jaeger/target .native/target modules/opencensus/target modules/honeycomb/target modules/log/js/target modules/mtl/js/target modules/newrelic/target modules/log-odin/target modules/mtl/jvm/target modules/opentelemetry/target modules/lightstep/target project/target + run: mkdir -p modules/mtl/native/target modules/lightstep-grpc/target modules/log/jvm/target modules/noop/jvm/target modules/mock/target modules/examples/target modules/testkit/native/target target modules/log/native/target modules/testkit/js/target .js/target modules/core-tests/js/target modules/core/native/target modules/docs/target modules/lightstep-http/target modules/datadog/target modules/xray/.jvm/target modules/opentracing/target modules/noop/native/target modules/xray/.js/target modules/core/js/target modules/noop/js/target modules/core-tests/jvm/target modules/core/jvm/target .jvm/target modules/jaeger/target .native/target modules/opencensus/target modules/honeycomb/target modules/log/js/target modules/mtl/js/target modules/newrelic/target modules/log-odin/target modules/mtl/jvm/target modules/testkit/jvm/target modules/core-tests/native/target modules/opentelemetry/target modules/lightstep/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/0.1') - run: tar cf targets.tar modules/mtl/native/target modules/lightstep-grpc/target modules/log/jvm/target modules/noop/jvm/target modules/mock/target modules/examples/target target modules/log/native/target .js/target modules/core/native/target modules/docs/target modules/lightstep-http/target modules/datadog/target modules/xray/.jvm/target modules/opentracing/target modules/noop/native/target modules/xray/.js/target modules/core/js/target modules/noop/js/target modules/core/jvm/target .jvm/target modules/jaeger/target .native/target modules/opencensus/target modules/honeycomb/target modules/log/js/target modules/mtl/js/target modules/newrelic/target modules/log-odin/target modules/mtl/jvm/target modules/opentelemetry/target modules/lightstep/target project/target + run: tar cf targets.tar modules/mtl/native/target modules/lightstep-grpc/target modules/log/jvm/target modules/noop/jvm/target modules/mock/target modules/examples/target modules/testkit/native/target target modules/log/native/target modules/testkit/js/target .js/target modules/core-tests/js/target modules/core/native/target modules/docs/target modules/lightstep-http/target modules/datadog/target modules/xray/.jvm/target modules/opentracing/target modules/noop/native/target modules/xray/.js/target modules/core/js/target modules/noop/js/target modules/core-tests/jvm/target modules/core/jvm/target .jvm/target modules/jaeger/target .native/target modules/opencensus/target modules/honeycomb/target modules/log/js/target modules/mtl/js/target modules/newrelic/target modules/log-odin/target modules/mtl/jvm/target modules/testkit/jvm/target modules/core-tests/native/target modules/opentelemetry/target modules/lightstep/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/series/0.1') diff --git a/.mergify.yml b/.mergify.yml index d31aa75b..9101522e 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -29,6 +29,14 @@ pull_request_rules: add: - core remove: [] +- name: Label core-tests PRs + conditions: + - files~=^modules/core-tests/ + actions: + label: + add: + - core-tests + remove: [] - name: Label datadog PRs conditions: - files~=^modules/datadog/ @@ -165,6 +173,14 @@ pull_request_rules: add: - opentracing remove: [] +- name: Label testkit PRs + conditions: + - files~=^modules/testkit/ + actions: + label: + add: + - testkit + remove: [] - name: Label xray PRs conditions: - files~=^modules/xray/ diff --git a/build.sbt b/build.sbt index 5f8966d3..3d9e1f11 100644 --- a/build.sbt +++ b/build.sbt @@ -4,12 +4,12 @@ ThisBuild / tlBaseVersion := "0.3" val scala212Version = "2.12.17" val scala213Version = "2.13.10" -val scala30Version = "3.2.1" +val scala30Version = "3.2.2" val collectionCompatVersion = "2.9.0" val catsVersion = "2.9.0" -val catsEffectVersion = "3.4.5" +val catsEffectVersion = "3.4.6" val fs2Version = "3.5.0" // Publishing @@ -61,10 +61,6 @@ lazy val commonSettings = Seq( ) ) -lazy val commonNativeSettings = Seq( - tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "0.1.7").toMap -) - // Compilation ThisBuild / scalaVersion := scala213Version ThisBuild / crossScalaVersions := Seq(scala212Version, scala213Version, scala30Version) @@ -72,6 +68,7 @@ ThisBuild / githubWorkflowScalaVersions := Seq("2.12", "2.13", "3") lazy val root = tlCrossRootProject.aggregate( core, + coreTests, jaeger, honeycomb, opencensus, @@ -87,6 +84,7 @@ lazy val root = tlCrossRootProject.aggregate( noop, xray, logOdin, + testkit, examples ) @@ -106,7 +104,15 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform) "org.scala-lang.modules" %%% "scala-collection-compat" % collectionCompatVersion ) ) - .nativeSettings(commonNativeSettings) + .nativeSettings( + tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "0.1.7").toMap + ) + +lazy val coreTests = crossProject(JSPlatform, JVMPlatform, NativePlatform) + .in(file("modules/core-tests")) + .dependsOn(core, testkit) + .enablePlugins(AutomateHeaderPlugin, NoPublishPlugin) + .settings(commonSettings) lazy val jaeger = project .in(file("modules/jaeger")) @@ -229,8 +235,8 @@ lazy val datadog = project name := "natchez-datadog", description := "Datadog bindings for Natchez.", libraryDependencies ++= Seq( - "com.datadoghq" % "dd-trace-ot" % "1.5.0", - "com.datadoghq" % "dd-trace-api" % "1.5.0" + "com.datadoghq" % "dd-trace-ot" % "1.6.0", + "com.datadoghq" % "dd-trace-api" % "1.6.0" ) ) @@ -248,7 +254,9 @@ lazy val log = crossProject(JSPlatform, JVMPlatform, NativePlatform) "io.github.cquiroz" %%% "scala-java-time" % "2.5.0" % Test ) ) - .nativeSettings(commonNativeSettings) + .nativeSettings( + tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "0.1.7").toMap + ) lazy val newrelic = project .in(file("modules/newrelic")) @@ -278,7 +286,9 @@ lazy val mtl = crossProject(JSPlatform, JVMPlatform, NativePlatform) "org.typelevel" %%% "cats-mtl" % "1.3.0" ) ) - .nativeSettings(commonNativeSettings) + .nativeSettings( + tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "0.1.7").toMap + ) lazy val noop = crossProject(JSPlatform, JVMPlatform, NativePlatform) .in(file("modules/noop")) @@ -290,7 +300,9 @@ lazy val noop = crossProject(JSPlatform, JVMPlatform, NativePlatform) description := "No-Op Open Tracing implementation", libraryDependencies ++= Seq() ) - .nativeSettings(commonNativeSettings) + .nativeSettings( + tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "0.1.7").toMap + ) lazy val xray = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) @@ -369,6 +381,17 @@ lazy val logOdin = project ) ) +lazy val testkit = crossProject(JSPlatform, JVMPlatform, NativePlatform) + .in(file("modules/testkit")) + .dependsOn(core) + .enablePlugins(AutomateHeaderPlugin) + .settings(commonSettings) + .settings( + name := "natchez-testkit", + description := "In-memory Natchez implementation that is useful for testing", + tlVersionIntroduced := List("2.12", "2.13", "3").map(_ -> "0.3.1").toMap + ) + lazy val docs = project .in(file("modules/docs")) .dependsOn(mtl.jvm, honeycomb, datadog, jaeger, log.jvm, opentelemetry) diff --git a/modules/core/shared/src/test/scala/ImplicitResolutionTest.scala b/modules/core-tests/shared/src/test/scala/ImplicitResolutionTest.scala similarity index 100% rename from modules/core/shared/src/test/scala/ImplicitResolutionTest.scala rename to modules/core-tests/shared/src/test/scala/ImplicitResolutionTest.scala diff --git a/modules/core-tests/shared/src/test/scala/InMemorySuite.scala b/modules/core-tests/shared/src/test/scala/InMemorySuite.scala new file mode 100644 index 00000000..3610057b --- /dev/null +++ b/modules/core-tests/shared/src/test/scala/InMemorySuite.scala @@ -0,0 +1,59 @@ +// Copyright (c) 2019-2020 by Rob Norris and Contributors +// This software is licensed under the MIT License (MIT). +// For more information see LICENSE or https://opensource.org/licenses/MIT + +package natchez + +import cats.data.Kleisli +import cats.effect.{IO, MonadCancelThrow} +import munit.CatsEffectSuite +import natchez.InMemory.Lineage.defaultRootName + +trait InMemorySuite extends CatsEffectSuite { + type Lineage = InMemory.Lineage + val Lineage = InMemory.Lineage + type NatchezCommand = InMemory.NatchezCommand + val NatchezCommand = InMemory.NatchezCommand + + trait TraceTest { + def program[F[_]: MonadCancelThrow: Trace]: F[Unit] + def expectedHistory: List[(Lineage, NatchezCommand)] + } + + def traceTest(name: String, tt: TraceTest): Unit = { + test(s"$name - Kleisli")( + testTraceKleisli(tt.program[Kleisli[IO, Span[IO], *]](implicitly, _), tt.expectedHistory) + ) + test(s"$name - IOLocal")(testTraceIoLocal(tt.program[IO](implicitly, _), tt.expectedHistory)) + } + + def testTraceKleisli( + traceProgram: Trace[Kleisli[IO, Span[IO], *]] => Kleisli[IO, Span[IO], Unit], + expectedHistory: List[(Lineage, NatchezCommand)] + ): IO[Unit] = testTrace[Kleisli[IO, Span[IO], *]]( + traceProgram, + root => IO.pure(Trace[Kleisli[IO, Span[IO], *]] -> (k => k.run(root))), + expectedHistory + ) + + def testTraceIoLocal( + traceProgram: Trace[IO] => IO[Unit], + expectedHistory: List[(Lineage, NatchezCommand)] + ): IO[Unit] = testTrace[IO](traceProgram, Trace.ioTrace(_).map(_ -> identity), expectedHistory) + + def testTrace[F[_]]( + traceProgram: Trace[F] => F[Unit], + makeTraceAndResolver: Span[IO] => IO[(Trace[F], F[Unit] => IO[Unit])], + expectedHistory: List[(Lineage, NatchezCommand)] + ): IO[Unit] = + InMemory.EntryPoint.create[IO].flatMap { ep => + val traced = ep.root(defaultRootName).use { r => + makeTraceAndResolver(r).flatMap { case (traceInstance, resolve) => + resolve(traceProgram(traceInstance)) + } + } + traced *> ep.ref.get.map { history => + assertEquals(history.toList, expectedHistory) + } + } +} diff --git a/modules/core/shared/src/test/scala/SpanCoalesceTest.scala b/modules/core-tests/shared/src/test/scala/SpanCoalesceTest.scala similarity index 100% rename from modules/core/shared/src/test/scala/SpanCoalesceTest.scala rename to modules/core-tests/shared/src/test/scala/SpanCoalesceTest.scala diff --git a/modules/core/shared/src/test/scala/SpanPropagationTest.scala b/modules/core-tests/shared/src/test/scala/SpanPropagationTest.scala similarity index 100% rename from modules/core/shared/src/test/scala/SpanPropagationTest.scala rename to modules/core-tests/shared/src/test/scala/SpanPropagationTest.scala diff --git a/modules/core/shared/src/test/scala/TraceValueTest.scala b/modules/core-tests/shared/src/test/scala/TraceValueTest.scala similarity index 100% rename from modules/core/shared/src/test/scala/TraceValueTest.scala rename to modules/core-tests/shared/src/test/scala/TraceValueTest.scala diff --git a/modules/core/shared/src/test/scala/InMemory.scala b/modules/testkit/shared/src/main/scala/InMemory.scala similarity index 55% rename from modules/core/shared/src/test/scala/InMemory.scala rename to modules/testkit/shared/src/main/scala/InMemory.scala index 23e30475..a77926ea 100644 --- a/modules/core/shared/src/test/scala/InMemory.scala +++ b/modules/testkit/shared/src/main/scala/InMemory.scala @@ -4,41 +4,41 @@ package natchez -import java.net.URI - -import cats.data.{Chain, Kleisli} -import cats.effect.{IO, MonadCancelThrow, Ref, Resource} - +import cats._ +import cats.data._ +import cats.effect.{Trace => _, _} +import cats.syntax.all._ import natchez.Span.Options -import munit.CatsEffectSuite + +import java.net.URI object InMemory { - class Span( + class Span[F[_]: Applicative]( lineage: Lineage, k: Kernel, - ref: Ref[IO, Chain[(Lineage, NatchezCommand)]], + ref: Ref[F, Chain[(Lineage, NatchezCommand)]], val options: Options - ) extends natchez.Span.Default[IO] { + ) extends natchez.Span.Default[F] { override protected val spanCreationPolicyOverride: Options.SpanCreationPolicy = options.spanCreationPolicy - def put(fields: (String, natchez.TraceValue)*): IO[Unit] = + def put(fields: (String, natchez.TraceValue)*): F[Unit] = ref.update(_.append(lineage -> NatchezCommand.Put(fields.toList))) - def attachError(err: Throwable, fields: (String, TraceValue)*): IO[Unit] = + def attachError(err: Throwable, fields: (String, TraceValue)*): F[Unit] = ref.update(_.append(lineage -> NatchezCommand.AttachError(err, fields.toList))) - def log(event: String): IO[Unit] = + def log(event: String): F[Unit] = ref.update(_.append(lineage -> NatchezCommand.LogEvent(event))) - def log(fields: (String, TraceValue)*): IO[Unit] = + def log(fields: (String, TraceValue)*): F[Unit] = ref.update(_.append(lineage -> NatchezCommand.LogFields(fields.toList))) - def kernel: IO[Kernel] = + def kernel: F[Kernel] = ref.update(_.append(lineage -> NatchezCommand.AskKernel(k))).as(k) - def makeSpan(name: String, options: Options): Resource[IO, natchez.Span[IO]] = { + def makeSpan(name: String, options: Options): Resource[F, natchez.Span[F]] = { val acquire = ref .update(_.append(lineage -> NatchezCommand.CreateSpan(name, options.parentKernel, options))) .as(new Span(lineage / name, k, ref, options)) @@ -48,44 +48,44 @@ object InMemory { Resource.make(acquire)(_ => release) } - def traceId: IO[Option[String]] = + def traceId: F[Option[String]] = ref.update(_.append(lineage -> NatchezCommand.AskTraceId)).as(None) - def spanId: IO[Option[String]] = + def spanId: F[Option[String]] = ref.update(_.append(lineage -> NatchezCommand.AskSpanId)).as(None) - def traceUri: IO[Option[URI]] = + def traceUri: F[Option[URI]] = ref.update(_.append(lineage -> NatchezCommand.AskTraceUri)).as(None) } - class EntryPoint(val ref: Ref[IO, Chain[(Lineage, NatchezCommand)]]) - extends natchez.EntryPoint[IO] { + class EntryPoint[F[_]: Applicative](val ref: Ref[F, Chain[(Lineage, NatchezCommand)]]) + extends natchez.EntryPoint[F] { - override def root(name: String, options: natchez.Span.Options): Resource[IO, Span] = + override def root(name: String, options: natchez.Span.Options): Resource[F, Span[F]] = newSpan(name, Kernel(Map.empty), options) override def continue( name: String, kernel: Kernel, options: natchez.Span.Options - ): Resource[IO, Span] = + ): Resource[F, Span[F]] = newSpan(name, kernel, options) override def continueOrElseRoot( name: String, kernel: Kernel, options: natchez.Span.Options - ): Resource[IO, Span] = + ): Resource[F, Span[F]] = newSpan(name, kernel, options) private def newSpan( name: String, kernel: Kernel, options: natchez.Span.Options - ): Resource[IO, Span] = { + ): Resource[F, Span[F]] = { val acquire = ref .update(_.append(Lineage.Root -> NatchezCommand.CreateRootSpan(name, kernel, options))) - .as(new Span(Lineage.Root, kernel, ref, options)) + .as(new Span(Lineage.Root(name), kernel, ref, options)) val release = ref.update(_.append(Lineage.Root -> NatchezCommand.ReleaseRootSpan(name))) @@ -94,15 +94,17 @@ object InMemory { } object EntryPoint { - def create: IO[EntryPoint] = - Ref.of[IO, Chain[(Lineage, NatchezCommand)]](Chain.empty).map(log => new EntryPoint(log)) + def create[F[_]: Concurrent]: F[EntryPoint[F]] = + Ref.of[F, Chain[(Lineage, NatchezCommand)]](Chain.empty).map(log => new EntryPoint(log)) } sealed trait Lineage { def /(name: String): Lineage.Child = Lineage.Child(name, this) } object Lineage { - case object Root extends Lineage + val defaultRootName = "root" + case class Root(name: String) extends Lineage + object Root extends Root(defaultRootName) final case class Child(name: String, parent: Lineage) extends Lineage } @@ -127,52 +129,3 @@ object InMemory { } } - -trait InMemorySuite extends CatsEffectSuite { - type Lineage = InMemory.Lineage - val Lineage = InMemory.Lineage - type NatchezCommand = InMemory.NatchezCommand - val NatchezCommand = InMemory.NatchezCommand - - trait TraceTest { - def program[F[_]: MonadCancelThrow: Trace]: F[Unit] - def expectedHistory: List[(Lineage, NatchezCommand)] - } - - def traceTest(name: String, tt: TraceTest) = { - test(s"$name - Kleisli")( - testTraceKleisli(tt.program[Kleisli[IO, Span[IO], *]](implicitly, _), tt.expectedHistory) - ) - test(s"$name - IOLocal")(testTraceIoLocal(tt.program[IO](implicitly, _), tt.expectedHistory)) - } - - def testTraceKleisli( - traceProgram: Trace[Kleisli[IO, Span[IO], *]] => Kleisli[IO, Span[IO], Unit], - expectedHistory: List[(Lineage, NatchezCommand)] - ) = testTrace[Kleisli[IO, Span[IO], *]]( - traceProgram, - root => IO.pure(Trace[Kleisli[IO, Span[IO], *]] -> (k => k.run(root))), - expectedHistory - ) - - def testTraceIoLocal( - traceProgram: Trace[IO] => IO[Unit], - expectedHistory: List[(Lineage, NatchezCommand)] - ) = testTrace[IO](traceProgram, Trace.ioTrace(_).map(_ -> identity), expectedHistory) - - def testTrace[F[_]]( - traceProgram: Trace[F] => F[Unit], - makeTraceAndResolver: Span[IO] => IO[(Trace[F], F[Unit] => IO[Unit])], - expectedHistory: List[(Lineage, NatchezCommand)] - ) = - InMemory.EntryPoint.create.flatMap { ep => - val traced = ep.root("root").use { r => - makeTraceAndResolver(r).flatMap { case (traceInstance, resolve) => - resolve(traceProgram(traceInstance)) - } - } - traced *> ep.ref.get.map { history => - assertEquals(history.toList, expectedHistory) - } - } -} diff --git a/project/plugins.sbt b/project/plugins.sbt index d25250c2..88b60994 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,6 +5,6 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.4.1") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.3") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.0") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.9") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.10") addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.2.0") -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.6") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7")