Suppress logs for test cases unless a failure is reported (#8694)

The change adds a convenient trait `ReportLogsOnFailure` that, when merged with the test class, will keep logs in memory and only delegate to the underlying appender on failure. For now we only support forwarding to the console which is sufficient.
A corresponding entry in `application-test.conf` has to point to the new `memory` appender. The additional complexity in the implementation ensures that if someone forgets to mixin `ReportLogsOnFailure` logs appear as before i.e. they respect the log level.

As a bonus fixed arguments passed to ScalaTest in build.sbt so that we are now, again, showing timings of individual tests.

Closes #8603.

# Important Notes
Before:
```
[info] VcsManagerTest:
[info] Initializing project
[ERROR] [2024-01-04 17:27:03,366] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must create a repository (3 seconds, 538 milliseconds)
[info] - must fail to create a repository for an already existing project (141 milliseconds)
[info] Save project
[ERROR] [2024-01-04 17:27:08,346] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must create a commit with a timestamp (198 milliseconds)
[ERROR] [2024-01-04 17:27:08,570] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must create a commit with a name (148 milliseconds)
[ERROR] [2024-01-04 17:27:08,741] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must force all pending saves (149 milliseconds)
[info] Status project
[ERROR] [2024-01-04 17:27:08,910] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must report changed files since last commit (148 milliseconds)
[info] Restore project
[ERROR] [2024-01-04 17:27:09,076] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must reset to the last state with committed changes (236 milliseconds)
[ERROR] [2024-01-04 17:27:09,328] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must reset to a named save (pending)
[ERROR] [2024-01-04 17:27:09,520] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] - must reset to a named save and notify about removed files *** FAILED *** (185 milliseconds)
[info]   Right({
[info]     "jsonrpc" : "2.0",
[info]     "method" : "file/event",
[info]     "params" : {
[info]       "path" : {
[info]         "rootId" : "cd84a4a3-fa50-4ead-8d80-04f6d0d124a3",
[info]         "segments" : [
[info]           "src",
[info]           "Bar.enso"
[info]         ]
[info]       },
[info]       "kind" : "Removed"
[info]     }
[info]   }) did not equal Right({
[info]     "jsonrpc" : "1.0",
[info]     "method" : "file/event",
[info]     "params" : {
[info]       "path" : {
[info]         "rootId" : "cd84a4a3-fa50-4ead-8d80-04f6d0d124a3",
[info]         "segments" : [
[info]           "src",
[info]           "Bar.enso"
[info]         ]
[info]       },
[info]       "kind" : "Removed"
[info]     }
[info]   }) (VcsManagerTest.scala:1343)
[info]   Analysis:
[info]   Right(value: Json$JObject(value: object[jsonrpc -> "2.0",method -> "file/event",params -> {
[info]   "path" : {
[info]     "rootId" : "cd84a4a3-fa50-4ead-8d80-04f6d0d124a3",
[info]     "segments" : [
[info]       "src",
[info]       "Bar.enso"
[info]     ]
[info]   },
[info]   "kind" : "Removed"
[info] }] -> object[jsonrpc -> "1.0",method -> "file/event",params -> {
[info]   "path" : {
[info]     "rootId" : "cd84a4a3-fa50-4ead-8d80-04f6d0d124a3",
[info]     "segments" : [
[info]       "src",
[info]       "Bar.enso"
[info]     ]
[info]   },
[info]   "kind" : "Removed"
[info] }]))
[ERROR] [2024-01-04 17:27:09,734] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/3607843843826594318].
[info] List project saves
[info] - must return all explicit commits (146 milliseconds)
[info] Run completed in 9 seconds, 270 milliseconds.
[info] Total number of tests run: 9
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 8, failed 1, canceled 0, ignored 0, pending 1
[info] *** 1 TEST FAILED ***
```

After:
```
[info] VcsManagerTest:
[info] Initializing project
[info] - must create a repository (3 seconds, 554 milliseconds)
[info] - must fail to create a repository for an already existing project (164 milliseconds)
[info] Save project
[info] - must create a commit with a timestamp (212 milliseconds)
[info] - must create a commit with a name (142 milliseconds)
[info] - must force all pending saves (185 milliseconds)
[info] Status project
[info] - must report changed files since last commit (142 milliseconds)
[info] Restore project
[info] - must reset to the last state with committed changes (202 milliseconds)
[info] - must reset to a named save (pending)
[ERROR] [2024-01-04 17:24:55,738] [org.enso.languageserver.search.SuggestionsHandler] Cannot read the package definition from [/tmp/8456553964637757156].
[info] - must reset to a named save and notify about removed files *** FAILED *** (186 milliseconds)
[info]   Right({
[info]     "jsonrpc" : "2.0",
[info]     "method" : "file/event",
[info]     "params" : {
[info]       "path" : {
[info]         "rootId" : "965ed5c8-1760-4284-91f2-1376406fde0d",
[info]         "segments" : [
[info]           "src",
[info]           "Bar.enso"
[info]         ]
[info]       },
[info]       "kind" : "Removed"
[info]     }
[info]   }) did not equal Right({
[info]     "jsonrpc" : "1.0",
[info]     "method" : "file/event",
[info]     "params" : {
[info]       "path" : {
[info]         "rootId" : "965ed5c8-1760-4284-91f2-1376406fde0d",
[info]         "segments" : [
[info]           "src",
[info]           "Bar.enso"
[info]         ]
[info]       },
[info]       "kind" : "Removed"
[info]     }
[info]   }) (VcsManagerTest.scala:1343)
[info]   Analysis:
[info]   Right(value: Json$JObject(value: object[jsonrpc -> "2.0",method -> "file/event",params -> {
[info]   "path" : {
[info]     "rootId" : "965ed5c8-1760-4284-91f2-1376406fde0d",
[info]     "segments" : [
[info]       "src",
[info]       "Bar.enso"
[info]     ]
[info]   },
[info]   "kind" : "Removed"
[info] }] -> object[jsonrpc -> "1.0",method -> "file/event",params -> {
[info]   "path" : {
[info]     "rootId" : "965ed5c8-1760-4284-91f2-1376406fde0d",
[info]     "segments" : [
[info]       "src",
[info]       "Bar.enso"
[info]     ]
[info]   },
[info]   "kind" : "Removed"
[info] }]))
[info] List project saves
[info] - must return all explicit commits (131 milliseconds)
[info] Run completed in 9 seconds, 400 milliseconds.
[info] Total number of tests run: 9
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 8, failed 1, canceled 0, ignored 0, pending 1
[info] *** 1 TEST FAILED ***
```
This commit is contained in:
Hubert Plociniczak 2024-01-09 10:59:10 +01:00 committed by GitHub
parent 4983550089
commit 31a0dcef65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 411 additions and 73 deletions

View File

@ -231,7 +231,7 @@ ThisBuild / scalacOptions ++= Seq(
)
ThisBuild / Test / testOptions ++=
Seq(Tests.Argument("-oI")) ++
Seq(Tests.Argument(TestFrameworks.ScalaTest, "-oID")) ++
sys.env
.get("ENSO_TEST_JUNIT_DIR")
.map { junitDir =>
@ -758,6 +758,7 @@ lazy val `logging-service-logback` = project
"org.slf4j" % "slf4j-api" % slf4jVersion,
"io.sentry" % "sentry-logback" % "6.28.0",
"io.sentry" % "sentry" % "6.28.0",
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided"
) ++ logbackPkg
)
@ -1552,9 +1553,6 @@ lazy val runtime = (project in file("engine/runtime"))
},
Test / parallelExecution := false,
Test / logBuffered := false,
Test / testOptions += Tests.Argument(
"-oD"
), // show timings for individual tests
scalacOptions += "-Ymacro-annotations",
scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"),
libraryDependencies ++= jmh ++ jaxb ++ GraalVM.langsPkgs ++ Seq(

View File

@ -22,11 +22,15 @@ logging-service {
io.methvin.watcher = error
}
appenders = [
{
name = "memory"
forward-to = console
},
{
name = "console"
pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex"
pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n"
}
]
default-appender = console
default-appender = memory
log-level = "error"
}

