Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ private static void GeneratePropertyMetadata(StringBuilder sb, PropertyWithDataS
sb.AppendLine(" {");
sb.AppendLine($" PropertyName = \"{propertyName}\",");
sb.AppendLine($" PropertyType = typeof({propertyTypeForTypeof}),");
sb.AppendLine($" ContainingType = typeof({classSymbol.ToDisplayString()}),");
sb.AppendLine($" ContainingType = typeof({propInfo.Property.ContainingType.ToDisplayString()}),");

// Generate CreateDataSource delegate
sb.AppendLine(" CreateDataSource = () =>");
Expand Down
175 changes: 175 additions & 0 deletions TUnit.TestProject/InheritanceSharedTypeRepro.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using TUnit.TestProject.Attributes;

namespace TUnit.TestProject.InheritanceSharedTypeRepro;

// Simulating the user's container wrappers
public class PostgreSqlContainerWrapper : IDisposable, IAsyncDisposable
{
public bool IsDisposed { get; private set; }
public string InstanceId { get; } = Guid.NewGuid().ToString();

public void Dispose()
{
IsDisposed = true;
Console.WriteLine($"PostgreSQL Container {InstanceId} disposed (sync)");
}

public ValueTask DisposeAsync()
{
IsDisposed = true;
Console.WriteLine($"PostgreSQL Container {InstanceId} disposed (async)");
return default;
}
}

public class RabbitMqContainerWrapper : IDisposable, IAsyncDisposable
{
public bool IsDisposed { get; private set; }
public string InstanceId { get; } = Guid.NewGuid().ToString();

public void Dispose()
{
IsDisposed = true;
Console.WriteLine($"RabbitMQ Container {InstanceId} disposed (sync)");
}

public ValueTask DisposeAsync()
{
IsDisposed = true;
Console.WriteLine($"RabbitMQ Container {InstanceId} disposed (async)");
return default;
}
}

// Base application testing server (like in the user's example)
public class BaseApplicationTestingServer<T>
{
// Base functionality
}

// The working scenario - containers defined directly on TestingServer
public class TestingServerWorking : BaseApplicationTestingServer<object>
{
[ClassDataSource<PostgreSqlContainerWrapper>(Shared = SharedType.PerTestSession)]
public required PostgreSqlContainerWrapper PostgresContainerWrapper { get; set; } = new();

[ClassDataSource<RabbitMqContainerWrapper>(Shared = SharedType.PerTestSession)]
public required RabbitMqContainerWrapper RabbitContainerWrapper { get; set; } = new();
}

// The broken scenario - containers defined on intermediate base class
public abstract class ModuleTestingServer : BaseApplicationTestingServer<object>
{
[ClassDataSource<PostgreSqlContainerWrapper>(Shared = SharedType.PerTestSession)]
public required PostgreSqlContainerWrapper PostgresContainerWrapper { get; set; } = new();

[ClassDataSource<RabbitMqContainerWrapper>(Shared = SharedType.PerTestSession)]
public required RabbitMqContainerWrapper RabbitContainerWrapper { get; set; } = new();
}

public class TestingServerBroken : ModuleTestingServer
{
// Inherits the ClassDataSource properties from ModuleTestingServer
}

// Test classes for the working scenario
[EngineTest(ExpectedResult.Pass)]
public class WorkingTestClass1
{
[ClassDataSource<TestingServerWorking>(Shared = SharedType.PerClass)]
public required TestingServerWorking TestingServer { get; set; } = default!;

[Test, NotInParallel(Order = 1)]
public async Task Test1()
{
Console.WriteLine($"WorkingTestClass1.Test1 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}

[Test, NotInParallel(Order = 2)]
public async Task Test2()
{
Console.WriteLine($"WorkingTestClass1.Test2 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}
}

[EngineTest(ExpectedResult.Pass)]
public class WorkingTestClass2
{
[ClassDataSource<TestingServerWorking>(Shared = SharedType.PerClass)]
public required TestingServerWorking TestingServer { get; set; } = default!;

[Test, NotInParallel(Order = 3)]
public async Task Test3()
{
Console.WriteLine($"WorkingTestClass2.Test3 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}

[Test, NotInParallel(Order = 4)]
public async Task Test4()
{
Console.WriteLine($"WorkingTestClass2.Test4 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}
}

// Test classes for the broken scenario
[EngineTest(ExpectedResult.Pass)]
public class BrokenTestClass1
{
[ClassDataSource<TestingServerBroken>(Shared = SharedType.PerClass)]
public required TestingServerBroken TestingServer { get; set; } = default!;

[Test, NotInParallel(Order = 5)]
public async Task Test5()
{
Console.WriteLine($"BrokenTestClass1.Test5 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}

[Test, NotInParallel(Order = 6)]
public async Task Test6()
{
Console.WriteLine($"BrokenTestClass1.Test6 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}
}

[EngineTest(ExpectedResult.Pass)]
public class BrokenTestClass2
{
[ClassDataSource<TestingServerBroken>(Shared = SharedType.PerClass)]
public required TestingServerBroken TestingServer { get; set; } = default!;

[Test, NotInParallel(Order = 7)]
public async Task Test7()
{
Console.WriteLine($"BrokenTestClass2.Test7 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}

[Test, NotInParallel(Order = 8)]
public async Task Test8()
{
Console.WriteLine($"BrokenTestClass2.Test8 - PostgreSQL: {TestingServer.PostgresContainerWrapper.InstanceId}, RabbitMQ: {TestingServer.RabbitContainerWrapper.InstanceId}");

await Assert.That(TestingServer.PostgresContainerWrapper.IsDisposed).IsFalse();
await Assert.That(TestingServer.RabbitContainerWrapper.IsDisposed).IsFalse();
}
}
63 changes: 63 additions & 0 deletions TUnit.TestProject/SimpleInheritanceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using TUnit.TestProject.Attributes;

namespace TUnit.TestProject;

// Simple reproduction of the inheritance issue
public class SimpleContainer : IDisposable
{
public string Id { get; } = Guid.NewGuid().ToString()[..8];
public bool IsDisposed { get; private set; }

public void Dispose()
{
IsDisposed = true;
Console.WriteLine($"Container {Id} disposed");
}
}

// Working scenario - attribute directly on concrete class
public class DirectTestServer
{
[ClassDataSource<SimpleContainer>(Shared = SharedType.PerTestSession)]
public required SimpleContainer Container { get; set; } = new();
}

// Broken scenario - attribute on abstract base class
public abstract class BaseTestServer
{
[ClassDataSource<SimpleContainer>(Shared = SharedType.PerTestSession)]
public required SimpleContainer Container { get; set; } = new();
}

public class InheritedTestServer : BaseTestServer
{
// Inherits the ClassDataSource property
}

[EngineTest(ExpectedResult.Pass)]
public class DirectServerTests
{
[ClassDataSource<DirectTestServer>(Shared = SharedType.PerClass)]
public required DirectTestServer Server { get; set; } = default!;

[Test]
public async Task DirectTest1()
{
Console.WriteLine($"DirectTest1 - Container ID: {Server.Container.Id}");
await Assert.That(Server.Container.IsDisposed).IsFalse();
}
}

[EngineTest(ExpectedResult.Pass)]
public class InheritedServerTests
{
[ClassDataSource<InheritedTestServer>(Shared = SharedType.PerClass)]
public required InheritedTestServer Server { get; set; } = default!;

[Test]
public async Task InheritedTest1()
{
Console.WriteLine($"InheritedTest1 - Container ID: {Server.Container.Id}");
await Assert.That(Server.Container.IsDisposed).IsFalse();
}
}
Loading