diff --git a/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete1.verified.txt b/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete1.verified.txt
index d4bda4466f..16cf573c7b 100644
--- a/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete1.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete1.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -50,7 +50,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass1.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete2.verified.txt b/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete2.verified.txt
index 85d3699447..47b36aa0d7 100644
--- a/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete2.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/AbstractTests.Concrete2.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -144,7 +144,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass2_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass2.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
@@ -219,7 +219,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass1.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/BasicTests.cs b/TUnit.Core.SourceGenerator.Tests/BasicTests.cs
index 8166bea3da..5480a6bd76 100644
--- a/TUnit.Core.SourceGenerator.Tests/BasicTests.cs
+++ b/TUnit.Core.SourceGenerator.Tests/BasicTests.cs
@@ -1,18 +1,34 @@
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
using TUnit.Core.SourceGenerator.Tests.Options;
namespace TUnit.Core.SourceGenerator.Tests;
internal class BasicTests : TestsBase
{
+ private static readonly string InputFile = Path.Combine(Git.RootDirectory.FullName, "TUnit.TestProject", "BasicTests.cs");
+
[Test]
- public Task Test() => RunTest(Path.Combine(Git.RootDirectory.FullName,
- "TUnit.TestProject",
- "BasicTests.cs"),
+ public Task Test() => RunTest(InputFile,
new RunTestOptions
{
VerifyConfigurator = verify => verify.UniqueForTargetFrameworkAndVersion()
},
async generatedFiles =>
{
- });
+ var source = await File.ReadAllTextAsync(InputFile);
+ var syntaxTree = CSharpSyntaxTree.ParseText(source);
+ var firstMethod = syntaxTree.GetRoot()
+ .DescendantNodes()
+ .OfType()
+ .First(x => x.Identifier.ValueText == "SynchronousTest");
+
+ var lineSpan = firstMethod.GetLocation().GetLineSpan();
+ var generatedCode = string.Join(Environment.NewLine, generatedFiles);
+
+ await Assert.That(generatedCode).Contains($"LineNumber = {lineSpan.StartLinePosition.Line + 1},");
+ await Assert.That(generatedCode).Contains($"StartColumnNumber = {lineSpan.StartLinePosition.Character + 1},");
+ await Assert.That(generatedCode).Contains($"EndLineNumber = {lineSpan.EndLinePosition.Line + 1},");
+ await Assert.That(generatedCode).Contains($"EndColumnNumber = {lineSpan.EndLinePosition.Character + 1},");
+ });
}
diff --git a/TUnit.Core.SourceGenerator.Tests/Bugs2971NullableTypeTest.cs b/TUnit.Core.SourceGenerator.Tests/Bugs2971NullableTypeTest.cs
index b2c91106fc..97187bcef8 100644
--- a/TUnit.Core.SourceGenerator.Tests/Bugs2971NullableTypeTest.cs
+++ b/TUnit.Core.SourceGenerator.Tests/Bugs2971NullableTypeTest.cs
@@ -23,7 +23,11 @@ public Task Test() => RunTest(Path.Combine(Git.RootDirectory.FullName,
{
// Scrub GUIDs from generated files before verification (same as TestsBase)
var scrubbedFiles = generatedFiles.Select(file => ScrubGuids(file)).ToArray();
- await Verify(scrubbedFiles).UniqueForTargetFrameworkAndVersion();
+ await Verify(scrubbedFiles)
+ .ScrubLinesContaining("StartColumnNumber = ")
+ .ScrubLinesContaining("EndLineNumber = ")
+ .ScrubLinesContaining("EndColumnNumber = ")
+ .UniqueForTargetFrameworkAndVersion();
});
private string ScrubGuids(string text)
@@ -50,4 +54,4 @@ private string ScrubGuids(string text)
return scrubbedText;
}
-}
\ No newline at end of file
+}
diff --git a/TUnit.Core.SourceGenerator.Tests/ConcreteClassTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/ConcreteClassTests.Test.verified.txt
index 85d3699447..47b36aa0d7 100644
--- a/TUnit.Core.SourceGenerator.Tests/ConcreteClassTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/ConcreteClassTests.Test.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -144,7 +144,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass2_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass2.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
@@ -219,7 +219,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass1.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/InheritedPropertySetterTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/InheritedPropertySetterTests.Test.verified.txt
index 9d930b2712..1f9a7f369a 100644
--- a/TUnit.Core.SourceGenerator.Tests/InheritedPropertySetterTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/InheritedPropertySetterTests.Test.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -266,7 +266,7 @@ internal static class TUnit_TestProject_InheritedPropertySetterTests_Test_TestSo
MethodName = "Test",
FullyQualifiedName = "TUnit.TestProject.InheritedPropertySetterTests.Test",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 70,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/InheritedTestsFromDifferentProjectTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/InheritedTestsFromDifferentProjectTests.Test.verified.txt
index 1679c6d689..62bb573445 100644
--- a/TUnit.Core.SourceGenerator.Tests/InheritedTestsFromDifferentProjectTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/InheritedTestsFromDifferentProjectTests.Test.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -292,7 +292,7 @@ internal static class TUnit_TestProject_InheritedTestsFromDifferentProjectTests_
MethodName = "BaseTest",
FullyQualifiedName = "TUnit.TestProject.InheritedTestsFromDifferentProjectTests.BaseTest",
FilePath = @"",
- LineNumber = 5,
+ LineNumber = 8,
Categories = new string[] { "BaseCategory" },
Properties = global::System.Array.Empty(),
HasDataSource = false,
@@ -372,7 +372,7 @@ internal static class TUnit_TestProject_InheritedTestsFromDifferentProjectTests_
MethodName = "BaseTestWithMultipleCategories",
FullyQualifiedName = "TUnit.TestProject.InheritedTestsFromDifferentProjectTests.BaseTestWithMultipleCategories",
FilePath = @"",
- LineNumber = 5,
+ LineNumber = 15,
Categories = new string[] { "AnotherBaseCategory", "MultipleCategories" },
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/InheritsTestsAbstractTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/InheritsTestsAbstractTests.Test.verified.txt
index 85d3699447..47b36aa0d7 100644
--- a/TUnit.Core.SourceGenerator.Tests/InheritsTestsAbstractTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/InheritsTestsAbstractTests.Test.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -144,7 +144,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass2_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass2.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
@@ -219,7 +219,7 @@ internal static class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
MethodName = "AssertClassName",
FullyQualifiedName = "TUnit.TestProject.AbstractTests.ConcreteClass1.AssertClassName",
FilePath = @"",
- LineNumber = 3,
+ LineNumber = 6,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/InheritsTestsTests.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/InheritsTestsTests.Test.verified.txt
index bc92075324..ce09d93dc1 100644
--- a/TUnit.Core.SourceGenerator.Tests/InheritsTestsTests.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/InheritsTestsTests.Test.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -198,7 +198,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo
MethodName = "Test",
FullyQualifiedName = "TUnit.TestProject.Bugs._1924.None.Tests.Test",
FilePath = @"",
- LineNumber = 23,
+ LineNumber = 16,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
@@ -298,7 +298,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo
MethodName = "Test",
FullyQualifiedName = "TUnit.TestProject.Bugs._1924.None.Tests2.Test",
FilePath = @"",
- LineNumber = 26,
+ LineNumber = 16,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
@@ -398,7 +398,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new glo
MethodName = "Test",
FullyQualifiedName = "TUnit.TestProject.Bugs._1924.None.Tests3.Test",
FilePath = @"",
- LineNumber = 29,
+ LineNumber = 16,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet10_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet10_0.verified.txt
index e12b6432db..8d771b7d9c 100644
--- a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet10_0.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet10_0.verified.txt
@@ -141,7 +141,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test2",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test2",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 12,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
@@ -235,7 +235,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test3",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test3",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 19,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet8_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet8_0.verified.txt
index e12b6432db..8d771b7d9c 100644
--- a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet8_0.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet8_0.verified.txt
@@ -141,7 +141,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test2",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test2",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 12,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
@@ -235,7 +235,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test3",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test3",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 19,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet9_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet9_0.verified.txt
index e12b6432db..8d771b7d9c 100644
--- a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet9_0.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.DotNet9_0.verified.txt
@@ -141,7 +141,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test2",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test2",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 12,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
@@ -235,7 +235,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test3",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test3",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 19,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.Net4_7.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.Net4_7.verified.txt
index 862d9edb8b..33c81a7ab6 100644
--- a/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.Net4_7.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/Tests1889.Test.Net4_7.verified.txt
@@ -141,7 +141,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test2",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test2",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 12,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
@@ -235,7 +235,7 @@ global::TUnit.Core.ParameterMetadataFactory.Create(typeof(bool), "condition", ne
MethodName = "Test3",
FullyQualifiedName = "TUnit.TestProject.Bugs._1889.DerivedTest.Test3",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 19,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = true,
diff --git a/TUnit.Core.SourceGenerator.Tests/Tests1899.Test.verified.txt b/TUnit.Core.SourceGenerator.Tests/Tests1899.Test.verified.txt
index 43d87b6af9..cfcaa07987 100644
--- a/TUnit.Core.SourceGenerator.Tests/Tests1899.Test.verified.txt
+++ b/TUnit.Core.SourceGenerator.Tests/Tests1899.Test.verified.txt
@@ -1,4 +1,4 @@
-//
+//
#pragma warning disable
#nullable enable
@@ -52,7 +52,7 @@ internal static class TUnit_TestProject_Bugs__1899_DerivedTest_Test1_TestSource
MethodName = "Test1",
FullyQualifiedName = "TUnit.TestProject.Bugs._1899.DerivedTest.Test1",
FilePath = @"",
- LineNumber = 6,
+ LineNumber = 14,
Categories = global::System.Array.Empty(),
Properties = global::System.Array.Empty(),
HasDataSource = false,
diff --git a/TUnit.Core.SourceGenerator.Tests/TestsBase.cs b/TUnit.Core.SourceGenerator.Tests/TestsBase.cs
index 91ff554b83..3fbf1c12ec 100644
--- a/TUnit.Core.SourceGenerator.Tests/TestsBase.cs
+++ b/TUnit.Core.SourceGenerator.Tests/TestsBase.cs
@@ -161,7 +161,10 @@ Have you added required references and additional files?
// Scrub GUIDs from generated files before verification
var scrubbedFiles = generatedFiles.Select(file => Scrub(file)).ToArray();
var verifyTask = Verify(scrubbedFiles)
- .ScrubFilePaths();
+ .ScrubFilePaths()
+ .ScrubLinesContaining("StartColumnNumber = ")
+ .ScrubLinesContaining("EndLineNumber = ")
+ .ScrubLinesContaining("EndColumnNumber = ");
if (runTestOptions.VerifyConfigurator != null)
{
diff --git a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
index 226f8a6119..92f353e4f2 100644
--- a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
+++ b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
@@ -160,14 +160,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var isGenericType = containingType is { IsGenericType: true, TypeParameters.Length: > 0 };
var isGenericMethod = methodSymbol is { IsGenericMethod: true };
- var (filePath, lineNumber) = GetTestMethodSourceLocation(methodSyntax, testAttribute);
+ var location = GetTestMethodSourceLocation(methodSyntax, testAttribute);
return new TestMethodMetadata
{
MethodSymbol = methodSymbol ?? throw new InvalidOperationException("Symbol is not a method"),
TypeSymbol = containingType,
- FilePath = filePath,
- LineNumber = lineNumber,
+ FilePath = location.FilePath,
+ LineNumber = location.LineNumber,
+ StartColumnNumber = location.StartColumnNumber,
+ EndLineNumber = location.EndLineNumber,
+ EndColumnNumber = location.EndColumnNumber,
TestAttribute = context.Attributes.First(),
Context = context,
CompilationContext = compilationContext,
@@ -222,7 +225,7 @@ private static void GenerateInheritedTestSources(SourceProductionContext context
{
continue;
}
- var (filePath, lineNumber) = GetTestMethodSourceLocation(method, testAttribute, classInfo);
+ var location = GetTestMethodSourceLocation(method, testAttribute, classInfo);
// If the method is from a generic base class, use the constructed version from the inheritance hierarchy
var typeForMetadata = classInfo.TypeSymbol;
@@ -246,8 +249,11 @@ private static void GenerateInheritedTestSources(SourceProductionContext context
{
MethodSymbol = concreteMethod ?? method, // Use concrete method if found, otherwise base method
TypeSymbol = typeForMetadata, // Use constructed generic base if applicable
- FilePath = filePath,
- LineNumber = lineNumber,
+ FilePath = location.FilePath,
+ LineNumber = location.LineNumber,
+ StartColumnNumber = location.StartColumnNumber,
+ EndLineNumber = location.EndLineNumber,
+ EndColumnNumber = location.EndColumnNumber,
TestAttribute = testAttribute,
Context = classInfo.Context, // Use class context to access Compilation
CompilationContext = classInfo.CompilationContext,
@@ -1050,8 +1056,7 @@ private static void EmitTestEntryFields(CodeWriter writer, TestMethodMetadata te
writer.AppendLine($"MethodName = \"{EscapeString(entry.TestName)}\",");
writer.AppendLine($"FullyQualifiedName = \"{EscapeString(fullyQualifiedName)}\",");
- writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
- writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ WriteSourceLocationMetadata(writer, testMethod);
writer.AppendLine($"Categories = {categoriesArray},");
writer.AppendLine($"Properties = {propertiesArray},");
writer.AppendLine($"HasDataSource = {(hasDataSource ? "true" : "false")},");
@@ -1195,15 +1200,22 @@ private static void GenerateMetadata(CodeWriter writer, TestMethodMetadata testM
// Inheritance depth
writer.AppendLine($"InheritanceDepth = {testMethod.InheritanceDepth},");
- // File location metadata
- writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
- writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ WriteSourceLocationMetadata(writer, testMethod);
// Method metadata
writer.Append("MethodMetadata = ");
SourceInformationWriter.GenerateMethodInformation(writer, compilation, testMethod.TypeSymbol, testMethod.MethodSymbol, null, ',');
}
+ private static void WriteSourceLocationMetadata(CodeWriter writer, TestMethodMetadata testMethod)
+ {
+ writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
+ writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ writer.AppendLine($"StartColumnNumber = {testMethod.StartColumnNumber},");
+ writer.AppendLine($"EndLineNumber = {testMethod.EndLineNumber},");
+ writer.AppendLine($"EndColumnNumber = {testMethod.EndColumnNumber},");
+ }
+
private static void GenerateMetadataForConcreteInstantiation(CodeWriter writer, TestMethodMetadata testMethod)
{
var compilation = testMethod.Context!.Value.SemanticModel.Compilation;
@@ -1243,9 +1255,7 @@ private static void GenerateMetadataForConcreteInstantiation(CodeWriter writer,
// Inheritance depth
writer.AppendLine($"InheritanceDepth = {testMethod.InheritanceDepth},");
- // File location metadata
- writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
- writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ WriteSourceLocationMetadata(writer, testMethod);
// Method metadata
writer.Append("MethodMetadata = ");
@@ -3068,8 +3078,7 @@ private static string PreGenerateTestEntryDataFields(TestMethodMetadata testMeth
writer.AppendLine($"MethodName = \"{EscapeString(methodName)}\",");
writer.AppendLine($"FullyQualifiedName = \"{EscapeString(fullyQualifiedName)}\",");
- writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
- writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ WriteSourceLocationMetadata(writer, testMethod);
writer.AppendLine($"Categories = {categoriesArray},");
writer.AppendLine($"Properties = {propertiesArray},");
writer.AppendLine($"HasDataSource = {(hasDataSource ? "true" : "false")},");
@@ -3981,71 +3990,87 @@ private static bool TypesMatchForInheritance(ITypeSymbol derivedType, ITypeSymbo
return false;
}
- private static (string filePath, int lineNumber) GetTestMethodSourceLocation(
+ private static TestSourceLocation GetTestMethodSourceLocation(
MethodDeclarationSyntax methodSyntax,
AttributeData testAttribute)
{
- // Prioritize TestAttribute's File/Line from [CallerFilePath]/[CallerLineNumber] first
var attrFilePath = testAttribute.ConstructorArguments.ElementAtOrDefault(0).Value?.ToString();
- if (!string.IsNullOrEmpty(attrFilePath))
- {
- var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? 0;
- if (attrLineNumber > 0)
- {
- return (attrFilePath!, attrLineNumber);
- }
- }
+ var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? 0;
- // Fall back to method syntax location
var methodLocation = methodSyntax.GetLocation();
- var filePath = methodLocation.SourceTree?.FilePath;
- if (!string.IsNullOrEmpty(filePath))
+ if (methodLocation.IsInSource)
{
- var lineNumber = methodLocation.GetLineSpan().StartLinePosition.Line + 1;
- return (filePath!, lineNumber);
+ return CreateSourceLocation(
+ methodLocation.GetLineSpan(),
+ !string.IsNullOrEmpty(attrFilePath) ? attrFilePath! : methodLocation.SourceTree?.FilePath ?? methodSyntax.SyntaxTree.FilePath ?? "");
}
- // Final fallback
- filePath = methodSyntax.SyntaxTree.FilePath ?? "";
- var fallbackLineNumber = methodLocation.GetLineSpan().StartLinePosition.Line + 1;
- return (filePath, fallbackLineNumber);
+ return CreateFallbackSourceLocation(
+ !string.IsNullOrEmpty(attrFilePath) ? attrFilePath! : methodSyntax.SyntaxTree.FilePath ?? "",
+ attrLineNumber);
}
- private static (string filePath, int lineNumber) GetTestMethodSourceLocation(
+ private static TestSourceLocation GetTestMethodSourceLocation(
IMethodSymbol method,
AttributeData testAttribute,
InheritsTestsClassMetadata classInfo)
{
- // Prioritize TestAttribute's File/Line from [CallerFilePath]/[CallerLineNumber] first
var attrFilePath = testAttribute.ConstructorArguments.ElementAtOrDefault(0).Value?.ToString();
- if (!string.IsNullOrEmpty(attrFilePath))
- {
- var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? 0;
- if (attrLineNumber > 0)
- {
- return (attrFilePath!, attrLineNumber);
- }
- }
+ var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? 0;
- // Fall back to method symbol location
var methodLocation = method.Locations.FirstOrDefault();
if (methodLocation != null && methodLocation.IsInSource)
{
- var filePath = methodLocation.SourceTree?.FilePath;
- if (!string.IsNullOrEmpty(filePath))
- {
- var lineNumber = methodLocation.GetLineSpan().StartLinePosition.Line + 1;
- return (filePath!, lineNumber);
- }
+ return CreateSourceLocation(
+ methodLocation.GetLineSpan(),
+ !string.IsNullOrEmpty(attrFilePath) ? attrFilePath! : methodLocation.SourceTree?.FilePath ?? "");
}
- // Final fallback to class location
var classLocation = classInfo.ClassSyntax.GetLocation();
- var derivedFilePath = classLocation.SourceTree?.FilePath ?? classInfo.ClassSyntax.SyntaxTree.FilePath ?? "";
- var derivedLineNumber = classLocation.GetLineSpan().StartLinePosition.Line + 1;
- return (derivedFilePath, derivedLineNumber);
+ if (classLocation.IsInSource)
+ {
+ return CreateSourceLocation(
+ classLocation.GetLineSpan(),
+ !string.IsNullOrEmpty(attrFilePath) ? attrFilePath! : classLocation.SourceTree?.FilePath ?? classInfo.ClassSyntax.SyntaxTree.FilePath ?? "");
+ }
+
+ return CreateFallbackSourceLocation(
+ !string.IsNullOrEmpty(attrFilePath) ? attrFilePath! : classInfo.ClassSyntax.SyntaxTree.FilePath ?? "",
+ attrLineNumber);
}
+ private static TestSourceLocation CreateSourceLocation(FileLinePositionSpan lineSpan, string filePath)
+ {
+ // Roslyn reports zero-based line/character positions; TUnit stores user-facing source
+ // coordinates as one-based values alongside the existing LineNumber convention.
+ return new TestSourceLocation(
+ filePath,
+ lineSpan.StartLinePosition.Line + 1,
+ lineSpan.StartLinePosition.Character + 1,
+ lineSpan.EndLinePosition.Line + 1,
+ lineSpan.EndLinePosition.Character + 1);
+ }
+
+ private static TestSourceLocation CreateFallbackSourceLocation(string filePath, int lineNumber)
+ {
+ // Exact Roslyn span data was unavailable. Preserve any caller-supplied start line, and
+ // use zero-valued columns to indicate that the precise character range could not be determined.
+ var resolvedLineNumber = lineNumber > 0 ? lineNumber : 0;
+ return new TestSourceLocation(filePath, resolvedLineNumber, 0, resolvedLineNumber, 0);
+ }
+
+ ///
+ /// Source coordinates for a discovered test.
+ /// Line and column values use one-based numbering; zero-valued columns indicate that the
+ /// precise character span could not be determined for the fallback location.
+ ///
+ private readonly record struct TestSourceLocation(
+ string FilePath,
+ int LineNumber,
+ int StartColumnNumber,
+ int EndLineNumber,
+ int EndColumnNumber);
+
private static void GenerateReflectionFieldAccessors(CodeWriter writer, INamedTypeSymbol typeSymbol)
{
// Find all init-only properties with data source attributes
@@ -6114,8 +6139,7 @@ private static void GenerateConcreteMetadataWithFilteredDataSources(
GeneratePropertyInjections(writer, concreteTypeSymbol, concreteTypeSymbol.GloballyQualified());
// Other metadata
- writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
- writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ WriteSourceLocationMetadata(writer, testMethod);
writer.AppendLine($"InheritanceDepth = {testMethod.InheritanceDepth},");
writer.AppendLine("TestSessionId = testSessionId,");
@@ -6462,8 +6486,7 @@ private static void GenerateConcreteTestMetadataForNonGeneric(
// Generate typed invoker
GenerateTypedInvokers(writer, testMethod, className);
- writer.AppendLine($"FilePath = @\"{(testMethod.FilePath ?? "").Replace("\\", "\\\\")}\",");
- writer.AppendLine($"LineNumber = {testMethod.LineNumber},");
+ WriteSourceLocationMetadata(writer, testMethod);
writer.Unindent();
writer.AppendLine("};");
diff --git a/TUnit.Core.SourceGenerator/Models/TestMethodMetadata.cs b/TUnit.Core.SourceGenerator/Models/TestMethodMetadata.cs
index 33616e1e7d..0d3713a44a 100644
--- a/TUnit.Core.SourceGenerator/Models/TestMethodMetadata.cs
+++ b/TUnit.Core.SourceGenerator/Models/TestMethodMetadata.cs
@@ -18,6 +18,9 @@ public class TestMethodMetadata : IEquatable
public required INamedTypeSymbol TypeSymbol { get; init; }
public required string FilePath { get; init; }
public required int LineNumber { get; init; }
+ public required int StartColumnNumber { get; init; }
+ public required int EndLineNumber { get; init; }
+ public required int EndColumnNumber { get; init; }
public required AttributeData TestAttribute { get; init; }
public GeneratorAttributeSyntaxContext? Context { get; init; }
public required CompilationContext CompilationContext { get; init; }
@@ -49,6 +52,9 @@ public bool Equals(TestMethodMetadata? other)
SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) &&
FilePath == other.FilePath &&
LineNumber == other.LineNumber &&
+ StartColumnNumber == other.StartColumnNumber &&
+ EndLineNumber == other.EndLineNumber &&
+ EndColumnNumber == other.EndColumnNumber &&
IsGenericType == other.IsGenericType &&
IsGenericMethod == other.IsGenericMethod &&
InheritanceDepth == other.InheritanceDepth;
@@ -68,6 +74,9 @@ public override int GetHashCode()
hashCode = (hashCode * 397) ^ SymbolEqualityComparer.Default.GetHashCode(TypeSymbol);
hashCode = (hashCode * 397) ^ FilePath.GetHashCode();
hashCode = (hashCode * 397) ^ LineNumber;
+ hashCode = (hashCode * 397) ^ StartColumnNumber;
+ hashCode = (hashCode * 397) ^ EndLineNumber;
+ hashCode = (hashCode * 397) ^ EndColumnNumber;
hashCode = (hashCode * 397) ^ IsGenericType.GetHashCode();
hashCode = (hashCode * 397) ^ IsGenericMethod.GetHashCode();
hashCode = (hashCode * 397) ^ InheritanceDepth;
diff --git a/TUnit.Core/TestDetails.cs b/TUnit.Core/TestDetails.cs
index 8c25a03311..119f493605 100644
--- a/TUnit.Core/TestDetails.cs
+++ b/TUnit.Core/TestDetails.cs
@@ -49,6 +49,9 @@ public partial class TestDetails : ITestIdentity, ITestClass, ITestMethod, ITest
public required MethodMetadata MethodMetadata { get; set; }
public string TestFilePath { get; set; } = "";
public int TestLineNumber { get; set; }
+ internal int TestStartColumnNumber { get; set; }
+ internal int TestEndLineNumber { get; set; }
+ internal int TestEndColumnNumber { get; set; }
public required Type ReturnType { get; set; }
// Lazy — the vast majority of tests use zero injected properties, so we skip the
// per-test ConcurrentDictionary allocation (~200+ bytes) until there's actually
diff --git a/TUnit.Core/TestEntry.cs b/TUnit.Core/TestEntry.cs
index a16d5a2fdc..a7505c05b9 100644
--- a/TUnit.Core/TestEntry.cs
+++ b/TUnit.Core/TestEntry.cs
@@ -36,9 +36,18 @@ public sealed class TestEntry<
/// Source file path.
public required string FilePath { get; init; }
- /// Source line number.
+ /// Source start line number. This preserves the existing LineNumber semantics for backwards compatibility.
public required int LineNumber { get; init; }
+ /// Source start column number. Uses a one-based column index; zero indicates the exact column is unavailable for fallback/no-span locations.
+ public int StartColumnNumber { get; init; }
+
+ /// Source end line number.
+ public int EndLineNumber { get; init; }
+
+ /// Source end column number. Uses a one-based column index; zero indicates the exact column is unavailable for fallback/no-span locations.
+ public int EndColumnNumber { get; init; }
+
/// Pre-extracted categories for fast filtering.
public string[] Categories { get; init; } = [];
@@ -127,6 +136,9 @@ internal TestMetadata ToTestMetadata(string testSessionId)
AttributeGroupIndex = AttributeGroupIndex,
FilePath = FilePath,
LineNumber = LineNumber,
+ StartColumnNumber = StartColumnNumber,
+ EndLineNumber = EndLineNumber,
+ EndColumnNumber = EndColumnNumber,
MethodMetadata = MethodMetadata,
RepeatCount = RepeatCount > 0 ? RepeatCount : null,
TestSessionId = testSessionId,
diff --git a/TUnit.Core/TestMetadata.cs b/TUnit.Core/TestMetadata.cs
index c87581862e..894a0b857e 100644
--- a/TUnit.Core/TestMetadata.cs
+++ b/TUnit.Core/TestMetadata.cs
@@ -39,6 +39,12 @@ public abstract class TestMetadata
public required int LineNumber { get; init; }
+ internal int StartColumnNumber { get; init; }
+
+ internal int EndLineNumber { get; init; }
+
+ internal int EndColumnNumber { get; init; }
+
public required MethodMetadata MethodMetadata { get; init; }
public GenericTypeInfo? GenericTypeInfo { get; init; }
diff --git a/TUnit.Engine.Tests/TestNodeLocationTests.cs b/TUnit.Engine.Tests/TestNodeLocationTests.cs
new file mode 100644
index 0000000000..c1d218e323
--- /dev/null
+++ b/TUnit.Engine.Tests/TestNodeLocationTests.cs
@@ -0,0 +1,147 @@
+#pragma warning disable TPEXP
+
+using Microsoft.Testing.Platform.Extensions.Messages;
+using Shouldly;
+using TUnit.Core;
+using TUnit.Engine.Extensions;
+
+namespace TUnit.Engine.Tests;
+
+public class TestNodeLocationTests
+{
+ [Test]
+ public void ToTestNode_Uses_Source_Span_For_Mtp_File_Location()
+ {
+ TestExtensions.ClearCaches();
+
+ var context = CreateTestContext(
+ testId: Guid.NewGuid().ToString("N"),
+ filePath: @"C:\tests\SampleTests.cs",
+ lineNumber: 12,
+ startColumnNumber: 5,
+ endLineNumber: 16,
+ endColumnNumber: 6);
+
+ var node = context.ToTestNode(DiscoveredTestNodeStateProperty.CachedInstance);
+
+ var location = node.Properties.AsEnumerable()
+ .OfType()
+ .Single();
+
+ location.FilePath.ShouldBe(@"C:\tests\SampleTests.cs");
+ location.LineSpan.Start.Line.ShouldBe(12);
+ location.LineSpan.Start.Column.ShouldBe(5);
+ location.LineSpan.End.Line.ShouldBe(16);
+ location.LineSpan.End.Column.ShouldBe(6);
+ }
+
+ [Test]
+ public void ToTestNode_Falls_Back_To_Start_Line_When_End_Line_Is_Unavailable()
+ {
+ TestExtensions.ClearCaches();
+
+ var context = CreateTestContext(
+ testId: Guid.NewGuid().ToString("N"),
+ filePath: @"C:\tests\SampleTests.cs",
+ lineNumber: 12,
+ startColumnNumber: 0,
+ endLineNumber: 0,
+ endColumnNumber: 0);
+
+ var node = context.ToTestNode(DiscoveredTestNodeStateProperty.CachedInstance);
+
+ var location = node.Properties.AsEnumerable()
+ .OfType()
+ .Single();
+
+ location.LineSpan.Start.Line.ShouldBe(12);
+ location.LineSpan.Start.Column.ShouldBe(0);
+ location.LineSpan.End.Line.ShouldBe(12);
+ location.LineSpan.End.Column.ShouldBe(0);
+ }
+
+ private static TestContext CreateTestContext(
+ string testId,
+ string filePath,
+ int lineNumber,
+ int startColumnNumber,
+ int endLineNumber,
+ int endColumnNumber)
+ {
+ var classMetadata = new ClassMetadata
+ {
+ Type = typeof(TestNodeLocationTests),
+ TypeInfo = new ConcreteType(typeof(TestNodeLocationTests)),
+ Name = nameof(TestNodeLocationTests),
+ Namespace = typeof(TestNodeLocationTests).Namespace,
+ Assembly = new AssemblyMetadata
+ {
+ Name = typeof(TestNodeLocationTests).Assembly.GetName().Name ?? string.Empty
+ },
+ Parent = null,
+ Parameters = [],
+ Properties = []
+ };
+
+ var methodMetadata = new MethodMetadata
+ {
+ Type = typeof(TestNodeLocationTests),
+ TypeInfo = new ConcreteType(typeof(TestNodeLocationTests)),
+ Name = nameof(ToTestNode_Uses_Source_Span_For_Mtp_File_Location),
+ GenericTypeCount = 0,
+ ReturnType = typeof(void),
+ ReturnTypeInfo = new ConcreteType(typeof(void)),
+ Parameters = [],
+ Class = classMetadata
+ };
+
+ var beforeDiscoveryContext = new BeforeTestDiscoveryContext { TestFilter = null };
+ var discoveryContext = new TestDiscoveryContext(beforeDiscoveryContext) { TestFilter = null };
+ var sessionContext = new TestSessionContext(discoveryContext)
+ {
+ Id = Guid.NewGuid().ToString("N"),
+ TestFilter = null
+ };
+ var assemblyContext = new AssemblyHookContext(sessionContext)
+ {
+ Assembly = typeof(TestNodeLocationTests).Assembly
+ };
+ var classContext = new ClassHookContext(assemblyContext)
+ {
+ ClassType = typeof(TestNodeLocationTests)
+ };
+ var builderContext = new TestBuilderContext
+ {
+ TestMetadata = methodMetadata
+ };
+
+ var context = new TestContext(testId, EmptyServiceProvider.Instance, classContext, builderContext, CancellationToken.None);
+ context.TestDetails = new TestDetails([])
+ {
+ TestId = testId,
+ TestName = methodMetadata.Name,
+ ClassType = typeof(TestNodeLocationTests),
+ MethodName = methodMetadata.Name,
+ ClassInstance = new TestNodeLocationTests(),
+ TestMethodArguments = [],
+ TestClassArguments = [],
+ MethodMetadata = methodMetadata,
+ TestFilePath = filePath,
+ TestLineNumber = lineNumber,
+ TestStartColumnNumber = startColumnNumber,
+ TestEndLineNumber = endLineNumber,
+ TestEndColumnNumber = endColumnNumber,
+ ReturnType = typeof(void),
+ AttributesByType = new Dictionary>()
+ };
+
+ return context;
+ }
+
+ private sealed class EmptyServiceProvider : IServiceProvider
+ {
+ public static EmptyServiceProvider Instance { get; } = new();
+
+ public object? GetService(Type serviceType) => null;
+ }
+}
diff --git a/TUnit.Engine/Building/TestBuilder.cs b/TUnit.Engine/Building/TestBuilder.cs
index c366936314..3b64c01ffe 100644
--- a/TUnit.Engine/Building/TestBuilder.cs
+++ b/TUnit.Engine/Building/TestBuilder.cs
@@ -1081,6 +1081,9 @@ private async ValueTask CreateTestContextAsync(string testId, TestM
TestClassArguments = testData.ClassData,
TestFilePath = metadata.FilePath,
TestLineNumber = metadata.LineNumber,
+ TestStartColumnNumber = metadata.StartColumnNumber,
+ TestEndLineNumber = metadata.EndLineNumber,
+ TestEndColumnNumber = metadata.EndColumnNumber,
ReturnType = metadata.MethodMetadata.ReturnType ?? typeof(void),
MethodMetadata = metadata.MethodMetadata,
AttributesByType = attributes.ToAttributeDictionary(),
@@ -1175,6 +1178,9 @@ private TestDetails CreateFailedTestDetails(TestMetadata metadata, string testId
TestClassArguments = [],
TestFilePath = metadata.FilePath ?? "Unknown",
TestLineNumber = metadata.LineNumber,
+ TestStartColumnNumber = metadata.StartColumnNumber,
+ TestEndLineNumber = metadata.EndLineNumber,
+ TestEndColumnNumber = metadata.EndColumnNumber,
ReturnType = typeof(Task),
MethodMetadata = metadata.MethodMetadata,
AttributesByType = AttributeDictionaryHelper.Empty
diff --git a/TUnit.Engine/Building/TestBuilderPipeline.cs b/TUnit.Engine/Building/TestBuilderPipeline.cs
index de56c157e6..493aec53b5 100644
--- a/TUnit.Engine/Building/TestBuilderPipeline.cs
+++ b/TUnit.Engine/Building/TestBuilderPipeline.cs
@@ -254,6 +254,9 @@ private async Task GenerateDynamicTests(TestMetadata m
TestClassArguments = [],
TestFilePath = metadata.FilePath ?? "Unknown",
TestLineNumber = metadata.LineNumber,
+ TestStartColumnNumber = metadata.StartColumnNumber,
+ TestEndLineNumber = metadata.EndLineNumber,
+ TestEndColumnNumber = metadata.EndColumnNumber,
ReturnType = typeof(Task),
MethodMetadata = metadata.MethodMetadata,
AttributesByType = attributes.ToAttributeDictionary()
@@ -380,6 +383,9 @@ private async IAsyncEnumerable BuildTestsFromSingleMetad
TestClassArguments = [],
TestFilePath = resolvedMetadata.FilePath ?? "Unknown",
TestLineNumber = resolvedMetadata.LineNumber,
+ TestStartColumnNumber = resolvedMetadata.StartColumnNumber,
+ TestEndLineNumber = resolvedMetadata.EndLineNumber,
+ TestEndColumnNumber = resolvedMetadata.EndColumnNumber,
ReturnType = typeof(Task),
MethodMetadata = resolvedMetadata.MethodMetadata,
AttributesByType = attributes.ToAttributeDictionary()
@@ -458,6 +464,9 @@ private AbstractExecutableTest CreateFailedTestForDataGenerationError(TestMetada
TestClassArguments = [],
TestFilePath = metadata.FilePath ?? "Unknown",
TestLineNumber = metadata.LineNumber,
+ TestStartColumnNumber = metadata.StartColumnNumber,
+ TestEndLineNumber = metadata.EndLineNumber,
+ TestEndColumnNumber = metadata.EndColumnNumber,
ReturnType = typeof(Task),
MethodMetadata = metadata.MethodMetadata,
AttributesByType = AttributeDictionaryHelper.Empty
@@ -510,6 +519,9 @@ private AbstractExecutableTest CreateFailedTestForGenericResolutionError(TestMet
TestClassArguments = [],
TestFilePath = metadata.FilePath ?? "Unknown",
TestLineNumber = metadata.LineNumber,
+ TestStartColumnNumber = metadata.StartColumnNumber,
+ TestEndLineNumber = metadata.EndLineNumber,
+ TestEndColumnNumber = metadata.EndColumnNumber,
ReturnType = typeof(Task),
MethodMetadata = metadata.MethodMetadata,
AttributesByType = AttributeDictionaryHelper.Empty
diff --git a/TUnit.Engine/Extensions/TestExtensions.cs b/TUnit.Engine/Extensions/TestExtensions.cs
index 841e8cc70d..875fe97e5f 100644
--- a/TUnit.Engine/Extensions/TestExtensions.cs
+++ b/TUnit.Engine/Extensions/TestExtensions.cs
@@ -48,8 +48,10 @@ private static CachedTestNodeProperties GetOrCreateCachedProperties(TestContext
var testDetails = testContext.Metadata.TestDetails;
var fileLocation = new TestFileLocationProperty(testDetails.TestFilePath, new LinePositionSpan(
- new LinePosition(testDetails.TestLineNumber, 0),
- new LinePosition(testDetails.TestLineNumber, 0)
+ new LinePosition(testDetails.TestLineNumber, testDetails.TestStartColumnNumber),
+ new LinePosition(
+ testDetails.TestEndLineNumber > 0 ? testDetails.TestEndLineNumber : testDetails.TestLineNumber,
+ testDetails.TestEndColumnNumber)
));
var methodIdentifier = new TestMethodIdentifierProperty(
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
index 5ab83ff645..409ded9318 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
@@ -1578,6 +1578,8 @@ namespace
public required <[], object?[], T> CreateInstance { get; init; }
public .TestDependency[] Dependencies { get; init; }
public string[] DependsOn { get; init; }
+ public int EndColumnNumber { get; init; }
+ public int EndLineNumber { get; init; }
public required string FilePath { get; init; }
public required string FullyQualifiedName { get; init; }
public bool HasDataSource { get; init; }
@@ -1589,6 +1591,7 @@ namespace
public required string MethodName { get; init; }
public string[] Properties { get; init; }
public int RepeatCount { get; init; }
+ public int StartColumnNumber { get; init; }
public .IDataSourceAttribute[] TestDataSources { get; init; }
}
public class TestInstantiationException : .TestBuilderException
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt
index 2c27794cfb..67bf6e0e85 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt
@@ -1578,6 +1578,8 @@ namespace
public required <[], object?[], T> CreateInstance { get; init; }
public .TestDependency[] Dependencies { get; init; }
public string[] DependsOn { get; init; }
+ public int EndColumnNumber { get; init; }
+ public int EndLineNumber { get; init; }
public required string FilePath { get; init; }
public required string FullyQualifiedName { get; init; }
public bool HasDataSource { get; init; }
@@ -1589,6 +1591,7 @@ namespace
public required string MethodName { get; init; }
public string[] Properties { get; init; }
public int RepeatCount { get; init; }
+ public int StartColumnNumber { get; init; }
public .IDataSourceAttribute[] TestDataSources { get; init; }
}
public class TestInstantiationException : .TestBuilderException
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt
index 24562acdb3..a53b2b7905 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt
@@ -1578,6 +1578,8 @@ namespace
public required <[], object?[], T> CreateInstance { get; init; }
public .TestDependency[] Dependencies { get; init; }
public string[] DependsOn { get; init; }
+ public int EndColumnNumber { get; init; }
+ public int EndLineNumber { get; init; }
public required string FilePath { get; init; }
public required string FullyQualifiedName { get; init; }
public bool HasDataSource { get; init; }
@@ -1589,6 +1591,7 @@ namespace
public required string MethodName { get; init; }
public string[] Properties { get; init; }
public int RepeatCount { get; init; }
+ public int StartColumnNumber { get; init; }
public .IDataSourceAttribute[] TestDataSources { get; init; }
}
public class TestInstantiationException : .TestBuilderException
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt
index 64ca32b5e5..7b10e10041 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt
@@ -1519,6 +1519,8 @@ namespace
public required <[], object?[], T> CreateInstance { get; init; }
public .TestDependency[] Dependencies { get; init; }
public string[] DependsOn { get; init; }
+ public int EndColumnNumber { get; init; }
+ public int EndLineNumber { get; init; }
public required string FilePath { get; init; }
public required string FullyQualifiedName { get; init; }
public bool HasDataSource { get; init; }
@@ -1530,6 +1532,7 @@ namespace
public required string MethodName { get; init; }
public string[] Properties { get; init; }
public int RepeatCount { get; init; }
+ public int StartColumnNumber { get; init; }
public .IDataSourceAttribute[] TestDataSources { get; init; }
}
public class TestInstantiationException : .TestBuilderException