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
43 changes: 43 additions & 0 deletions PolyShim.Tests/Net100/CollectionExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.Net100;

public class CollectionExtensionsTests
{
[Fact]
public void AsReadOnly_ISet_Test()
{
// Arrange
var set = (ISet<int>)new HashSet<int> { 1, 2, 3 };

// Act
var readOnly = set.AsReadOnly();

// Assert
readOnly.Should().BeOfType<ReadOnlySet<int>>();
readOnly.Count.Should().Be(3);
readOnly.Contains(1).Should().BeTrue();
readOnly.Contains(4).Should().BeFalse();
readOnly.IsSubsetOf(new[] { 1, 2, 3 }).Should().BeTrue();
readOnly.IsSupersetOf(new[] { 1 }).Should().BeTrue();
}

[Fact]
public void AsReadOnly_ISet_ReflectsChanges_Test()
{
// Arrange
var inner = new HashSet<int> { 1, 2, 3 };
var set = (ISet<int>)inner;

// Act
var readOnly = set.AsReadOnly();
inner.Add(4);

// Assert
readOnly.Count.Should().Be(4);
readOnly.Contains(4).Should().BeTrue();
}
}
54 changes: 54 additions & 0 deletions PolyShim.Tests/Net70/CollectionExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.Net70;

public class CollectionExtensionsTests
{
[Fact]
public void AsReadOnly_IList_Test()
Comment thread
Tyrrrz marked this conversation as resolved.
{
// Arrange
var list = (IList<string>)new List<string> { "a", "b", "c" };

// Act
var readOnly = list.AsReadOnly();

// Assert
readOnly.Should().BeOfType<ReadOnlyCollection<string>>();
readOnly.Should().Equal("a", "b", "c");
}

[Fact]
public void AsReadOnly_IList_ReflectsChanges_Test()
{
// Arrange
var inner = new List<string> { "a", "b" };
var list = (IList<string>)inner;

// Act
var readOnly = list.AsReadOnly();
inner.Add("c");

// Assert
readOnly.Should().Equal("a", "b", "c");
}

[Fact]
public void AsReadOnly_IDictionary_Test()
Comment thread
Tyrrrz marked this conversation as resolved.
{
// Arrange
var dictionary =
(IDictionary<string, int>)new Dictionary<string, int> { ["one"] = 1, ["two"] = 2 };

// Act
var readOnly = dictionary.AsReadOnly();

// Assert
readOnly.Should().BeOfType<ReadOnlyDictionary<string, int>>();
readOnly["one"].Should().Be(1);
readOnly["two"].Should().Be(2);
}
}
81 changes: 81 additions & 0 deletions PolyShim.Tests/Net80/CollectionExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using Xunit;

namespace PolyShim.Tests.Net80;

public class CollectionExtensionsTests
{
[Fact]
public void AddRange_Test()
{
// Arrange
var list = new List<int> { 1, 2, 3 };
var items = (ReadOnlySpan<int>)new[] { 4, 5, 6 };

// Act
list.AddRange(items);

// Assert
list.Should().Equal(1, 2, 3, 4, 5, 6);
}

[Fact]
public void AddRange_Empty_Test()
{
// Arrange
var list = new List<int> { 1, 2, 3 };
var items = (ReadOnlySpan<int>)[];

// Act
list.AddRange(items);

// Assert
list.Should().Equal(1, 2, 3);
}

[Fact]
public void InsertRange_Test()
{
// Arrange
var list = new List<int> { 1, 2, 6 };
var items = (ReadOnlySpan<int>)new[] { 3, 4, 5 };

// Act
list.InsertRange(2, items);

// Assert
list.Should().Equal(1, 2, 3, 4, 5, 6);
}

[Fact]
public void CopyTo_Test()
{
// Arrange
var list = new List<int> { 1, 2, 3 };
var destination = new int[5];

// Act
list.CopyTo(destination.AsSpan());

// Assert
destination[0].Should().Be(1);
destination[1].Should().Be(2);
destination[2].Should().Be(3);
}

[Fact]
public void CopyTo_TooSmall_Test()
{
// Arrange
var list = new List<int> { 1, 2, 3 };
var destination = new int[2];

// Act
var act = () => list.CopyTo(destination.AsSpan());

// Assert
act.Should().Throw<ArgumentException>();
}
}
65 changes: 65 additions & 0 deletions PolyShim.Tests/NetCore20/CollectionExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,69 @@ public void GetValueOrDefault_Test()
dictionary.GetValueOrDefault("b").Should().Be("B");
dictionary.GetValueOrDefault("d").Should().BeNull();
}

[Fact]
public void TryAdd_IDictionary_Test()
{
// Arrange
var dictionary =
(IDictionary<string, int>)new Dictionary<string, int> { ["apple"] = 1, ["banana"] = 2 };

// Act
var result = dictionary.TryAdd("cherry", 3);

// Assert
result.Should().BeTrue();
dictionary.Should().ContainKey("cherry");
dictionary["cherry"].Should().Be(3);
dictionary.Should().HaveCount(3);
}