View File

@ -7,6 +7,7 @@ import org.enso.languageserver.boot.{ProfilingConfig, StartupConfig}
import org.enso.languageserver.data._
import org.enso.languageserver.event.InitializedEvent
import org.enso.languageserver.filemanager.{ContentRoot, ContentRootWithFile}
import org.enso.logger.ReportLogsOnFailure
import org.enso.searcher.sql.{SchemaVersion, SqlDatabase, SqlSuggestionsRepo}
import org.enso.testkit.{FlakySpec, ToScalaFutureConversions}
import org.scalatest.BeforeAndAfterAll
@ -16,7 +17,6 @@ import org.sqlite.SQLiteException
import java.nio.file.{Files, StandardOpenOption}
import java.util.UUID
import scala.concurrent.Await
import scala.concurrent.duration._
@ -27,7 +27,8 @@ class RepoInitializationSpec
with Matchers
with BeforeAndAfterAll
with ToScalaFutureConversions
with FlakySpec {
with FlakySpec
with ReportLogsOnFailure {
import system.dispatcher

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.ContentRootManagerProtocol.{
ContentRootsAddedNotification,
SubscribeToNotifications
}
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.testkit.{EitherValue, WithTemporaryDirectory}
import org.scalatest.concurrent.Futures
@ -30,7 +31,8 @@ class ContentRootManagerSpec
with Inside
with EitherValue
with OptionValues
with WithTemporaryDirectory {
with WithTemporaryDirectory
with ReportLogsOnFailure {
var rootManager: ContentRootManagerWrapper = _
var rootActor: ActorRef = _

View File

@ -3,12 +3,16 @@ package org.enso.languageserver.filemanager
import io.circe.Json
import io.circe.syntax._
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import java.util.UUID
class ContentRootSerializationSpec extends AnyWordSpec with Matchers {
class ContentRootSerializationSpec
extends AnyWordSpec
with Matchers
with ReportLogsOnFailure {
"ContentRoot" should {
"correctly serialize" in {
val id = UUID.randomUUID()

View File

@ -2,6 +2,7 @@ package org.enso.languageserver.filemanager
import org.apache.commons.io.FileUtils
import org.enso.languageserver.effect.Effects
import org.enso.logger.ReportLogsOnFailure
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
@ -13,7 +14,11 @@ import scala.collection.mutable.ArrayBuffer
import scala.io.Source
import scala.jdk.CollectionConverters._
class FileSystemSpec extends AnyWordSpecLike with Matchers with Effects {
class FileSystemSpec
extends AnyWordSpecLike
with Matchers
with Effects
with ReportLogsOnFailure {
import FileSystemApi._

View File

@ -1,6 +1,7 @@
package org.enso.languageserver.libraries
import org.enso.editions.LibraryName
import org.enso.logger.ReportLogsOnFailure
import org.enso.pkg.{
Component,
ComponentGroup,
@ -13,7 +14,10 @@ import org.enso.pkg.{
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ComponentGroupsResolverSpec extends AnyWordSpec with Matchers {
class ComponentGroupsResolverSpec
extends AnyWordSpec
with Matchers
with ReportLogsOnFailure {
import ComponentGroupsResolverSpec._

View File

@ -2,11 +2,15 @@ package org.enso.languageserver.libraries
import io.circe.DecodingFailure
import org.enso.editions.LibraryName
import org.enso.logger.ReportLogsOnFailure
import org.enso.pkg.{ComponentGroups, Config, GroupName, GroupReference}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ComponentGroupsValidatorSpec extends AnyWordSpec with Matchers {
class ComponentGroupsValidatorSpec
extends AnyWordSpec
with Matchers
with ReportLogsOnFailure {
import ComponentGroupsValidator._
import ComponentGroupsValidatorSpec._

View File

@ -5,10 +5,14 @@ import org.enso.languageserver.libraries.EditionReference.{
CurrentProjectEdition,
NamedEdition
}
import org.enso.logger.ReportLogsOnFailure
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class EditionNameSerializationSpec extends AnyWordSpec with Matchers {
class EditionNameSerializationSpec
extends AnyWordSpec
with Matchers
with ReportLogsOnFailure {
"EditionName" should {
"serialize and deserialize to the same thing" in {
val edition1: EditionReference = CurrentProjectEdition

View File

@ -1,10 +1,14 @@
package org.enso.languageserver.libraries
import io.circe.syntax._
import org.enso.logger.ReportLogsOnFailure
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class LibraryEntrySerializationSpec extends AnyWordSpec with Matchers {
class LibraryEntrySerializationSpec
extends AnyWordSpec
with Matchers
with ReportLogsOnFailure {
"LibraryEntry" should {
"serialize and deserialize to the same thing" in {
val entry1 =

View File

@ -5,6 +5,7 @@ import akka.testkit._
import org.enso.distribution.FileSystem.PathSyntax
import org.enso.editions.LibraryName
import org.enso.librarymanager.LibraryLocations
import org.enso.logger.ReportLogsOnFailure
import org.enso.pkg.PackageManager
import org.enso.testkit.WithTemporaryDirectory
import org.scalatest.matchers.should.Matchers
@ -20,7 +21,8 @@ class LocalLibraryManagerSpec
with AnyWordSpecLike
with Matchers
with BeforeAndAfterAll
with WithTemporaryDirectory {
with WithTemporaryDirectory
with ReportLogsOnFailure {
val Timeout: FiniteDuration = 10.seconds

View File

@ -11,6 +11,7 @@ import org.enso.languageserver.monitoring.HealthCheckEndpointSpec.{
}
import org.enso.languageserver.monitoring.MonitoringProtocol.{Ping, Pong}
import org.enso.languageserver.requesthandler.monitoring.PingHandler
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.must.Matchers
@ -22,7 +23,8 @@ class HealthCheckEndpointSpec
with Matchers
with FlakySpec
with ScalatestRouteTest
with Directives {
with Directives
with ReportLogsOnFailure {
implicit val timeout: RouteTestTimeout = RouteTestTimeout(25.seconds)

View File

@ -5,6 +5,7 @@ import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
import org.enso.languageserver.TestClock
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.should.Matchers
@ -16,7 +17,8 @@ class IdlenessEndpointSpec
with Matchers
with FlakySpec
with ScalatestRouteTest
with Directives {
with Directives
with ReportLogsOnFailure {
implicit val timeout: RouteTestTimeout = RouteTestTimeout(25.seconds)

View File

@ -6,6 +6,7 @@ import org.enso.jsonrpc.Id.Number
import org.enso.jsonrpc.{Request, ResponseResult, Unused}
import org.enso.languageserver.monitoring.MonitoringApi
import org.enso.languageserver.monitoring.MonitoringProtocol.{Ping, Pong}
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.must.Matchers
@ -17,7 +18,8 @@ class PingHandlerSpec
with ImplicitSender
with AnyFlatSpecLike
with Matchers
with FlakySpec {
with FlakySpec
with ReportLogsOnFailure {
"A PingHandler" must "scatter pings to all subsystems" in {
//given

View File

@ -19,6 +19,7 @@ import org.enso.languageserver.session.SessionRouter.{
DeliverToBinaryController,
DeliverToJsonController
}
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.testkit.RetrySpec
import org.scalatest.BeforeAndAfterAll
@ -35,7 +36,8 @@ class ContextEventsListenerSpec
with AnyWordSpecLike
with Matchers
with BeforeAndAfterAll
with RetrySpec {
with RetrySpec
with ReportLogsOnFailure {
override def afterAll(): Unit = {
TestKit.shutdownActorSystem(system)

View File

@ -13,6 +13,7 @@ import org.enso.languageserver.event.InitializedEvent
import org.enso.languageserver.filemanager._
import org.enso.languageserver.session.JsonSession
import org.enso.languageserver.session.SessionRouter.DeliverToJsonController
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.data.{Tree, TypeGraph}
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.polyglot.{ExportedSymbol, ModuleExports, Suggestion}
@ -37,7 +38,8 @@ class SuggestionsHandlerSpec
with AnyWordSpecLike
with Matchers
with BeforeAndAfterAll
with RetrySpec {
with RetrySpec
with ReportLogsOnFailure {
import system.dispatcher

View File

@ -10,6 +10,7 @@ import org.eclipse.jgit.api.{Git => JGit}
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import org.eclipse.jgit.revwalk.RevCommit
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import scala.concurrent.duration._
@ -19,7 +20,8 @@ class GitSpec
extends AnyWordSpecLike
with Matchers
with Effects
with FlakySpec {
with FlakySpec
with ReportLogsOnFailure {
override def opTimeout = 5.seconds

View File

@ -5,6 +5,7 @@ import org.apache.commons.io.FileUtils
import org.enso.languageserver.protocol.binary._
import org.enso.languageserver.util.binary.BinaryDecoder
import org.enso.languageserver.websocket.binary.factory._
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import java.io.File
@ -12,10 +13,12 @@ import java.nio.ByteBuffer
import java.nio.file.Files
import java.security.MessageDigest
import java.util.UUID
import scala.io.Source
class BinaryFileManipulationTest extends BaseBinaryServerTest with FlakySpec {
class BinaryFileManipulationTest
extends BaseBinaryServerTest
with FlakySpec
with ReportLogsOnFailure {
implicit private val decoder: BinaryDecoder[OutboundMessage] =
OutboundMessageDecoder

View File

@ -1,7 +1,6 @@
package org.enso.languageserver.websocket.binary
import java.util.UUID
import com.google.flatbuffers.FlatBufferBuilder
import org.enso.languageserver.protocol.binary.{
InboundPayload,
@ -13,9 +12,13 @@ import org.enso.languageserver.websocket.binary.factory.{
InboundMessageFactory,
SessionInitFactory
}
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
class BinarySessionManagementTest extends BaseBinaryServerTest with FlakySpec {
class BinarySessionManagementTest
extends BaseBinaryServerTest
with FlakySpec
with ReportLogsOnFailure {
implicit private val decoder: BinaryDecoder[OutboundMessage] =
OutboundMessageDecoder

View File

@ -2,7 +2,6 @@ package org.enso.languageserver.websocket.binary
import java.nio.ByteBuffer
import java.util.UUID
import org.enso.languageserver.protocol.binary.{
OutboundMessage,
OutboundPayload,
@ -13,13 +12,15 @@ import org.enso.languageserver.runtime.ContextRegistryProtocol.{
VisualizationUpdate
}
import org.enso.languageserver.util.binary.BinaryDecoder
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import org.scalatest.concurrent.Eventually
class VisualizationProtocolTest
extends BaseBinaryServerTest
with Eventually
with FlakySpec {
with FlakySpec
with ReportLogsOnFailure {
implicit private val decoder: BinaryDecoder[OutboundMessage] =
OutboundMessageDecoder

View File

@ -3,11 +3,12 @@ package org.enso.languageserver.websocket.json
import io.circe.literal._
import io.circe.parser.parse
import io.circe.syntax.EncoderOps
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import java.util.UUID
class CapabilitiesTest extends BaseServerTest {
class CapabilitiesTest extends BaseServerTest with ReportLogsOnFailure {
"capability/acquire" must {

View File

@ -8,11 +8,12 @@ import org.enso.languageserver.runtime.{
import org.enso.languageserver.websocket.json.{
ExecutionContextJsonMessages => json
}
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import java.util.UUID
class ContextRegistryTest extends BaseServerTest {
class ContextRegistryTest extends BaseServerTest with ReportLogsOnFailure {
"ContextRegistry" must {

View File

@ -6,6 +6,7 @@ import org.apache.commons.io.FileUtils
import org.bouncycastle.util.encoders.Hex
import org.enso.languageserver.boot.{ProfilingConfig, StartupConfig}
import org.enso.languageserver.data._
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.testkit.RetrySpec
@ -16,7 +17,10 @@ import java.security.MessageDigest
import java.util.UUID
import scala.concurrent.duration._
class FileManagerTest extends BaseServerTest with RetrySpec {
class FileManagerTest
extends BaseServerTest
with RetrySpec
with ReportLogsOnFailure {
override def mkConfig: Config = {
val directoriesDir = Files.createTempDirectory(null).toRealPath()

View File

@ -2,11 +2,15 @@ package org.enso.languageserver.websocket.json
import java.io.File
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.testkit.FlakySpec
import org.enso.text.editing.model.{Position, Range, TextEdit}
class FileNotificationsTest extends BaseServerTest with FlakySpec {
class FileNotificationsTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
def file(name: String): File = new File(testContentRoot.file, name)

View File

@ -1,9 +1,13 @@
package org.enso.languageserver.websocket.json
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.testkit.RetrySpec
class InputOutputRedirectionTest extends BaseServerTest with RetrySpec {
class InputOutputRedirectionTest
extends BaseServerTest
with RetrySpec
with ReportLogsOnFailure {
"Standard output redirection controller" must {

View File

@ -19,6 +19,7 @@ import org.enso.librarymanager.published.repository.{
ExampleRepository,
LibraryManifest
}
import org.enso.logger.ReportLogsOnFailure
import org.enso.pkg.{Config, Contact, Package, PackageManager}
import org.enso.yaml.YamlHelper
@ -26,7 +27,7 @@ import java.nio.file.Files
import java.nio.file.Path
import scala.concurrent.duration._
class LibrariesTest extends BaseServerTest {
class LibrariesTest extends BaseServerTest with ReportLogsOnFailure {
private val libraryRepositoryPort: Int = 47308
private val exampleRepo = new ExampleRepository(

View File

@ -1,9 +1,13 @@
package org.enso.languageserver.websocket.json
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
class MonitoringTest extends BaseServerTest with FlakySpec {
class MonitoringTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
"Monitoring subsystem" must {

View File

@ -3,10 +3,11 @@ package org.enso.languageserver.websocket.json
import org.enso.distribution.DistributionManager
import org.enso.languageserver.profiling.ProfilingManager
import org.enso.languageserver.runtime.RuntimeConnector
import org.enso.logger.ReportLogsOnFailure
import java.nio.file.Files
class ProfilingManagerTest extends BaseServerTest {
class ProfilingManagerTest extends BaseServerTest with ReportLogsOnFailure {
private val json = ProfilingJsonMessages

View File

@ -2,10 +2,13 @@ package org.enso.languageserver.websocket.json
import io.circe.literal._
import org.enso.distribution.FileSystem
import org.enso.logger.ReportLogsOnFailure
import java.nio.file.Files
class ProjectSettingsManagerTest extends BaseServerTest {
class ProjectSettingsManagerTest
extends BaseServerTest
with ReportLogsOnFailure {
override def beforeEach(): Unit = {
super.beforeEach()

View File

@ -2,9 +2,13 @@ package org.enso.languageserver.websocket.json
import java.nio.file.{Files, Paths}
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
class ReceivesTreeUpdatesHandlerTest extends BaseServerTest with FlakySpec {
class ReceivesTreeUpdatesHandlerTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
override val isFileWatcherEnabled = true

View File

@ -1,11 +1,12 @@
package org.enso.languageserver.websocket.json
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import java.util.UUID
class RefactoringTest extends BaseServerTest {
class RefactoringTest extends BaseServerTest with ReportLogsOnFailure {
"refactoring/renameProject" should {

View File

@ -2,9 +2,10 @@ package org.enso.languageserver.websocket.json
import io.circe.literal._
import org.enso.languageserver.runtime.TestComponentGroups
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
class RuntimeTest extends BaseServerTest {
class RuntimeTest extends BaseServerTest with ReportLogsOnFailure {
"runtime/getComponentGroups" should {

View File

@ -3,8 +3,9 @@ package org.enso.languageserver.websocket.json
import io.circe.literal._
import io.circe.parser.parse
import io.circe.syntax.EncoderOps
import org.enso.logger.ReportLogsOnFailure
class SessionManagementTest extends BaseServerTest {
class SessionManagementTest extends BaseServerTest with ReportLogsOnFailure {
"A server" when {

View File

@ -3,6 +3,7 @@ package org.enso.languageserver.websocket.json
import io.circe.literal._
import org.enso.languageserver.search.Suggestions
import org.enso.languageserver.websocket.json.{SearchJsonMessages => json}
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.{ExportedSymbol, ModuleExports}
import org.enso.polyglot.data.Tree
import org.enso.polyglot.runtime.Runtime.Api
@ -10,7 +11,10 @@ import org.enso.testkit.FlakySpec
import scala.collection.immutable.ListSet
class SuggestionsHandlerEventsTest extends BaseServerTest with FlakySpec {
class SuggestionsHandlerEventsTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
"SuggestionsHandlerEvents" must {

View File

@ -1,14 +1,15 @@
package org.enso.languageserver.websocket.json
import java.util.UUID
import io.circe.literal._
import org.enso.languageserver.websocket.json.{SearchJsonMessages => json}
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.{FlakySpec, RetrySpec}
class SuggestionsHandlerTest
extends BaseServerTest
with FlakySpec
with RetrySpec {
with RetrySpec
with ReportLogsOnFailure {
"SuggestionsHandler" must {

View File

@ -5,6 +5,7 @@ import io.circe.literal._
import org.enso.languageserver.event.{BufferClosed, JsonSessionTerminated}
import org.enso.languageserver.filemanager.Path
import org.enso.languageserver.session.JsonSession
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.testkit.FlakySpec
import org.enso.text.editing.model
@ -12,10 +13,12 @@ import org.enso.text.editing.model
import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import scala.concurrent.duration._
class TextOperationsTest extends BaseServerTest with FlakySpec {
class TextOperationsTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
override def isFileWatcherEnabled = true

View File

@ -10,6 +10,7 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import org.enso.languageserver.boot.{ProfilingConfig, StartupConfig}
import org.enso.languageserver.data._
import org.enso.languageserver.vcsmanager.VcsApi
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import java.io.File
@ -19,7 +20,10 @@ import java.time.{Clock, LocalDate}
import scala.concurrent.duration._
import scala.jdk.CollectionConverters._
class VcsManagerTest extends BaseServerTest with FlakySpec {
class VcsManagerTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
override def mkConfig: Config = {
val directoriesDir = Files.createTempDirectory(null).toRealPath()

View File

@ -5,10 +5,13 @@ import org.enso.languageserver.runtime.{
MethodPointer,
VisualizationConfiguration
}
import org.enso.logger.ReportLogsOnFailure
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.text.editing.model
class VisualizationOperationsTest extends BaseServerTest {
class VisualizationOperationsTest
extends BaseServerTest
with ReportLogsOnFailure {
"executionContext/attachVisualization" must {

View File

@ -3,11 +3,15 @@ package org.enso.languageserver.websocket.json
import buildinfo.Info
import io.circe.literal.JsonStringContext
import org.enso.languageserver.data.Config
import org.enso.logger.ReportLogsOnFailure
import org.enso.testkit.FlakySpec
import java.io.{File, FileOutputStream}
class WorkspaceOperationsTest extends BaseServerTest with FlakySpec {
class WorkspaceOperationsTest
extends BaseServerTest
with FlakySpec
with ReportLogsOnFailure {
override def initializeProjectPackage: Boolean = false

View File

@ -60,6 +60,14 @@ public abstract class LoggerSetup {
*/
public abstract boolean setupConsoleAppender(Level logLevel);
/**
* Setup writing logger's log event to a memory.
*
* @param logLevel the maximal level of logs that will be displayed
* @return true if logger was setup correctly, false otherwise
*/
public abstract boolean setupMemoryAppender(Level logLevel);
/**
* Setup forwarding logger's log event to a sentry,io service. Requires the presence of the
* sentry's dependency appropriate to the logging implementation.

View File

@ -10,7 +10,7 @@ import org.slf4j.event.Level;
* to do with the recorded log events
*/
public abstract sealed class Appender
permits FileAppender, SocketAppender, SentryAppender, ConsoleAppender {
permits ConsoleAppender, FileAppender, MemoryAppender, SentryAppender, SocketAppender {
/**
* Returns the name of the appender
@ -37,6 +37,8 @@ public abstract sealed class Appender
return SentryAppender.parse(config);
case ConsoleAppender.appenderName:
return ConsoleAppender.parse(config);
case MemoryAppender.appenderName:
return MemoryAppender.parse(config);
default:
return null;
}

View File

@ -141,6 +141,10 @@ public class LoggingServiceConfig implements BaseConfig {
return (ConsoleAppender) appenders.getOrDefault(ConsoleAppender.appenderName, null);
}
public MemoryAppender getMemoryAppender() {
return (MemoryAppender) appenders.getOrDefault(MemoryAppender.appenderName, null);
}
public SentryAppender getSentryAppender() {
return (SentryAppender) appenders.getOrDefault(SentryAppender.appenderName, null);
}

View File

@ -0,0 +1,54 @@
package org.enso.logger.config;
import com.typesafe.config.Config;
import java.nio.file.Path;
import org.enso.logger.LoggerSetup;
import org.slf4j.event.Level;
/**
* Configuration for appender that keeps log events in memory and, optionally, forwards them to the
* underlying appneder.
*/
public final class MemoryAppender extends Appender {
public static final String appenderName = "memory";
private final String forwardTo;
private MemoryAppender(String forwardTo) {
this.forwardTo = forwardTo;
}
public static MemoryAppender parse(Config config) {
String fowardTo = config.hasPath(forwardToKey) ? config.getString(forwardToKey) : "console";
return new MemoryAppender(fowardTo);
}
@Override
public boolean setup(Level logLevel, LoggerSetup appenderSetup) {
return appenderSetup.setupMemoryAppender(logLevel);
}
@Override
public boolean setupForPath(
Level logLevel, Path logRoot, String logPrefix, LoggerSetup loggerSetup) {
LogToFile logToFileOpt = loggerSetup.getConfig().logToFile();
if (logToFileOpt.enabled()) {
Level minLevel =
Level.intToLevel(Math.min(logToFileOpt.logLevel().toInt(), logLevel.toInt()));
loggerSetup.setupFileAppender(minLevel, logRoot, logPrefix);
}
return loggerSetup.setupMemoryAppender(logLevel);
}
public String getTarget() {
return forwardTo;
}
@Override
public String getName() {
return appenderName;
}
private static final String forwardToKey = "forward-to";
}

View File

@ -151,7 +151,8 @@ public final class LogbackSetup extends LoggerSetup {
}
final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern(appenderConfig.getPattern());
env.finalizeEncoder(encoder);
encoder.setContext(env.ctx);
encoder.start();
FileAppender<ILoggingEvent> fileAppender;
@ -210,6 +211,13 @@ public final class LogbackSetup extends LoggerSetup {
@Override
public boolean setupConsoleAppender(Level logLevel) {
LoggerAndContext env = contextInit(logLevel, config, !logToFileEnabled());
var consoleAppender = getConsoleAppender(env.ctx, config);
env.finalizeAppender(consoleAppender);
return true;
}
private ch.qos.logback.core.Appender<ILoggingEvent> getConsoleAppender(
LoggerContext ctx, LoggingServiceConfig config) {
org.enso.logger.config.ConsoleAppender appenderConfig = config.getConsoleAppender();
final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
try {
@ -222,13 +230,34 @@ public final class LogbackSetup extends LoggerSetup {
e.printStackTrace();
encoder.setPattern(Appender.defaultPattern);
}
env.finalizeEncoder(encoder);
encoder.setContext(ctx);
encoder.start();
ConsoleAppender<ILoggingEvent> consoleAppender = new ConsoleAppender<>();
consoleAppender.setName("enso-console");
consoleAppender.setEncoder(encoder);
return consoleAppender;
}
env.finalizeAppender(consoleAppender);
@Override
public boolean setupMemoryAppender(Level logLevel) {
LoggerAndContext env = contextInit(logLevel, config, !logToFileEnabled());
org.enso.logger.config.MemoryAppender appenderConfig = config.getMemoryAppender();
ch.qos.logback.core.Appender<ILoggingEvent> target;
switch (appenderConfig.getTarget()) {
case org.enso.logger.config.ConsoleAppender.appenderName:
target = getConsoleAppender(env.ctx, config);
break;
default:
target = null;
}
if (target == null) {
throw new RuntimeException("unsupported appender " + appenderConfig.getTarget());
}
target.setContext(env.ctx);
target.start();
var memoryAppender = new MemoryAppender(target);
env.finalizeAppender(memoryAppender);
return true;
}
@ -308,11 +337,6 @@ public final class LogbackSetup extends LoggerSetup {
private record LoggerAndContext(
Level level, LoggerContext ctx, Logger logger, Filter<ILoggingEvent> filter) {
void finalizeEncoder(ch.qos.logback.core.encoder.Encoder<ILoggingEvent> encoder) {
encoder.setContext(ctx);
encoder.start();
}
void finalizeAppender(ch.qos.logback.core.Appender<ILoggingEvent> appender) {
if (filter == null) {
ThresholdFilter threshold = new ThresholdFilter();

View File

@ -0,0 +1,59 @@
package org.enso.logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.AppenderBase;
import java.util.ArrayList;
import java.util.List;
/**
* Appender that keeps all log events internally and can either discard them or forward to the
* underlying appender.
*/
public class MemoryAppender extends AppenderBase<ILoggingEvent> {
private final Appender<ILoggingEvent> underlying;
private final List<ILoggingEvent> events;
private volatile boolean forwardLogs;
public MemoryAppender(Appender<ILoggingEvent> underlying) {
this.underlying = underlying;
this.events = new ArrayList<>();
this.forwardLogs = true;
}
protected void append(ILoggingEvent e) {
if (forwardLogs) {
underlying.doAppend(e);
} else {
events.add(e);
}
}
public void reset() {
this.forwardLogs = true;
this.underlying.start();
events.clear();
}
public void flush() {
// Ensure context set
underlying.start();
for (var element : events) {
underlying.doAppend(element);
}
underlying.stop();
}
public void stopForwarding() {
this.forwardLogs = false;
this.underlying.stop();
}
@Override
public String getName() {
return NAME;
}
public static final String NAME = "memory";
}

View File

@ -0,0 +1,51 @@
package org.enso.logger
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.Appender
import org.scalatest.{Args, Failed, Outcome, Status, TestSuite}
import org.slf4j.{Logger, LoggerFactory}
trait ReportLogsOnFailure extends TestSuite {
private lazy val appender: Appender[ILoggingEvent] = {
val ctx = LoggerFactory.getILoggerFactory;
val rootLogger = ctx
.getLogger(Logger.ROOT_LOGGER_NAME)
.asInstanceOf[ch.qos.logback.classic.Logger]
rootLogger.getAppender(MemoryAppender.NAME)
}
abstract override protected def runTest(
testName: String,
args: Args
): Status = {
appender match {
case memoryAppender: MemoryAppender =>
memoryAppender.stopForwarding()
super.runTest(testName, args)
case _ =>
super.runTest(testName, args)
}
}
abstract override def withFixture(test: NoArgTest): Outcome = {
appender match {
case memoryAppender: MemoryAppender =>
try {
super.withFixture(test) match {
case outcome @ Failed(_) =>
memoryAppender.flush()
outcome
case outcome =>
outcome
}
} finally {
memoryAppender.reset()
}
case _ =>
super.withFixture(test)
}
}
}

View File

@ -10,12 +10,16 @@ logging-service {
akka = error
}
appenders = [
{
name = "memory"
forward-to = console
},
{
name = "console"
pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n%nopex"
}
]
default-appender = console
default-appender = memory
log-level = "warn"
}

View File

@ -3,6 +3,7 @@ package org.enso.projectmanager.infrastructure.languageserver
import akka.testkit.TestDuration
import io.circe.literal._
import nl.gn0s1s.bump.SemVer
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.test.Net._
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
import org.enso.runtimeversionmanager.test.OverrideTestVersionSuite
@ -13,7 +14,8 @@ import scala.concurrent.duration._
class LanguageServerGatewaySpec
extends BaseServerSpec
with OverrideTestVersionSuite
with ProjectManagementOps {
with ProjectManagementOps
with ReportLogsOnFailure {
override val testVersion: SemVer = SemVer(0, 0, 1)

View File

@ -3,6 +3,7 @@ package org.enso.projectmanager.infrastructure.languageserver
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{ImplicitSender, TestActor, TestKit, TestProbe}
import com.miguno.akka.testing.VirtualTime
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.boot.configuration.SupervisionConfig
import org.enso.projectmanager.infrastructure.http.AkkaBasedWebSocketConnectionFactory
import org.enso.projectmanager.infrastructure.languageserver.LanguageServerBootLoader.ServerBooted
@ -27,7 +28,8 @@ class LanguageServerSupervisorSpec
with Matchers
with BeforeAndAfterAll
with MockitoSugar
with FlakySpec {
with FlakySpec
with ReportLogsOnFailure {
"A language supervisor" should "monitor language server by sending ping requests on regular basis" taggedAs Flaky in new TestCtx {
//given

View File

@ -3,6 +3,7 @@ package org.enso.projectmanager.infrastructure.languageserver
import akka.actor.ActorSystem
import akka.testkit._
import com.miguno.akka.testing.VirtualTime
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.data.Socket
import org.enso.projectmanager.infrastructure.languageserver.LanguageServerProtocol.{
ProjectRenamed,
@ -26,7 +27,8 @@ class ProjectRenameActionSpec
with Matchers
with BeforeAndAfterAll
with MockitoSugar
with FlakySpec {
with FlakySpec
with ReportLogsOnFailure {
"A project rename action" should "delegate request to the Language Server" taggedAs Flaky in new TestCtx {
//given

View File

@ -1,13 +1,15 @@
package org.enso.projectmanager.protocol
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
import org.enso.testkit.FlakySpec
class ConfigApiSpec
extends BaseServerSpec
with FlakySpec
with ProjectManagementOps {
with ProjectManagementOps
with ReportLogsOnFailure {
"global-config/get" must {
"return none for missing keys" in {

View File

@ -2,12 +2,16 @@ package org.enso.projectmanager.protocol
import akka.testkit.TestDuration
import io.circe.literal._
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.BaseServerSpec
import org.enso.testkit.FlakySpec
import scala.concurrent.duration.DurationInt
class EngineManagementApiSpec extends BaseServerSpec with FlakySpec {
class EngineManagementApiSpec
extends BaseServerSpec
with FlakySpec
with ReportLogsOnFailure {
"engine/*" must {
"report no installed engines by default" in {

View File

@ -1,13 +1,17 @@
package org.enso.projectmanager.protocol
import io.circe.literal.JsonStringContext
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.BaseServerSpec
import org.enso.testkit.FlakySpec
import scala.concurrent.Future
import java.net.URI
class LoggingServiceEndpointSpec extends BaseServerSpec with FlakySpec {
class LoggingServiceEndpointSpec
extends BaseServerSpec
with FlakySpec
with ReportLogsOnFailure {
class TestException extends RuntimeException {
override def toString: String = "test-exception"

View File

@ -2,12 +2,14 @@ package org.enso.projectmanager.protocol
import io.circe.literal.JsonStringContext
import nl.gn0s1s.bump.SemVer
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.BaseServerSpec
import org.enso.runtimeversionmanager.test.OverrideTestVersionSuite
class ProjectCreateDefaultToLatestSpec
extends BaseServerSpec
with OverrideTestVersionSuite {
with OverrideTestVersionSuite
with ReportLogsOnFailure {
override val testVersion: SemVer = SemVer(0, 1, 0)

View File

@ -1,10 +1,13 @@
package org.enso.projectmanager.protocol
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.TestDistributionConfiguration
import org.enso.runtimeversionmanager.runner.JVMSettings
import org.enso.runtimeversionmanager.test.FakeReleases
class ProjectCreateHandleMissingRuntimeSpec extends ProjectCreateSpecBase {
class ProjectCreateHandleMissingRuntimeSpec
extends ProjectCreateSpecBase
with ReportLogsOnFailure {
override val distributionConfiguration =
new TestDistributionConfiguration(
distributionRoot = testDistributionRoot.toPath,

View File

@ -1,6 +1,10 @@
package org.enso.projectmanager.protocol
class ProjectCreateMissingComponentsSpec extends ProjectCreateSpecBase {
import org.enso.logger.ReportLogsOnFailure
class ProjectCreateMissingComponentsSpec
extends ProjectCreateSpecBase
with ReportLogsOnFailure {
"project/create" should {
behave like correctlyHandleMissingComponents()
}

View File

@ -4,6 +4,7 @@ import akka.testkit.TestDuration
import io.circe.literal._
import nl.gn0s1s.bump.SemVer
import org.apache.commons.io.FileUtils
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.boot.configuration.TimeoutConfig
import org.enso.projectmanager.{BaseServerSpec, ProjectManagementOps}
import org.enso.runtimeversionmanager.CurrentVersion
@ -14,7 +15,6 @@ import org.scalactic.source.Position
import java.io.File
import java.nio.file.{Files, Paths}
import java.util.UUID
import scala.concurrent.duration._
import scala.io.Source
@ -22,7 +22,8 @@ class ProjectManagementApiSpec
extends BaseServerSpec
with FlakySpec
with OverrideTestVersionSuite
with ProjectManagementOps {
with ProjectManagementOps
with ReportLogsOnFailure {
override val testVersion: SemVer = SemVer(0, 0, 1)

View File

@ -1,10 +1,12 @@
package org.enso.projectmanager.protocol
import nl.gn0s1s.bump.SemVer
import org.enso.logger.ReportLogsOnFailure
import org.enso.runtimeversionmanager.test.OverrideTestVersionSuite
class ProjectOpenMissingComponentsSpec
extends ProjectOpenSpecBase
with OverrideTestVersionSuite {
with OverrideTestVersionSuite
with ReportLogsOnFailure {
override val testVersion: SemVer = defaultVersion

View File

@ -4,6 +4,7 @@ import akka.actor.ActorRef
import io.circe.literal._
import nl.gn0s1s.bump.SemVer
import org.enso.jsonrpc.ClientControllerFactory
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.boot.configuration.TimeoutConfig
import org.enso.projectmanager.event.ClientEvent.ClientDisconnected
import zio.{ZAny, ZIO}
@ -20,7 +21,8 @@ class ProjectShutdownSpec
extends BaseServerSpec
with FlakySpec
with OverrideTestVersionSuite
with ProjectManagementOps {
with ProjectManagementOps
with ReportLogsOnFailure {
override val testVersion: SemVer = SemVer(0, 0, 1)

View File

@ -1,5 +1,6 @@
package org.enso.projectmanager.service.validation
import org.enso.logger.ReportLogsOnFailure
import org.enso.projectmanager.control.effect.Effects
import org.scalatest.EitherValues
import org.scalatest.matchers.must.Matchers
@ -11,7 +12,8 @@ class ProjectNameValidatorSpec
extends AnyWordSpec
with Matchers
with EitherValues
with Effects {
with Effects
with ReportLogsOnFailure {
val projectNameValidator = new ProjectNameValidator[ZIO[ZAny, *, *]]()