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
4 changes: 3 additions & 1 deletion TUnit.Core/DiscoveredTestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ internal DiscoveredTestContext(TestContext testContext)

public void AddProperty(string key, string value)
{
TestContext.TestDetails.InternalCustomProperties.Add(key, value);
TestContext.TestDetails.InternalCustomProperties
.GetOrAdd(key, [])
.Add(value);
}

public void AddCategory(string category)
Expand Down
10 changes: 7 additions & 3 deletions TUnit.Core/TestDetails.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel;
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using TUnit.Core.Interfaces;

Expand Down Expand Up @@ -114,9 +115,12 @@ public abstract record TestDetails
/// <summary>
/// Gets the custom properties for the test.
/// </summary>
public IReadOnlyDictionary<string, string> CustomProperties => InternalCustomProperties;
[field: AllowNull, MaybeNull]
public IReadOnlyDictionary<string, IReadOnlyList<string>> CustomProperties => field ??= InternalCustomProperties.ToDictionary(
kvp => kvp.Key, IReadOnlyList<string> (kvp) => kvp.Value.AsReadOnly()
);

internal Dictionary<string, string> InternalCustomProperties { get; } = [];
internal ConcurrentDictionary<string, List<string>> InternalCustomProperties { get; } = [];

/// <summary>
/// Gets the attributes for the test assembly.
Expand Down
49 changes: 49 additions & 0 deletions TUnit.Engine.Tests/Bugs/Bug2481.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Shouldly;
using TUnit.Engine.Tests.Enums;

namespace TUnit.Engine.Tests.Bugs;

public class Bug2481(TestMode testMode) : InvokableTestBase(testMode)
{
[Test]
public async Task Test()
{
await RunTestsWithFilter(
"/*/TUnit.TestProject.Bugs._2481/*/*[Group=Bugs]",
[
result => result.ResultSummary.Outcome.ShouldBe("Completed"),
result => result.ResultSummary.Counters.Total.ShouldBe(1),
result => result.ResultSummary.Counters.Passed.ShouldBe(1),
result => result.ResultSummary.Counters.Failed.ShouldBe(0),
result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
]);
}

[Test]
public async Task Test2()
{
await RunTestsWithFilter(
"/*/TUnit.TestProject.Bugs._2481/*/*[Group=2481]",
[
result => result.ResultSummary.Outcome.ShouldBe("Completed"),
result => result.ResultSummary.Counters.Total.ShouldBe(1),
result => result.ResultSummary.Counters.Passed.ShouldBe(1),
result => result.ResultSummary.Counters.Failed.ShouldBe(0),
result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
]);
}

[Test]
public async Task Test3()
{
await RunTestsWithFilter(
"/*/TUnit.TestProject.Bugs._2481/*/*[Group=TUnit]",
[
result => result.ResultSummary.Outcome.ShouldBe("Completed"),
result => result.ResultSummary.Counters.Total.ShouldBe(1),
result => result.ResultSummary.Counters.Passed.ShouldBe(1),
result => result.ResultSummary.Counters.Failed.ShouldBe(0),
result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
]);
}
}
15 changes: 13 additions & 2 deletions TUnit.Engine/Extensions/TestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ internal static TestNode ToTestNode(this TestContext testContext)
),

// Custom TUnit Properties
..testDetails.Categories.Select(category => new TestMetadataProperty(category, string.Empty)),
..testDetails.CustomProperties.Select(x => new TestMetadataProperty(x.Key, x.Value)),
..testDetails.Categories.Select(category => new TestMetadataProperty(category)),
..ExtractProperties(testDetails),

// Artifacts
..testContext.Artifacts.Select(x => new FileArtifactProperty(x.File, x.DisplayName, x.Description)),
Expand All @@ -48,6 +48,17 @@ internal static TestNode ToTestNode(this TestContext testContext)
return testNode;
}

public static IEnumerable<KeyValuePairStringProperty> ExtractProperties(this TestDetails testDetails)
{
foreach (var propertyGroup in testDetails.CustomProperties)
{
foreach (var propertyValue in propertyGroup.Value)
{
yield return new KeyValuePairStringProperty(propertyGroup.Key, propertyValue);
}
}
}

internal static TestNode WithProperty(this TestNode testNode, IProperty property)
{
testNode.Properties.Add(property);
Expand Down
2 changes: 1 addition & 1 deletion TUnit.Engine/Json/TestJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public record TestJson

public required TimeSpan? Timeout { get; init; }

public required IReadOnlyDictionary<string, string> CustomProperties { get; init; }
public required IReadOnlyDictionary<string, IReadOnlyList<string>> CustomProperties { get; init; }

public required string? ReturnType { get; init; }

Expand Down
10 changes: 8 additions & 2 deletions TUnit.Engine/Services/TestFilterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.Testing.Platform.Logging;
using Microsoft.Testing.Platform.Requests;
using TUnit.Core;
using TUnit.Engine.Extensions;

namespace TUnit.Engine.Services;

Expand Down Expand Up @@ -73,9 +74,14 @@ private string BuildPath(TestDetails testDetails)

private PropertyBag BuildPropertyBag(TestDetails testDetails)
{
var properties = testDetails.ExtractProperties();

var categories = testDetails.Categories.Select(x => new TestMetadataProperty(x));

return new PropertyBag(
[
..testDetails.CustomProperties.Select(x => new KeyValuePairStringProperty(x.Key, x.Value)),
..properties,
..categories,
..testDetails.Categories.Select(x => new KeyValuePairStringProperty("Category", x))
]
);
Expand All @@ -86,4 +92,4 @@ private bool UnhandledFilter(ITestExecutionFilter testExecutionFilter)
_logger.LogWarning($"Filter is Unhandled Type: {testExecutionFilter.GetType().FullName}");
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ namespace TUnit.Core
public System.Attribute[] ClassAttributes { get; }
public abstract object ClassInstance { get; }
public required int CurrentRepeatAttempt { get; init; }
public System.Collections.Generic.IReadOnlyDictionary<string, string> CustomProperties { get; }
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IReadOnlyList<string>> CustomProperties { get; }
[System.Text.Json.Serialization.JsonIgnore]
public required System.Attribute[] DataAttributes { get; init; }
[System.Text.Json.Serialization.JsonIgnore]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ namespace TUnit.Core
public System.Attribute[] ClassAttributes { get; }
public abstract object ClassInstance { get; }
public required int CurrentRepeatAttempt { get; init; }
public System.Collections.Generic.IReadOnlyDictionary<string, string> CustomProperties { get; }
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IReadOnlyList<string>> CustomProperties { get; }
[System.Text.Json.Serialization.JsonIgnore]
public required System.Attribute[] DataAttributes { get; init; }
[System.Text.Json.Serialization.JsonIgnore]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ namespace TUnit.Core
public System.Attribute[] ClassAttributes { get; }
public abstract object ClassInstance { get; }
public required int CurrentRepeatAttempt { get; init; }
public System.Collections.Generic.IReadOnlyDictionary<string, string> CustomProperties { get; }
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IReadOnlyList<string>> CustomProperties { get; }
[System.Text.Json.Serialization.JsonIgnore]
public required System.Attribute[] DataAttributes { get; init; }
[System.Text.Json.Serialization.JsonIgnore]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ namespace TUnit.Engine.Json
public TestJson() { }
public required System.Collections.Generic.IReadOnlyList<string> Categories { get; init; }
public required string? ClassType { get; init; }
public required System.Collections.Generic.IReadOnlyDictionary<string, string> CustomProperties { get; init; }
public required System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IReadOnlyList<string>> CustomProperties { get; init; }
public required string DisplayName { get; set; }
public required System.Collections.Generic.Dictionary<string, object?> ObjectBag { get; init; }
public required TUnit.Engine.Json.TestResultJson? Result { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ namespace TUnit.Engine.Json
public TestJson() { }
public required System.Collections.Generic.IReadOnlyList<string> Categories { get; init; }
public required string? ClassType { get; init; }
public required System.Collections.Generic.IReadOnlyDictionary<string, string> CustomProperties { get; init; }
public required System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IReadOnlyList<string>> CustomProperties { get; init; }
public required string DisplayName { get; set; }
public required System.Collections.Generic.Dictionary<string, object?> ObjectBag { get; init; }
public required TUnit.Engine.Json.TestResultJson? Result { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ namespace TUnit.Engine.Json
public TestJson() { }
public required System.Collections.Generic.IReadOnlyList<string> Categories { get; init; }
public required string? ClassType { get; init; }
public required System.Collections.Generic.IReadOnlyDictionary<string, string> CustomProperties { get; init; }
public required System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IReadOnlyList<string>> CustomProperties { get; init; }
public required string DisplayName { get; set; }
public required System.Collections.Generic.Dictionary<string, object?> ObjectBag { get; init; }
public required TUnit.Engine.Json.TestResultJson? Result { get; set; }
Expand Down
22 changes: 22 additions & 0 deletions TUnit.TestProject/Bugs/2481/Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace TUnit.TestProject.Bugs._2481;

public class Tests
{
[Test]
[Property("Group", "Bugs")]
[Property("Group", "2481")]
[Property("Group", "TUnit")]
public async Task Test()
{
var properties = TestContext.Current!.TestDetails.CustomProperties;

await Assert.That(properties).HasCount().EqualTo(1);

var array = properties["Group"].ToArray();

await Assert.That(array).HasCount().EqualTo(3)
.And.Contains(x => x is "Bugs")
.And.Contains(x => x is "2481")
.And.Contains(x => x is "TUnit");
}
}
16 changes: 7 additions & 9 deletions TUnit.TestProject/CustomPropertyTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Immutable;
using TUnit.Assertions;
using TUnit.Assertions.Extensions;

namespace TUnit.TestProject;

Expand All @@ -14,25 +12,25 @@ public class CustomPropertyTests
public async Task Test()
{
await Assert.That(GetDictionary()).ContainsKey("ClassProperty");
await Assert.That(GetDictionary()).ContainsValue("ClassPropertyValue");
await Assert.That(GetDictionary()["ClassProperty"]).Contains("ClassPropertyValue");

await Assert.That(GetDictionary()).ContainsKey("ClassProperty2");
await Assert.That(GetDictionary()).ContainsValue("ClassPropertyValue2");
await Assert.That(GetDictionary()["ClassProperty2"]).Contains("ClassPropertyValue2");

await Assert.That(GetDictionary()).ContainsKey("MethodProperty");
await Assert.That(GetDictionary()).ContainsValue("MethodPropertyValue");
await Assert.That(GetDictionary()["MethodProperty"]).Contains("MethodPropertyValue");

await Assert.That(GetDictionary()).ContainsKey("MethodProperty2");
await Assert.That(GetDictionary()).ContainsValue("MethodPropertyValue2");
await Assert.That(GetDictionary()["MethodProperty2"]).Contains("MethodPropertyValue2");
}

private static ImmutableDictionary<string, string> GetDictionary()
private static ImmutableDictionary<string, IReadOnlyList<string>> GetDictionary()
{
return TestContext.Current?.TestDetails.CustomProperties.ToImmutableDictionary(x => x.Key, x => x.Value)
?? ImmutableDictionary<string, string>.Empty;
?? ImmutableDictionary<string, IReadOnlyList<string>>.Empty;
}

public class ClassPropertyAttribute() : PropertyAttribute("ClassProperty2", "ClassPropertyValue2");

public class MethodPropertyAttribute() : PropertyAttribute("MethodProperty2", "MethodPropertyValue2");
}
}
Loading