From 2d7473566b3badb3c15d69bb26e4fc7d64667cde Mon Sep 17 00:00:00 2001 From: Adam Fraser Date: Fri, 28 Jul 2023 16:47:31 +0100 Subject: [PATCH 1/3] warn if scope cannot be closed --- .../main/scala/zio/test/TestExecutor.scala | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/test/shared/src/main/scala/zio/test/TestExecutor.scala b/test/shared/src/main/scala/zio/test/TestExecutor.scala index fce4765cdba..077fd3616a7 100644 --- a/test/shared/src/main/scala/zio/test/TestExecutor.scala +++ b/test/shared/src/main/scala/zio/test/TestExecutor.scala @@ -62,17 +62,30 @@ object TestExecutor { loop(label :: labels, spec, exec, ancestors, sectionId) case Spec.ScopedCase(managed) => - ZIO - .scoped( - managed - .flatMap(loop(labels, _, exec, ancestors, sectionId)) - ) - .catchAllCause { e => - val event = - ExecutionEvent.RuntimeFailure(sectionId, labels, TestFailure.Runtime(e), ancestors) + Scope.make.flatMap { scope => + scope + .extend(managed.flatMap(loop(labels, _, exec, ancestors, sectionId))) + .onExit { exit => + val warning = + "Warning: ZIO Test is attempting to close the scope of suite " + + s"${labels.reverse.mkString(" - ")} in $fullyQualifiedName, " + + "but closing the scope has taken more than 60 seconds to " + + "complete. This may indicate a resource leak." + for { + warning <- + ZIO.logWarning(warning).delay(60.seconds).withClock(ClockLive).interruptible.forkDaemon + finalizer <- scope.close(exit).ensuring(warning.interrupt).forkDaemon + _ <- warning.await + option <- finalizer.poll + _ <- option.fold(ZIO.unit)(_ => finalizer.join) + } yield () + } + }.catchAllCause { e => + val event = + ExecutionEvent.RuntimeFailure(sectionId, labels, TestFailure.Runtime(e), ancestors) - processEvent(event) - } + processEvent(event) + } case Spec.MultipleCase(specs) => ZIO.uninterruptibleMask(restore => From b9ce897fdd71e6c012b06a61c9cf9561950751bf Mon Sep 17 00:00:00 2001 From: Adam Fraser Date: Fri, 28 Jul 2023 17:36:56 +0100 Subject: [PATCH 2/3] add test --- .../shared/src/test/scala/zio/test/SpecSpec.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test-tests/shared/src/test/scala/zio/test/SpecSpec.scala b/test-tests/shared/src/test/scala/zio/test/SpecSpec.scala index 9945b9c561c..38b7417fa70 100644 --- a/test-tests/shared/src/test/scala/zio/test/SpecSpec.scala +++ b/test-tests/shared/src/test/scala/zio/test/SpecSpec.scala @@ -10,6 +10,9 @@ object SpecSpec extends ZIOBaseSpec { val specLayer: ZLayer[Any, Nothing, Unit] = ZLayer.succeed(()) + val neverFinalizerLayer = + ZLayer.scoped(ZIO.acquireRelease(ZIO.unit)(_ => ZIO.never)) + def spec: Spec[TestEnvironment, TestFailure[Nothing]] = suite("SpecSpec")( suite("provideLayer")( test("does not have early initialization issues") { @@ -207,6 +210,14 @@ object SpecSpec extends ZIOBaseSpec { assert(false)(isFalse) } ) - } + }, + suite("suite does not wait for finalizer to complete")( + test("some test") { + assertCompletes + }, + test("some other test") { + assertCompletes + } + ).provideLayerShared(neverFinalizerLayer) ) } From 54024c67349cb4d18aab9689884564b65756b333 Mon Sep 17 00:00:00 2001 From: Adam Fraser Date: Fri, 28 Jul 2023 19:02:04 +0100 Subject: [PATCH 3/3] fix race condition --- test/shared/src/main/scala/zio/test/TestExecutor.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/shared/src/main/scala/zio/test/TestExecutor.scala b/test/shared/src/main/scala/zio/test/TestExecutor.scala index 077fd3616a7..bb7394fbd95 100644 --- a/test/shared/src/main/scala/zio/test/TestExecutor.scala +++ b/test/shared/src/main/scala/zio/test/TestExecutor.scala @@ -75,9 +75,8 @@ object TestExecutor { warning <- ZIO.logWarning(warning).delay(60.seconds).withClock(ClockLive).interruptible.forkDaemon finalizer <- scope.close(exit).ensuring(warning.interrupt).forkDaemon - _ <- warning.await - option <- finalizer.poll - _ <- option.fold(ZIO.unit)(_ => finalizer.join) + exit <- warning.await + _ <- finalizer.join.when(exit.isInterrupted) } yield () } }.catchAllCause { e =>