diff --git a/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/Log.scala b/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/Log.scala index 908d13d1..5871f8df 100644 --- a/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/Log.scala +++ b/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/Log.scala @@ -40,15 +40,17 @@ trait Log[F[*]] { def canLog: CanLog - def log[A](fa: F[A])(toLeveledMessage: A => LogMessage with NotIgnorable): F[A] = + def log[A](fa: F[A])(toLeveledMessage: A => LogMessage with MaybeIgnorable): F[A] = flatMap0(fa) { a => toLeveledMessage(a) match { case LogMessage.LeveledMessage(message, level) => flatMap0(EF.effectOf(canLog.getLogger(level)(message())))(_ => EF.pureOf(a)) + case LogMessage.Ignore => + EF.pureOf(a) } } - def log_[A](fa: F[A])(toLeveledMessage: A => LogMessage with NotIgnorable): F[Unit] = + def log_[A](fa: F[A])(toLeveledMessage: A => LogMessage with MaybeIgnorable): F[Unit] = map0(log(fa)(toLeveledMessage))(_ => ()) def logS( diff --git a/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/syntax/LogSyntax.scala b/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/syntax/LogSyntax.scala index 4153f168..f66d0927 100644 --- a/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/syntax/LogSyntax.scala +++ b/modules/logger-f-core/shared/src/main/scala-2/loggerf/core/syntax/LogSyntax.scala @@ -12,12 +12,12 @@ trait LogSyntax { import LogSyntax._ - @inline def log[F[*], A](fa: F[A])(toLeveledMessage: A => LogMessage with NotIgnorable)( + @inline def log[F[*], A](fa: F[A])(toLeveledMessage: A => LogMessage with MaybeIgnorable)( implicit L: Log[F] ): F[A] = L.log(fa)(toLeveledMessage) - @inline def log_[F[*], A](fa: F[A])(toLeveledMessage: A => LogMessage with NotIgnorable)( + @inline def log_[F[*], A](fa: F[A])(toLeveledMessage: A => LogMessage with MaybeIgnorable)( implicit L: Log[F] ): F[Unit] = L.log_(fa)(toLeveledMessage) @@ -93,10 +93,10 @@ trait LogSyntax { object LogSyntax extends LogSyntax { final class LogFOfASyntax[F[*], A](private val fa: F[A]) extends AnyVal { - @inline def log(toLeveledMessage: A => LogMessage with NotIgnorable)(implicit L: Log[F]): F[A] = + @inline def log(toLeveledMessage: A => LogMessage with MaybeIgnorable)(implicit L: Log[F]): F[A] = LogSyntax.log(fa)(toLeveledMessage) - @inline def log_(toLeveledMessage: A => LogMessage with NotIgnorable)(implicit L: Log[F]): F[Unit] = + @inline def log_(toLeveledMessage: A => LogMessage with MaybeIgnorable)(implicit L: Log[F]): F[Unit] = LogSyntax.log_(fa)(toLeveledMessage) } diff --git a/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/Log.scala b/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/Log.scala index 93251f8d..44f9163a 100644 --- a/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/Log.scala +++ b/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/Log.scala @@ -19,6 +19,18 @@ import scala.annotation.implicitNotFound // or import loggerf.instances.cats.logF --- + ----- + If this doesn't solve, you probably need a CanLog instance. + To create it, please check out the following document. + + https://logger-f.kevinly.dev/docs/cats/import#canlog-logger + + ----- + If it doesn't solve, it's probably because of missing an Fx[F] instance. + + You can simply import the Fx[F] instance of your effect library. + Please check out the message of @implicitNotFound annotation on effectie.core.Fx. + """ ) trait Log[F[*]] { @@ -31,15 +43,17 @@ trait Log[F[*]] { def canLog: CanLog - def log[A](fa: F[A])(toLeveledMessage: A => LeveledMessage): F[A] = + def log[A](fa: F[A])(toLeveledMessage: A => LeveledMessage | Ignore.type): F[A] = flatMap0(fa) { a => toLeveledMessage(a) match { case LeveledMessage(message, level) => flatMap0(EF.effectOf(canLog.getLogger(level)(message())))(_ => EF.pureOf(a)) + case Ignore => + EF.pureOf(a) } } - def log_[A](fa: F[A])(toLeveledMessage: A => LeveledMessage): F[Unit] = + def log_[A](fa: F[A])(toLeveledMessage: A => LeveledMessage | Ignore.type): F[Unit] = map0(log(fa)(toLeveledMessage))(_ => ()) def logS(message: => String)(toLeveledMessage: (String => LeveledMessage) with LeveledMessage.Leveled): F[String] = diff --git a/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/syntax/LogSyntax.scala b/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/syntax/LogSyntax.scala index 536fc39f..3f451d4e 100644 --- a/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/syntax/LogSyntax.scala +++ b/modules/logger-f-core/shared/src/main/scala-3/loggerf/core/syntax/LogSyntax.scala @@ -13,10 +13,10 @@ import scala.concurrent.Future trait LogSyntax { extension [F[*], A](fa: F[A]) { - def log(toLeveledMessage: A => LeveledMessage)(using L: Log[F]): F[A] = + def log(toLeveledMessage: A => LeveledMessage | Ignore.type)(using L: Log[F]): F[A] = L.log(fa)(toLeveledMessage) - def log_(toLeveledMessage: A => LeveledMessage)(using L: Log[F]): F[Unit] = + def log_(toLeveledMessage: A => LeveledMessage | Ignore.type)(using L: Log[F]): F[Unit] = L.log_(fa)(toLeveledMessage) } diff --git a/modules/logger-f-core/shared/src/test/scala/loggerf/instances/instancesSpec.scala b/modules/logger-f-core/shared/src/test/scala/loggerf/instances/instancesSpec.scala index 2ea727dc..fc0fb431 100644 --- a/modules/logger-f-core/shared/src/test/scala/loggerf/instances/instancesSpec.scala +++ b/modules/logger-f-core/shared/src/test/scala/loggerf/instances/instancesSpec.scala @@ -17,13 +17,18 @@ import java.util.concurrent.ExecutorService import scala.concurrent.duration._ import scala.concurrent.{ExecutionContext, Future} +import loggerf.test_data +import loggerf.test_data.TestCases + /** @author Kevin Lee * @since 2022-02-09 */ object instancesSpec extends Properties { override def tests: List[Test] = List( property("test Log.log(F[A])", testLogFA), + property("test Log.log(F[A]) with matching cases", testLogFAWithMatchingCases), property("test Log.log_(F[A])", testLog_FA), + property("test Log.log_(F[A]) with matching cases", testLog_FAWithMatchingCases), property("test Log.logS(String)", testLogS), property("test Log.logS_(String)", testLogS_()), property("test Log.log(F[Option[A]])", testLogFOptionA), @@ -56,9 +61,13 @@ object instancesSpec extends Properties { def runLog[F[*]: Fx: Log: Monad]: F[Unit] = (for { _ <- Log[F].log(Fx[F].effectOf(debugMsg))(debug) + _ <- Log[F].log(Fx[F].effectOf(debugMsg))(ignoreA) _ <- Log[F].log(Fx[F].effectOf(infoMsg))(info) + _ <- Log[F].log(Fx[F].effectOf(infoMsg))(ignoreA) _ <- Log[F].log(Fx[F].effectOf(warnMsg))(warn) + _ <- Log[F].log(Fx[F].effectOf(warnMsg))(ignoreA) _ <- Log[F].log(Fx[F].effectOf(errorMsg))(error) + _ <- Log[F].log(Fx[F].effectOf(errorMsg))(ignoreA) } yield ()) val expected = LoggerForTesting( @@ -81,6 +90,69 @@ object instancesSpec extends Properties { } + def testLogFAWithMatchingCases: Property = for { + debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") + infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") + warnMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("warnMsg") + errorMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("errorMsg") + testCases <- test_data.Gens.genTestCases.log("testCases") + } yield { + + implicit val logger: LoggerForTesting = LoggerForTesting() + + def runLog[F[*]: Fx: Log: Monad]: F[Unit] = { + val fa1 = Log[F].log(Fx[F].effectOf(testCases)) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + debug(s"$debugMsg / id=${id.toString}, name=$name, not enabled") + } + val fa2 = Log[F].log(fa1) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + info(s"$infoMsg / id=${id.toString}, name=$name, not enabled") + } + val fa3 = Log[F].log(fa2) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + warn(s"$warnMsg / id=${id.toString}, name=$name, not enabled") + } + val fa4 = Log[F].log(fa3) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + error(s"$errorMsg / id=${id.toString}, name=$name, not enabled") + } + fa4 *> Fx[F].unitOf + + } + + val expected = + if (testCases.enabled) + LoggerForTesting() + else + LoggerForTesting( + debugMessages = Vector(s"$debugMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + infoMessages = Vector(s"$infoMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + warnMessages = Vector(s"$warnMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + errorMessages = Vector(s"$errorMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + ) + + implicit val es: ExecutorService = ConcurrentSupport.newExecutorService(2) + implicit val ec: ExecutionContext = + ConcurrentSupport.newExecutionContextWithLogger(es, ErrorLogger.printlnExecutionContextErrorLogger) + + ConcurrentSupport.futureToValueAndTerminate(es, waitFor400Millis) { + + runLog[Future] + + } + logger ==== expected + + } + def testLog_FA: Property = for { debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") @@ -93,9 +165,13 @@ object instancesSpec extends Properties { def runLog[F[*]: Fx: Log: Monad]: F[Unit] = Log[F] .log_(Fx[F].effectOf(debugMsg))(debug) + .flatMap { _ => Log[F].log_(Fx[F].effectOf(debugMsg))(ignoreA) } .flatMap { _ => Log[F].log_(Fx[F].effectOf(infoMsg))(info) } + .flatMap { _ => Log[F].log_(Fx[F].effectOf(infoMsg))(ignoreA) } .flatMap { _ => Log[F].log_(Fx[F].effectOf(warnMsg))(warn) } + .flatMap { _ => Log[F].log_(Fx[F].effectOf(warnMsg))(ignoreA) } .flatMap { _ => Log[F].log_(Fx[F].effectOf(errorMsg))(error) } + .flatMap { _ => Log[F].log_(Fx[F].effectOf(errorMsg))(ignoreA) } val expected = LoggerForTesting( debugMessages = Vector(debugMsg), @@ -117,6 +193,65 @@ object instancesSpec extends Properties { } + def testLog_FAWithMatchingCases: Property = for { + debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") + infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") + warnMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("warnMsg") + errorMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("errorMsg") + testCases <- test_data.Gens.genTestCases.log("testCases") + } yield { + + implicit val logger: LoggerForTesting = LoggerForTesting() + + def runLog[F[*]: Fx: Log: Monad]: F[Unit] = { + val fa = Fx[F].effectOf(testCases) + Log[F].log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + debug(s"$debugMsg / id=${id.toString}, name=$name, not enabled") + } *> Log[F].log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + info(s"$infoMsg / id=${id.toString}, name=$name, not enabled") + } *> Log[F].log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + warn(s"$warnMsg / id=${id.toString}, name=$name, not enabled") + } *> Log[F].log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + error(s"$errorMsg / id=${id.toString}, name=$name, not enabled") + } + } + + val expected = + if (testCases.enabled) + LoggerForTesting() + else + LoggerForTesting( + debugMessages = Vector(s"$debugMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + infoMessages = Vector(s"$infoMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + warnMessages = Vector(s"$warnMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + errorMessages = Vector(s"$errorMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + ) + + implicit val es: ExecutorService = ConcurrentSupport.newExecutorService(2) + implicit val ec: ExecutionContext = + ConcurrentSupport.newExecutionContextWithLogger(es, ErrorLogger.printlnExecutionContextErrorLogger) + + ConcurrentSupport.futureToValueAndTerminate(es, waitFor400Millis) { + + runLog[Future] + + } + logger ==== expected + + } + def testLogS: Property = for { debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") diff --git a/modules/logger-f-core/shared/src/test/scala/loggerf/instances/syntaxSpec.scala b/modules/logger-f-core/shared/src/test/scala/loggerf/instances/syntaxSpec.scala index 5aad195b..bf558d68 100644 --- a/modules/logger-f-core/shared/src/test/scala/loggerf/instances/syntaxSpec.scala +++ b/modules/logger-f-core/shared/src/test/scala/loggerf/instances/syntaxSpec.scala @@ -11,6 +11,8 @@ import hedgehog.runner._ import loggerf.core.Log import loggerf.core.syntax.all._ import loggerf.logger._ +import loggerf.test_data +import loggerf.test_data.TestCases import java.util.concurrent.ExecutorService import scala.concurrent.duration._ @@ -22,7 +24,9 @@ import scala.concurrent.{ExecutionContext, Future} object syntaxSpec extends Properties { override def tests: List[Test] = List( property("test log(F[A])", testLogFA), + property("test log(F[A]) with matching cases", testLogFAWithMatchingCases), property("test log_(F[A])", testLog_FA), + property("test log_(F[A]) with matching cases", testLog_FAWithMatchingCases), property("test log(String)", testLogString), property("test log_(String)", testLog_String), property("test log(F[Option[A]])", testLogFOptionA), @@ -39,7 +43,9 @@ object syntaxSpec extends Properties { property("test log_(F[Either[A, B]])(ignore, message)", testLog_FEitherABIgnoreRight), ) ++ List( property("test F[A].log", LogExtensionSpec.testFALog), + property("test F[A].log with matching cases", LogExtensionSpec.testFALogWithMatchingCases), property("test F[A].log_", LogExtensionSpec.testFALog_()), + property("test F[A].log_ with matching cases", LogExtensionSpec.testFALog_WithMatchingCases()), property("test String.logS", LogExtensionSpec.testStringLog), property("test String.logS_", LogExtensionSpec.testStringLog_()), property("test F[Option[A]].log", LogExtensionSpec.testFOptionALog), @@ -72,9 +78,13 @@ object syntaxSpec extends Properties { def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = (for { _ <- log(FxCtor[F].effectOf(debugMsg))(debug) + _ <- log(FxCtor[F].effectOf(debugMsg))(ignoreA) _ <- log(FxCtor[F].effectOf(infoMsg))(info) + _ <- log(FxCtor[F].effectOf(infoMsg))(ignoreA) _ <- log(FxCtor[F].effectOf(warnMsg))(warn) + _ <- log(FxCtor[F].effectOf(warnMsg))(ignoreA) _ <- log(FxCtor[F].effectOf(errorMsg))(error) + _ <- log(FxCtor[F].effectOf(errorMsg))(ignoreA) } yield ()) val expected = LoggerForTesting( @@ -96,6 +106,67 @@ object syntaxSpec extends Properties { logger ==== expected } + def testLogFAWithMatchingCases: Property = for { + debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") + infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") + warnMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("warnMsg") + errorMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("errorMsg") + testCases <- test_data.Gens.genTestCases.log("testCases") + } yield { + + implicit val logger: LoggerForTesting = LoggerForTesting() + + def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = + (for { + _ <- log(FxCtor[F].effectOf(testCases)) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + debug(s"$debugMsg / id=${id.toString}, name=$name, not enabled") + } + _ <- log(FxCtor[F].effectOf(testCases)) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + info(s"$infoMsg / id=${id.toString}, name=$name, not enabled") + } + _ <- log(FxCtor[F].effectOf(testCases)) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + warn(s"$warnMsg / id=${id.toString}, name=$name, not enabled") + } + _ <- log(FxCtor[F].effectOf(testCases)) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + error(s"$errorMsg / id=${id.toString}, name=$name, not enabled") + } + } yield ()) + + val expected = + if (testCases.enabled) + LoggerForTesting() + else + LoggerForTesting( + debugMessages = Vector(s"$debugMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + infoMessages = Vector(s"$infoMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + warnMessages = Vector(s"$warnMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + errorMessages = Vector(s"$errorMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + ) + + implicit val es: ExecutorService = ConcurrentSupport.newExecutorService(2) + implicit val ec: ExecutionContext = + ConcurrentSupport.newExecutionContextWithLogger(es, ErrorLogger.printlnExecutionContextErrorLogger) + + ConcurrentSupport.futureToValueAndTerminate(es, waitFor300Millis) { + import loggerf.instances.future.logFuture + runLog[Future] + } + + logger ==== expected + } + def testLog_FA: Property = for { debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") @@ -107,9 +178,13 @@ object syntaxSpec extends Properties { def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = log_(FxCtor[F].effectOf(debugMsg))(debug) + .flatMap { _ => log_(FxCtor[F].effectOf(debugMsg))(ignoreA) } .flatMap { _ => log_(FxCtor[F].effectOf(infoMsg))(info) } + .flatMap { _ => log_(FxCtor[F].effectOf(infoMsg))(ignoreA) } .flatMap { _ => log_(FxCtor[F].effectOf(warnMsg))(warn) } + .flatMap { _ => log_(FxCtor[F].effectOf(warnMsg))(ignoreA) } .flatMap { _ => log_(FxCtor[F].effectOf(errorMsg))(error) } + .flatMap { _ => log_(FxCtor[F].effectOf(errorMsg))(ignoreA) } val expected = LoggerForTesting( debugMessages = Vector(debugMsg), @@ -130,6 +205,73 @@ object syntaxSpec extends Properties { logger ==== expected } + def testLog_FAWithMatchingCases: Property = for { + debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") + infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") + warnMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("warnMsg") + errorMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("errorMsg") + testCases <- test_data.Gens.genTestCases.log("testCases") + } yield { + + implicit val logger: LoggerForTesting = LoggerForTesting() + + def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = { + val fa = FxCtor[F].effectOf(testCases) + log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + debug(s"$debugMsg / id=${id.toString}, name=$name, not enabled") + } + .flatMap { _ => + log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + info(s"$infoMsg / id=${id.toString}, name=$name, not enabled") + } + } + .flatMap { _ => + log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + warn(s"$warnMsg / id=${id.toString}, name=$name, not enabled") + } + } + .flatMap { _ => + log_(fa) { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + error(s"$errorMsg / id=${id.toString}, name=$name, not enabled") + } + } + } + + val expected = + if (testCases.enabled) + LoggerForTesting() + else + LoggerForTesting( + debugMessages = Vector(s"$debugMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + infoMessages = Vector(s"$infoMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + warnMessages = Vector(s"$warnMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + errorMessages = Vector(s"$errorMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + ) + + implicit val es: ExecutorService = ConcurrentSupport.newExecutorService(2) + implicit val ec: ExecutionContext = + ConcurrentSupport.newExecutionContextWithLogger(es, ErrorLogger.printlnExecutionContextErrorLogger) + + ConcurrentSupport.futureToValueAndTerminate(es, waitFor300Millis) { + import loggerf.instances.future.logFuture + runLog[Future] + } + + logger ==== expected + } + def testLogString: Property = for { debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") @@ -761,9 +903,13 @@ object syntaxSpec extends Properties { def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = (for { _ <- FxCtor[F].effectOf(debugMsg).log(debug) + _ <- FxCtor[F].effectOf(debugMsg).log(ignoreA) _ <- FxCtor[F].effectOf(infoMsg).log(info) + _ <- FxCtor[F].effectOf(infoMsg).log(ignoreA) _ <- FxCtor[F].effectOf(warnMsg).log(warn) + _ <- FxCtor[F].effectOf(warnMsg).log(ignoreA) _ <- FxCtor[F].effectOf(errorMsg).log(error) + _ <- FxCtor[F].effectOf(errorMsg).log(ignoreA) } yield ()) val expected = LoggerForTesting( @@ -785,6 +931,67 @@ object syntaxSpec extends Properties { logger ==== expected } + def testFALogWithMatchingCases: Property = for { + debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") + infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") + warnMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("warnMsg") + errorMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("errorMsg") + testCases <- test_data.Gens.genTestCases.log("testCases") + } yield { + + implicit val logger: LoggerForTesting = LoggerForTesting() + + def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = + (for { + _ <- FxCtor[F].effectOf(testCases).log { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + debug(s"$debugMsg / id=${id.toString}, name=$name, not enabled") + } + _ <- FxCtor[F].effectOf(testCases).log { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + info(s"$infoMsg / id=${id.toString}, name=$name, not enabled") + } + _ <- FxCtor[F].effectOf(testCases).log { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + warn(s"$warnMsg / id=${id.toString}, name=$name, not enabled") + } + _ <- FxCtor[F].effectOf(testCases).log { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + error(s"$errorMsg / id=${id.toString}, name=$name, not enabled") + } + } yield ()) + + val expected = + if (testCases.enabled) + LoggerForTesting() + else + LoggerForTesting( + debugMessages = Vector(s"$debugMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + infoMessages = Vector(s"$infoMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + warnMessages = Vector(s"$warnMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + errorMessages = Vector(s"$errorMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + ) + + implicit val es: ExecutorService = ConcurrentSupport.newExecutorService(2) + implicit val ec: ExecutionContext = + ConcurrentSupport.newExecutionContextWithLogger(es, ErrorLogger.printlnExecutionContextErrorLogger) + + ConcurrentSupport.futureToValueAndTerminate(es, waitFor300Millis) { + import loggerf.instances.future.logFuture + runLog[Future] + } + + logger ==== expected + } + def testFALog_(): Property = for { debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") @@ -799,9 +1006,13 @@ object syntaxSpec extends Properties { FxCtor[F] .effectOf(debugMsg) .log_(debug) + .flatMap { _ => FxCtor[F].effectOf(debugMsg).log_(ignoreA) } .flatMap { _ => FxCtor[F].effectOf(infoMsg).log_(info) } + .flatMap { _ => FxCtor[F].effectOf(infoMsg).log_(ignoreA) } .flatMap { _ => FxCtor[F].effectOf(warnMsg).log_(warn) } + .flatMap { _ => FxCtor[F].effectOf(warnMsg).log_(ignoreA) } .flatMap { _ => FxCtor[F].effectOf(errorMsg).log_(error) } + .flatMap { _ => FxCtor[F].effectOf(errorMsg).log_(ignoreA) } val expected = LoggerForTesting( debugMessages = Vector(debugMsg), @@ -822,6 +1033,72 @@ object syntaxSpec extends Properties { logger ==== expected } + def testFALog_WithMatchingCases(): Property = + for { + debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") + infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") + warnMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("warnMsg") + errorMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("errorMsg") + testCases <- test_data.Gens.genTestCases.log("testCases") + } yield { + + implicit val logger: LoggerForTesting = LoggerForTesting() + + def runLog[F[*]: FxCtor: Log: Monad]: F[Unit] = { + val fa = FxCtor[F].effectOf(testCases) + + fa.log_ { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + debug(s"$debugMsg / id=${id.toString}, name=$name, not enabled") + }.flatMap { _ => + fa.log_ { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + info(s"$infoMsg / id=${id.toString}, name=$name, not enabled") + } + }.flatMap { _ => + fa.log_ { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + warn(s"$warnMsg / id=${id.toString}, name=$name, not enabled") + } + }.flatMap { _ => + fa.log_ { + case TestCases(_, _, true) => + ignore + case TestCases(id, name, false) => + error(s"$errorMsg / id=${id.toString}, name=$name, not enabled") + } + } + } + + val expected = + if (testCases.enabled) + LoggerForTesting() + else + LoggerForTesting( + debugMessages = Vector(s"$debugMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + infoMessages = Vector(s"$infoMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + warnMessages = Vector(s"$warnMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + errorMessages = Vector(s"$errorMsg / id=${testCases.id.toString}, name=${testCases.name}, not enabled"), + ) + + implicit val es: ExecutorService = ConcurrentSupport.newExecutorService(2) + implicit val ec: ExecutionContext = + ConcurrentSupport.newExecutionContextWithLogger(es, ErrorLogger.printlnExecutionContextErrorLogger) + + ConcurrentSupport.futureToValueAndTerminate(es, waitFor300Millis) { + import loggerf.instances.future.logFuture + runLog[Future] + } + + logger ==== expected + } + def testStringLog: Property = for { debugMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("debugMsg") infoMsg <- Gen.string(Gen.unicode, Range.linear(1, 20)).log("infoMsg") diff --git a/modules/logger-f-core/shared/src/test/scala/loggerf/test_data/Gens.scala b/modules/logger-f-core/shared/src/test/scala/loggerf/test_data/Gens.scala new file mode 100644 index 00000000..528d9c08 --- /dev/null +++ b/modules/logger-f-core/shared/src/test/scala/loggerf/test_data/Gens.scala @@ -0,0 +1,15 @@ +package loggerf.test_data + +import hedgehog._ + +/** @author Kevin Lee + * @since 2023-11-07 + */ +object Gens { + def genTestCases: Gen[TestCases] = + for { + id <- Gen.int(Range.linear(1, Int.MaxValue)) + name <- Gen.string(Gen.alphaNum, Range.linear(1, 10)) + enabled <- Gen.boolean + } yield TestCases(id, name, enabled) +} diff --git a/modules/logger-f-core/shared/src/test/scala/loggerf/test_data/TestCases.scala b/modules/logger-f-core/shared/src/test/scala/loggerf/test_data/TestCases.scala new file mode 100644 index 00000000..8d88c9ff --- /dev/null +++ b/modules/logger-f-core/shared/src/test/scala/loggerf/test_data/TestCases.scala @@ -0,0 +1,6 @@ +package loggerf.test_data + +/** @author Kevin Lee + * @since 2023-11-06 + */ +final case class TestCases(id: Int, name: String, enabled: Boolean)