public void TestLongTryExecute()

in rd-net/Test.Lifetimes/Lifetimes/LifetimeTest.cs [272:367]


    public void TestLongTryExecute()
    {
      const string expectedWarningText = "can't wait for `ExecuteIfAlive` completed on other thread";
      const string expectedExceptionText = "ExecuteIfAlive after termination of";
      var warningReceived = false;
      Exception? receivedException = null;

      const string stackTraceHeader = "CurrentProcessThreadDumps:";
      var executionWasNotCancelledByTimeoutReceived = false;

      Lifetime.Using(lifetime =>
      {
        void LoggerHandler(LeveledMessage message)
        {
          if (message.Level == LoggingLevel.WARN && message.FormattedMessage.Contains(expectedWarningText)) 
            warningReceived = true;
        }

        lifetime.Bracket(
          () => TestLogger.ExceptionLogger.Handlers += LoggerHandler,
          () => TestLogger.ExceptionLogger.Handlers -= LoggerHandler
          );


        var lifetimeDefinition = lifetime.CreateNested();

        var def2 = lifetime.CreateNested();
        LifetimeDefinition.AdditionalDiagnostics = new LifetimeDefinition.AdditionalDiagnosticsInfo(false, async (lf) => 
        {
          var stacks = GetCurrentProcessThreadDumps();
          Assert.AreEqual(lifetimeDefinition.Lifetime, lf);
          executionWasNotCancelledByTimeoutReceived = true;
          return $"{stackTraceHeader}\n{stacks}";
        });
        
        def2.Terminate();
        Assert.IsFalse(executionWasNotCancelledByTimeoutReceived);
        
        var lifetimeTerminatedEvent = new ManualResetEvent(false);
        var backgroundThreadIsInTryExecuteEvent = new ManualResetEvent(false);
        var thread = new Thread(() => lifetimeDefinition.Lifetime.TryExecute(() =>
        {
          backgroundThreadIsInTryExecuteEvent.Set();
          WaitForLifetimeTerminatedEvent(lifetimeTerminatedEvent);
        }));
        thread.Start();
        backgroundThreadIsInTryExecuteEvent.WaitOne();
        lifetimeDefinition.Terminate();
        lifetimeTerminatedEvent.Set();
        thread.Join();
        try
        {
          TestLogger.ExceptionLogger.ThrowLoggedExceptions();
        }
        catch (Exception e)
        {
          if (!e.Message.Contains(expectedExceptionText))
            throw;

          receivedException = e;
        }
      });

      Assert.IsTrue(warningReceived, "Warning `{0}` must have been logged", expectedWarningText);
      Assert.IsNotNull(receivedException, "Exception `{0}` must have been logged", expectedExceptionText);
      
      Assert.IsTrue(executionWasNotCancelledByTimeoutReceived);
      Assert.IsTrue(receivedException.Message.Contains(stackTraceHeader), $"Exception `{expectedExceptionText}` doesn't contain {stackTraceHeader}");
      Assert.IsTrue(receivedException.Message.Contains(nameof(WaitForLifetimeTerminatedEvent)), $"Exception `{expectedExceptionText}` doesn't contain {nameof(WaitForLifetimeTerminatedEvent)} method");

      static string GetCurrentProcessThreadDumps()
      {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
          // clrmd crashes the process if os is not Windows, so just return the name of the method
          return nameof(WaitForLifetimeTerminatedEvent);
        }

        using var dataTarget = DataTarget.AttachToProcess(Process.GetCurrentProcess().Id, suspend: false);
        var clrVersion = dataTarget.ClrVersions.SingleOrDefault() ?? throw new Exception("Failed to get single clr from current process");

        using var runtime = clrVersion.CreateRuntime();
        var output = new StringBuilder();
        foreach (var clrThread in runtime.Threads)
        {
          if (!clrThread.IsAlive)
            continue;
          output.AppendLine($"Thread #{clrThread.ManagedThreadId}:");

          foreach (var frame in clrThread.EnumerateStackTrace())
            output.AppendLine($"\tat {frame}");
        }

        return output.ToString();
      }
    }