2024-04-18 15:31:38 -04:00
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2025-09-06 15:42:48 -04:00
|
|
|
using System.Threading;
|
2024-04-18 15:31:38 -04:00
|
|
|
using Xunit;
|
|
|
|
|
|
2026-01-27 08:51:29 -05:00
|
|
|
#pragma warning disable IDE0060 // Remove unused parameter
|
|
|
|
|
#pragma warning disable IDE0290 // Use primary constructor
|
2026-03-23 10:50:09 -04:00
|
|
|
namespace SabreTools.Collections.Extensions.Test
|
2024-04-18 15:31:38 -04:00
|
|
|
{
|
|
|
|
|
public class EnumerableExtensionsTests
|
|
|
|
|
{
|
2025-09-06 15:42:48 -04:00
|
|
|
#region IterateWithAction
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void IterateWithActionTest()
|
|
|
|
|
{
|
|
|
|
|
List<int> source = [1, 2, 3, 4];
|
|
|
|
|
int actual = 0;
|
|
|
|
|
|
|
|
|
|
source.IterateWithAction(i => Interlocked.Add(ref actual, i));
|
|
|
|
|
Assert.Equal(10, actual);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region SafeEnumerate
|
|
|
|
|
|
2024-04-18 15:31:38 -04:00
|
|
|
[Fact]
|
2024-11-25 15:23:51 -05:00
|
|
|
public void SafeEnumerate_Empty()
|
2024-04-18 15:31:38 -04:00
|
|
|
{
|
|
|
|
|
var source = Enumerable.Empty<string>();
|
|
|
|
|
var safe = source.SafeEnumerate();
|
|
|
|
|
var list = safe.ToList();
|
|
|
|
|
Assert.Empty(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
2024-11-25 15:23:51 -05:00
|
|
|
public void SafeEnumerate_Throws()
|
|
|
|
|
{
|
|
|
|
|
var source = new List<string> { "a", "ab", "abc" };
|
|
|
|
|
var wrapper = new ThrowsEnumerable(source);
|
|
|
|
|
|
|
|
|
|
var safe = wrapper.SafeEnumerate();
|
|
|
|
|
var list = safe.ToList();
|
|
|
|
|
Assert.Empty(list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public void SafeEnumerate_NoError()
|
2024-04-18 15:31:38 -04:00
|
|
|
{
|
|
|
|
|
var source = new List<string> { "a", "ab", "abc" };
|
|
|
|
|
var safe = source.SafeEnumerate();
|
|
|
|
|
var list = safe.ToList();
|
|
|
|
|
Assert.Equal(3, list.Count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
2024-11-25 15:23:51 -05:00
|
|
|
public void SafeEnumerate_ErrorMid()
|
2024-04-18 15:31:38 -04:00
|
|
|
{
|
|
|
|
|
var source = new List<string> { "a", "ab", "abc" };
|
|
|
|
|
var wrapper = new ErrorEnumerable(source);
|
|
|
|
|
|
|
|
|
|
var safe = wrapper.SafeEnumerate();
|
|
|
|
|
var list = safe.ToList();
|
|
|
|
|
Assert.Equal(2, list.Count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
2024-11-25 15:23:51 -05:00
|
|
|
public void SafeEnumerate_ErrorLast()
|
2024-04-18 15:31:38 -04:00
|
|
|
{
|
|
|
|
|
var source = new List<string> { "a", "ab", "abc", "abcd" };
|
|
|
|
|
var wrapper = new ErrorEnumerable(source);
|
2024-11-25 15:23:51 -05:00
|
|
|
|
2024-04-18 15:31:38 -04:00
|
|
|
var safe = wrapper.SafeEnumerate();
|
|
|
|
|
var list = safe.ToList();
|
|
|
|
|
Assert.Equal(2, list.Count);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-06 15:42:48 -04:00
|
|
|
#endregion
|
|
|
|
|
|
2024-04-18 15:31:38 -04:00
|
|
|
/// <summary>
|
2025-11-13 08:56:30 -05:00
|
|
|
/// Fake enumerable that uses <see cref="ErrorEnumerator"/>
|
2024-04-18 15:31:38 -04:00
|
|
|
/// </summary>
|
|
|
|
|
private class ErrorEnumerable : IEnumerable<string>
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Enumerator to use during enumeration
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly ErrorEnumerator _enumerator;
|
|
|
|
|
|
|
|
|
|
public ErrorEnumerable(IEnumerable<string> source)
|
|
|
|
|
{
|
|
|
|
|
_enumerator = new ErrorEnumerator(source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public IEnumerator<string> GetEnumerator() => _enumerator;
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => _enumerator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fake enumerator that throws an exception every other item while moving to the next item
|
|
|
|
|
/// </summary>
|
|
|
|
|
private class ErrorEnumerator : IEnumerator<string>
|
|
|
|
|
{
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public string Current
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_index == -1)
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
|
|
|
|
|
return _enumerator.Current;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
object IEnumerator.Current => Current;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Enumerator from the source enumerable
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly IEnumerator<string> _enumerator;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Enumerators start before the data
|
|
|
|
|
/// </summary>
|
|
|
|
|
private int _index = -1;
|
|
|
|
|
|
|
|
|
|
public ErrorEnumerator(IEnumerable<string> source)
|
|
|
|
|
{
|
|
|
|
|
_enumerator = source.GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public void Dispose() { }
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public bool MoveNext()
|
|
|
|
|
{
|
|
|
|
|
// Move to the next item, if possible
|
|
|
|
|
bool moved = _enumerator.MoveNext();
|
|
|
|
|
if (!moved)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Get the next real item
|
|
|
|
|
_index++;
|
|
|
|
|
|
|
|
|
|
// Every other move, throw an exception
|
|
|
|
|
if (_index % 2 == 1)
|
|
|
|
|
throw new Exception("Access issue for this item in the enumerable");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
_enumerator.Reset();
|
|
|
|
|
_index = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-25 15:23:51 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fake enumerable that throws an exception for the enumerator
|
|
|
|
|
/// </summary>
|
|
|
|
|
private class ThrowsEnumerable : IEnumerable<string>
|
|
|
|
|
{
|
|
|
|
|
public ThrowsEnumerable(IEnumerable<string> source) { }
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
public IEnumerator<string> GetEnumerator() => throw new Exception();
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => throw new Exception();
|
|
|
|
|
}
|
2024-04-18 15:31:38 -04:00
|
|
|
}
|
2025-07-23 10:25:19 -04:00
|
|
|
}
|