[Fact]
public void TryAdd_IDictionary_Exists_Test()
{
// Arrange
var dictionary =
(IDictionary<string, int>)new Dictionary<string, int> { ["apple"] = 1, ["banana"] = 2 };

// Act
var result = dictionary.TryAdd("apple", 99);

// Assert
result.Should().BeFalse();
dictionary["apple"].Should().Be(1);
dictionary.Should().HaveCount(2);
}

[Fact]
public void Remove_IDictionary_Test()
{
// Arrange
var dictionary =
(IDictionary<string, int>)new Dictionary<string, int> { ["apple"] = 1, ["banana"] = 2 };

// Act
var result = dictionary.Remove("apple", out var value);

// Assert
result.Should().BeTrue();
value.Should().Be(1);
dictionary.Should().NotContainKey("apple");
dictionary.Should().HaveCount(1);
}

[Fact]
public void Remove_IDictionary_NonExistent_Test()
{
// Arrange
var dictionary = (IDictionary<string, int>)new Dictionary<string, int> { ["apple"] = 1 };

// Act
var result = dictionary.Remove("cherry", out var value);

// Assert
result.Should().BeFalse();
value.Should().Be(default);
dictionary.Should().HaveCount(1);
}
}
23 changes: 23 additions & 0 deletions PolyShim/Net100/CollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#if (NETCOREAPP && !NET10_0_OR_GREATER) || (NETSTANDARD) || (NETFRAMEWORK)
#nullable enable
#pragma warning disable CS0436

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;

#if !POLYSHIM_INCLUDE_COVERAGE
[ExcludeFromCodeCoverage]
#endif
internal static class MemberPolyfills_Net100_CollectionExtensions
{
#if !NETFRAMEWORK || NET45_OR_GREATER
// ReadOnlySet<T> is not available in .NET Framework below 4.5
extension<T>(ISet<T> set)
{
// https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-iset((-0)))
public ReadOnlySet<T> AsReadOnly() => new(set);
}
#endif
}
#endif
27 changes: 27 additions & 0 deletions PolyShim/Net50/IReadOnlySet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#if (NETCOREAPP && !NET5_0_OR_GREATER) || (NETSTANDARD) || (NETFRAMEWORK)
#nullable enable
#pragma warning disable CS0436

namespace System.Collections.Generic;

#if !NETFRAMEWORK || NET45_OR_GREATER
// IReadOnlyCollection<T> (base interface) is not available in .NET Framework below 4.5
// https://learn.microsoft.com/dotnet/api/system.collections.generic.ireadonlyset-1
internal interface IReadOnlySet<T> : IReadOnlyCollection<T>
{
bool Contains(T item);

bool IsProperSubsetOf(IEnumerable<T> other);

bool IsProperSupersetOf(IEnumerable<T> other);

bool IsSubsetOf(IEnumerable<T> other);

bool IsSupersetOf(IEnumerable<T> other);

bool Overlaps(IEnumerable<T> other);

bool SetEquals(IEnumerable<T> other);
}
#endif
#endif
30 changes: 30 additions & 0 deletions PolyShim/Net70/CollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#if (NETCOREAPP && !NET7_0_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD)
#nullable enable
#pragma warning disable CS0436

using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;

namespace System.Collections.Generic;

#if !POLYSHIM_INCLUDE_COVERAGE
[ExcludeFromCodeCoverage]
#endif
internal static class MemberPolyfills_Net70_CollectionExtensions
{
extension<T>(IList<T> list)
{
// https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-ilist((-0)))
public ReadOnlyCollection<T> AsReadOnly() => new(list);
}

#if !NETFRAMEWORK || NET45_OR_GREATER
extension<TKey, TValue>(IDictionary<TKey, TValue> dictionary)
where TKey : notnull
{
// https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.asreadonly#system-collections-generic-collectionextensions-asreadonly-2(system-collections-generic-idictionary((-0-1)))
public ReadOnlyDictionary<TKey, TValue> AsReadOnly() => new(dictionary);
}
Comment thread
Tyrrrz marked this conversation as resolved.
Comment thread
Tyrrrz marked this conversation as resolved.
#endif
}
#endif
38 changes: 38 additions & 0 deletions PolyShim/Net80/CollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#if (NETCOREAPP && !NET8_0_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD)
#nullable enable
#pragma warning disable CS0436

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

#if !POLYSHIM_INCLUDE_COVERAGE
[ExcludeFromCodeCoverage]
#endif
internal static class MemberPolyfills_Net80_CollectionExtensions
{
extension<T>(List<T> list)
{
// https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.addrange
public void AddRange(ReadOnlySpan<T> source)
{
foreach (var item in source)
list.Add(item);
}

// https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.insertrange
public void InsertRange(int index, ReadOnlySpan<T> source) =>
list.InsertRange(index, source.ToArray());

// https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.copyto
public void CopyTo(Span<T> destination)
{
if (destination.Length < list.Count)
throw new ArgumentException("Destination is too short.", nameof(destination));

for (var i = 0; i < list.Count; i++)
destination[i] = list[i];
}
}
}
#endif
Loading
Loading