All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weaver.framework.DogFood.scala Maven / Gradle / Ivy

There is a newer version: 0.8.4
Show newest version
package weaver
package framework

import scala.concurrent.duration._
import scala.reflect.ClassTag

import cats.data.Chain
import cats.effect.{ Resource, Sync }
import cats.kernel.Eq
import cats.syntax.all._

import sbt.testing.{ Event => _, Status => _, Task => _, _ }

import Platform._

object DogFood extends DogFoodCompanion {
  type State = (Chain[LoggedEvent], Chain[sbt.testing.Event])
}

// Functionality to test how the frameworks react to successful and failing tests/suites
abstract class DogFood[F[_]](
    val framework: WeaverFramework[F])
    extends DogFoodCompat[F] {
  import framework.unsafeRun._
  import DogFood.State

  // ScalaJS executes asynchronously, therefore we need to wait
  // for some time before getting the logs back. On JVM platform
  // we do not need to wait, since the suite will run synchronously
  private val patience: Option[FiniteDuration] = PlatformCompat.platform match {
    case JS  => 2.seconds.some
    case JVM => none
  }

  def runSuites(
      suites: Seq[Fingerprinted],
      maxParallelism: Int = 512): F[State] =
    for {
      eventHandler <- effect.delay(new MemoryEventHandler())
      logger       <- effect.delay(new MemoryLogger())
      _ <- getTasks(suites, logger).use { case (runner, tasks) =>
        runTasks(runner, eventHandler, logger, maxParallelism)(tasks.toList)
      }
      _      <- patience.fold(effect.unit)(framework.unsafeRun.sleep)
      logs   <- logger.get
      events <- eventHandler.get
    } yield {
      (logs, events)
    }

  // // Method used to run test-suites
  def runSuites(suites: Fingerprinted*): F[State] =
    runSuites(suites)

  // Method used to run a test-suite
  def runSuite(suiteName: String): F[State] =
    runSuites(Fingerprinted.ModuleSuite(suiteName))

  // Method used to run a test-suite
  def runSuite(suite: EffectSuite[F]): F[State] =
    runSuite(suite.getClass.getName.dropRight(1))

  def isSuccess(event: sbt.testing.Event)(
      implicit loc: SourceLocation): Expectations = {
    event.status() match {
      case sbt.testing.Status.Success => Expectations.Helpers.success
      case status =>
        Expectations.Helpers.failure(
          s"${event.fullyQualifiedName()}:${event.selector()} failed with $status")
    }
  }

  private def getTasks(
      suites: Seq[Fingerprinted],
      logger: Logger): Resource[F, (WeaverRunner[F], Array[sbt.testing.Task])] = {
    val acquire = Sync[F].delay {
      val cl = PlatformCompat.getClassLoader(this.getClass())
      framework.weaverRunner(Array(), Array(), cl, None)
    }
    val runner = Resource.make(acquire) { runner =>
      done(runner).void
    }

    runner.evalMap { runner =>
      val taskDefs: Array[TaskDef] = suites.toArray.map { s =>
        new TaskDef(s.fullyQualifiedName,
                    s.fingerprint,
                    true,
                    Array(new SuiteSelector))
      }

      blocker.block(runner -> runner.tasks(taskDefs))
    }
  }

  private def runTasks(
      runner: WeaverRunner[F],
      eventHandler: EventHandler,
      logger: Logger,
      maxParallelism: Int)(
      tasks: List[sbt.testing.Task]): F[Unit] =
    runTasksCompat(runner, eventHandler, logger, maxParallelism)(tasks)

  def globalInit(g: GlobalResourceF[F]): Fingerprinted =
    Fingerprinted.GlobalInit(g.getClass.getName.dropRight(1))
  def moduleSuite(g: EffectSuite[F]): Fingerprinted =
    Fingerprinted.ModuleSuite(g.getClass.getName.dropRight(1))
  def sharingSuite[S <: BaseSuiteClass](
      implicit ct: ClassTag[S]): Fingerprinted =
    Fingerprinted.SharingSuite(ct.runtimeClass.getName())

  sealed trait Fingerprinted {
    import framework.fp

    def fullyQualifiedName: String
    def fingerprint: fp.WeaverFingerprint = {
      import Fingerprinted._
      this match {
        case ModuleSuite(_)  => fp.SuiteFingerprint
        case GlobalInit(_)   => fp.GlobalResourcesFingerprint
        case SharingSuite(_) => fp.ResourceSharingSuiteFingerprint
      }
    }
  }
  private object Fingerprinted {
    case class ModuleSuite(fullyQualifiedName: String)  extends Fingerprinted
    case class GlobalInit(fullyQualifiedName: String)   extends Fingerprinted
    case class SharingSuite(fullyQualifiedName: String) extends Fingerprinted
  }

  private class MemoryLogger() extends Logger {

    val logs = scala.collection.mutable.ListBuffer.empty[LoggedEvent]
    private def add(event: LoggedEvent): Unit = synchronized {
      val _ = logs.append(event)
    }

    override def ansiCodesSupported(): Boolean = false

    override def error(msg: String): Unit =
      add(LoggedEvent.Error(msg))

    override def warn(msg: String): Unit =
      add(LoggedEvent.Warn(msg))

    override def info(msg: String): Unit =
      add(LoggedEvent.Info(msg))

    override def debug(msg: String): Unit =
      add(LoggedEvent.Debug(msg))

    override def trace(t: Throwable): Unit =
      add(LoggedEvent.Trace(t))

    def get: F[Chain[LoggedEvent]] =
      effect.delay(Chain.fromSeq(logs.toList))
  }

  private class MemoryEventHandler() extends EventHandler {
    val events = scala.collection.mutable.ListBuffer.empty[sbt.testing.Event]

    override def handle(event: sbt.testing.Event): Unit = synchronized {
      val _ = events.append(event)
    }

    def get: F[Chain[sbt.testing.Event]] =
      effect.delay(Chain.fromSeq(events.toList))
  }

}

sealed trait LoggedEvent
object LoggedEvent {
  final case class Error(msg: String)  extends LoggedEvent
  final case class Warn(msg: String)   extends LoggedEvent
  final case class Info(msg: String)   extends LoggedEvent
  final case class Debug(msg: String)  extends LoggedEvent
  final case class Trace(t: Throwable) extends LoggedEvent

  implicit val loggedEventEq: Eq[LoggedEvent] = Eq.fromUniversalEquals
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy