Skip to content
Draft
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 @@ -125,7 +125,7 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo

// A node carries a single state property in practice; use FirstOrDefault rather than
// SingleOrDefault so a malformed producer can't throw out of the consumer pump.
TestNodeStateProperty? state = update.TestNode.Properties.OfType<TestNodeStateProperty>().FirstOrDefault();
TestNodeStateProperty? state = update.TestNode.Properties.FirstOrDefault<TestNodeStateProperty>();
if (state is null)
{
return Task.CompletedTask;
Expand Down Expand Up @@ -477,7 +477,7 @@ private static bool OverlapsAnyFailedWindow(VideoSegment segment, double[] faile

private (DateTimeOffset Start, DateTimeOffset End) ResolveTiming(TestNodeUpdateMessage update, string testUid)
{
TimingProperty? timing = update.TestNode.Properties.OfType<TimingProperty>().FirstOrDefault();
TimingProperty? timing = update.TestNode.Properties.FirstOrDefault<TimingProperty>();
if (timing is not null)
{
return (timing.GlobalTiming.StartTime, timing.GlobalTiming.EndTime);
Expand Down
38 changes: 38 additions & 0 deletions src/Platform/Microsoft.Testing.Platform/Messages/PropertyBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,44 @@ public bool Any<TProperty>()
return found;
}

/// <summary>
/// Returns the first property of the <typeparamref name="TProperty"/> type, or <see langword="null"/> if none is found.
/// Unlike <see cref="SingleOrDefault{TProperty}"/>, this method does not throw when multiple properties of the
/// same type are present — it simply returns the first one encountered.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <returns>The first property of the given type, or <see langword="null"/> if none is found.</returns>
public TProperty? FirstOrDefault<TProperty>()
where TProperty : IProperty
Comment on lines +216 to +224
{
if (_testNodeStateProperty is TProperty testNodeStateProperty)
{
return testNodeStateProperty;
}

// We don't want to walk the linked list if we know that we're looking for a TestNodeStateProperty.
if (typeof(TestNodeStateProperty).IsAssignableFrom(typeof(TProperty)))
{
return default;
}
Comment on lines +231 to +235

// Direct linked-list walk: avoids the array allocation from OfType<T>() and the subsequent
// LINQ FirstOrDefault() call. Early-returns on the first match so no duplicate tracking
// is needed (unlike SingleOrDefault<T>()).
Property? current = _property;
while (current is not null)
{
if (current.Current is TProperty match)
{
return match;
}

current = current.Next;
}

return default;
}

/// <summary>
/// Returns the only property of the <typeparamref name="TProperty"/> type, and throws an exception if there is not exactly one element.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#nullable enable
[TPEXP]Microsoft.Testing.Platform.Extensions.IBlockingDataConsumer
Microsoft.Testing.Platform.Extensions.Messages.PropertyBag.FirstOrDefault<TProperty>() -> TProperty?
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
Microsoft.Testing.Platform.Extensions.Messages.PropertyBag.FirstOrDefault<TProperty>() -> TProperty?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔧 RS0025 — This entry duplicates the one already present in the base PublicAPI/PublicAPI.Unshipped.txt. The net/ subdirectory file is for APIs that are .NET-TFM-only; since FirstOrDefault<TProperty>() has no #if NET guard and is available on all target frameworks, it should live only in the base file.

Remove this line to fix the duplicate-symbol build error:

Suggested change
Microsoft.Testing.Platform.Extensions.Messages.PropertyBag.FirstOrDefault<TProperty>() -> TProperty?

Loading