Skip to content

Commit

Permalink
Dispose TestThread-Container correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
obligaron committed May 17, 2024
1 parent e4e7eaa commit fe48701
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* Support for JSON files added to SpecFlow.ExternalData
* Fix: #120 Capture ExecutionContext after every binding invoke
* MsTest: Use ClassCleanupBehavior.EndOfClass instead of custom implementation (preparation for MsTest v4.0)
* Fix: #123 Dispose objects registred in TestThread-Container correctly

# v1.0.1 - 2024-02-16

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ public void CheckAnyOutputContainsText(string text)
containsAtAll.Should().BeTrue($"either Trx output or program output should contain '{text}'. Trx Output is: {LastTestExecutionResult.TrxOutput}");
}

public void CheckAnyOutputDoesNotContainsText(string text)
{
var textWithoutWhitespace = WithoutWhitespace(text);
bool trxContainsEntry = WithoutWhitespace(LastTestExecutionResult.TrxOutput).Contains(textWithoutWhitespace);
bool outputContainsEntry = WithoutWhitespace(LastTestExecutionResult.Output).Contains(textWithoutWhitespace);
bool containsAtAll = trxContainsEntry || outputContainsEntry;
containsAtAll.Should().BeFalse($"nether Trx output or program output should contain '{text}'. Trx Output is: {LastTestExecutionResult.TrxOutput}");
}

public static string WithoutWhitespace(string input)
{
return sWhitespace.Replace(input, string.Empty);
Expand Down
10 changes: 9 additions & 1 deletion Reqnroll/TestRunnerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class TestRunnerManager : ITestRunnerManager
protected readonly ITestTracer _testTracer;

private readonly ConcurrentDictionary<string, ITestRunner> _testRunnerRegistry = new();
private readonly ConcurrentBag<IObjectContainer> _testThreadContainers = new();
public bool IsTestRunInitialized { get; private set; }
private int _wasDisposed = 0;
private int _wasSingletonInstanceDisabled = 0;
Expand Down Expand Up @@ -119,7 +120,7 @@ public async Task FireTestRunStartAsync()
protected virtual ITestRunner CreateTestRunnerInstance()
{
var testThreadContainer = _containerBuilder.CreateTestThreadContainer(_globalContainer);

_testThreadContainers.Add(testThreadContainer);
return testThreadContainer.Resolve<ITestRunner>();
}

Expand Down Expand Up @@ -173,10 +174,17 @@ public virtual async Task DisposeAsync()
{
await FireTestRunEndAsync();

foreach (var objectContainer in _testThreadContainers)
{
objectContainer.Dispose();
}

// this call dispose on this object, but the disposeLockObj will avoid double execution
_globalContainer.Dispose();

_testRunnerRegistry.Clear();
while (_testThreadContainers.TryTake(out _))
; // TryTake() used instead of Clear(), because Clear() is not available in .NET Standard
OnTestRunnerManagerDisposed(this);
}
}
Expand Down
91 changes: 91 additions & 0 deletions Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,5 +289,96 @@ public void Scenario_outline_examples_gather_tags_and_parameters()

#endregion

[TestMethod]
public void Check_pooled_pbjects_are_reused_and_disposed()
{
AddFeatureFile("""
Feature: DispoableFeatureA
Scenario: ScenarioA1
When something happens
Scenario: ScenarioA2
When something happens
""");

AddFeatureFile("""
Feature: DispoableFeatureB
Scenario: ScenarioB1
When something happens
Scenario: ScenarioB2
When something happens
""");

AddBindingClass("""
using Reqnroll;
using Reqnroll.Tracing;
[Binding]
public sealed class DispoableStepDefinitions
{
private readonly ITestRunner _TestRunner;
sealed class MyDummyResource : IDisposable
{
public static int CreatedCount { get; private set; }
public static int DisposedCount { get; private set; }
private bool _Disposed;
private readonly ITraceListener _traceListener;
public MyDummyResource(ITraceListener traceListener)
{
_traceListener = traceListener;
CreatedCount += 1;
WriteLine(FormattableString.Invariant($"CreatedCount: {CreatedCount}."));
}
public void Dispose()
{
if (_Disposed)
throw new ObjectDisposedException(GetType().FullName);
_Disposed = true;
DisposedCount += 1;
WriteLine(FormattableString.Invariant($"DisposedCount: {DisposedCount}."));
}
void WriteLine(string line)
{
_traceListener.WriteTestOutput(line);
// Write to StandardOutput, to ensure that SystemTests see the output when disposing TestThreadContext
using var standardOutput = new StreamWriter(Console.OpenStandardOutput());
standardOutput.WriteLine(line);
}
}
public DispoableStepDefinitions(ITestRunner testRunner)
{
_TestRunner = testRunner;
}
[When(".*"), BeforeScenario, AfterScenario]
public void ScenarioHooks()
{
_TestRunner.ScenarioContext.ScenarioContainer.Resolve<TestThreadContext>().TestThreadContainer.Resolve<MyDummyResource>();
}
[BeforeFeature, AfterFeature]
public static void FeatureHooks(ITestRunner testRunner)
{
testRunner.FeatureContext.FeatureContainer.Resolve<TestThreadContext>().TestThreadContainer.Resolve<MyDummyResource>();
}
}
""");
ExecuteTests();
ShouldAllScenariosPass(4);
CheckAnyOutputContainsText("CreatedCount: 1.");
CheckAnyOutputDoesNotContainsText("CreatedCount: 2.");
CheckAnyOutputContainsText("DisposedCount: 1.");
CheckAnyOutputDoesNotContainsText("DisposedCount: 2.");
}

//TODO: test parallel execution (details TBD) - maybe this should be in a separate test class
}
12 changes: 12 additions & 0 deletions Tests/Reqnroll.SystemTests/SystemTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ protected int ConfirmAllTestsRan(int? expectedNrOfTestsSpec)
return expectedNrOfTests;
}

protected void CheckAnyOutputContainsText(string text)
{
_vsTestExecutionDriver.LastTestExecutionResult.Should().NotBeNull();
_vsTestExecutionDriver.CheckAnyOutputContainsText(text);
}

protected void CheckAnyOutputDoesNotContainsText(string text)
{
_vsTestExecutionDriver.LastTestExecutionResult.Should().NotBeNull();
_vsTestExecutionDriver.CheckAnyOutputDoesNotContainsText(text);
}

protected void AddHookBinding(string eventType, string? name = null, string code = "")
{
_projectsDriver.AddHookBinding(eventType, name, code: code);
Expand Down

0 comments on commit fe48701

Please sign in to comment.