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 @@ -187,8 +187,8 @@ private static bool ShouldLoadAssembly(IAssemblySymbol assembly, Compilation com
return false;
}

// Only load assemblies with physical locations
if (!HasPhysicalLocation(assembly, compilation))
// Only load assemblies that will be available at runtime
if (!IsLoadableAtRuntime(assembly, compilation))
{
return false;
}
Expand Down Expand Up @@ -223,12 +223,24 @@ private static bool IsTUnitFrameworkAssembly(IAssemblySymbol assembly)
name.StartsWith("TUnit.Assertions.", StringComparison.Ordinal);
}

private static bool HasPhysicalLocation(IAssemblySymbol assembly, Compilation compilation)
/// <summary>
/// Determines if an assembly will be loadable at runtime via Assembly.Load().
/// Any assembly that has a corresponding reference in the compilation will be available
/// at runtime because it will either be:
/// - A compiled DLL in the output directory (for project references)
/// - A NuGet package assembly in the probing paths (for package references)
/// </summary>
private static bool IsLoadableAtRuntime(IAssemblySymbol assembly, Compilation compilation)
{
// Find the MetadataReference that corresponds to this assembly symbol
var correspondingReference = compilation.References.FirstOrDefault(r =>
SymbolEqualityComparer.Default.Equals(compilation.GetAssemblyOrModuleSymbol(r), assembly));

return correspondingReference is PortableExecutableReference { FilePath: not null and not "" };
// If there's a corresponding reference, the assembly will be available at runtime.
// This includes:
// - PortableExecutableReference: compiled DLLs (NuGet packages, project outputs)
// - CompilationReference: project-to-project references (will be compiled to DLLs)
return correspondingReference != null;
}

private static string GetAssemblyFullName(IAssemblySymbol assemblySymbol)
Expand Down
25 changes: 25 additions & 0 deletions TUnit.TestProject.Library/Hooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,28 @@ public void AfterTests(TestContext testContext)
testContext.StateBag.Items["AfterHit"] = true;
}
}

/// <summary>
/// Test session hook in a library project.
/// This tests the fix for https://github.com/thomhurst/TUnit/issues/4583
/// where hooks in referenced library projects don't execute.
/// </summary>
public static class LibraryTestSessionHooks
{
public static bool BeforeTestSessionWasExecuted { get; private set; }
public static bool AfterTestSessionWasExecuted { get; private set; }

[Before(TestSession)]
public static Task BeforeTestSession()
{
BeforeTestSessionWasExecuted = true;
return Task.CompletedTask;
}

[After(TestSession)]
public static Task AfterTestSession()
{
AfterTestSessionWasExecuted = true;
return Task.CompletedTask;
}
}
20 changes: 20 additions & 0 deletions TUnit.TestProject/Bugs/Issue4583/LibraryTestSessionHookTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using TUnit.TestProject.Attributes;
using TUnit.TestProject.Library;

namespace TUnit.TestProject.Bugs.Issue4583;

/// <summary>
/// Reproduction test for https://github.com/thomhurst/TUnit/issues/4583
/// Tests that [Before(TestSession)] hooks in REFERENCED LIBRARY projects are executed.
/// This is different from issue #4541 which tested hooks in the same project.
/// </summary>
[EngineTest(ExpectedResult.Pass)]
public class LibraryTestSessionHookTests
{
[Test]
public async Task Verify_TestSession_Hook_In_Referenced_Library_Executed()
{
await Assert.That(LibraryTestSessionHooks.BeforeTestSessionWasExecuted).IsTrue()
.Because("[Before(TestSession)] hook in referenced library project should have executed");
}
}
Loading