From 0d233c16dbb8060d8242a3e0aed81864c720a423 Mon Sep 17 00:00:00 2001 From: Henry Parker Date: Thu, 18 May 2023 17:48:14 -0500 Subject: [PATCH 1/2] print stacktrace in logs --- .../shared/src/main/scala/weaver/Formatter.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/core/shared/src/main/scala/weaver/Formatter.scala b/modules/core/shared/src/main/scala/weaver/Formatter.scala index 6da10ab4..26fab74b 100644 --- a/modules/core/shared/src/main/scala/weaver/Formatter.scala +++ b/modules/core/shared/src/main/scala/weaver/Formatter.scala @@ -1,6 +1,7 @@ package weaver import scala.concurrent.duration.FiniteDuration +import scala.util.Try import cats.data.Chain import cats.syntax.show._ @@ -58,6 +59,9 @@ object Formatter { import outcome._ import TestOutcome.{ Verbose, Summary } + val maxStackFrames = sys.props.get("WEAVER_MAX_STACKFRAMES").flatMap(s => + Try(s.trim.toInt).toOption).getOrElse(50) + val builder = new StringBuilder() val newLine = '\n' builder.append(formatResultStatus(name, result, outcome.duration)) @@ -100,6 +104,15 @@ object Formatter { } builder.append(newLine) + entry.cause.map { t => + TestErrorFormatter.formatStackTrace(t, Some(maxStackFrames)).map{line => + builder.append(TAB4) + builder.append(line) + builder.append(newLine) + } + } + + () } From bd6250ef989ebd8ad315465be3e4482b3a96fa99 Mon Sep 17 00:00:00 2001 From: Henry Parker Date: Tue, 19 Mar 2024 18:15:55 -0500 Subject: [PATCH 2/2] Add tests and change formatter to include class and reason --- .../src/main/scala/weaver/Formatter.scala | 14 +++++++---- .../shared/src/test/scala/DogFoodTests.scala | 24 +++++++++++++++++++ .../shared/src/test/scala/Meta.scala | 18 ++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/modules/core/shared/src/main/scala/weaver/Formatter.scala b/modules/core/shared/src/main/scala/weaver/Formatter.scala index 26fab74b..1823a04b 100644 --- a/modules/core/shared/src/main/scala/weaver/Formatter.scala +++ b/modules/core/shared/src/main/scala/weaver/Formatter.scala @@ -105,14 +105,18 @@ object Formatter { builder.append(newLine) entry.cause.map { t => - TestErrorFormatter.formatStackTrace(t, Some(maxStackFrames)).map{line => - builder.append(TAB4) - builder.append(line) - builder.append(newLine) + builder.append(TAB4) + builder.append(t.toString) + builder.append(newLine) + + TestErrorFormatter.formatStackTrace(t, Some(maxStackFrames)).map { + line => + builder.append(TAB4.prefix * 2) + builder.append(line) + builder.append(newLine) } } - () } diff --git a/modules/framework-cats/shared/src/test/scala/DogFoodTests.scala b/modules/framework-cats/shared/src/test/scala/DogFoodTests.scala index db404b2f..e8540945 100644 --- a/modules/framework-cats/shared/src/test/scala/DogFoodTests.scala +++ b/modules/framework-cats/shared/src/test/scala/DogFoodTests.scala @@ -121,6 +121,30 @@ object DogFoodTests extends IOSuite { } } + test("failures with exceptions in logs display them correctly") { + _.runSuite(Meta.SucceedsWithErrorInLogs).map { + case (logs, _) => + val expected = + """ + |- failure 0ms + | expected (src/main/DogFoodTests.scala:5) + | + | [ERROR] 12:54:35 [DogFoodTests.scala:5] error + | weaver.framework.test.Meta$CustomException: surfaced error + | DogFoodTests.scala:15 my.package.MyClass#MyMethod + | DogFoodTests.scala:20 my.package.ClassOfDifferentLength#method$new$1 + | cats.effect.internals.<...> + | java.util.concurrent.<...> + |""".stripMargin.trim + + exists(extractLogEventAfterFailures(logs) { + case LoggedEvent.Error(msg) => msg + }) { actual => + expect.same(actual, expected) + } + } + } + test("failures with multi-line test name are rendered correctly") { _.runSuite(Meta.Rendering).map { case (logs, _) => diff --git a/modules/framework-cats/shared/src/test/scala/Meta.scala b/modules/framework-cats/shared/src/test/scala/Meta.scala index 90ddd5c8..58709e49 100644 --- a/modules/framework-cats/shared/src/test/scala/Meta.scala +++ b/modules/framework-cats/shared/src/test/scala/Meta.scala @@ -111,6 +111,24 @@ object Meta { } } + object SucceedsWithErrorInLogs extends SimpleIOSuite { + override implicit protected def effectCompat: UnsafeRun[IO] = + SetTimeUnsafeRun + implicit val sourceLocation: SourceLocation = TimeCop.sourceLocation + + loggedTest("failure") { log => + for { + _ <- log.error( + "error", + cause = CustomException( + "surfaced error", + withSnips = true + ) + ) + } yield failure("expected") + } + } + case class CustomException( str: String, causedBy: Exception = null,