using NUnit.Framework; using X10D.Linq; namespace X10D.Tests.Linq; [TestFixture] public class EnumerableTests { [Test] public void Except_ShouldFilterElements_GivenMatchingElements() { int[] source = Enumerable.Range(1, 10).ToArray(); int[] result = source.Except(5).ToArray(); Assert.That(result, Is.EquivalentTo(new[] {1, 2, 3, 4, 6, 7, 8, 9, 10})); } [Test] public void Except_ShouldReturnSameElements_GivenNoMatchingElements() { int[] source = Enumerable.Range(1, 10).ToArray(); int[] result = source.Except(11).ToArray(); Assert.That(result, Is.EquivalentTo(source)); } [Test] public void Except_ShouldThrowArgumentNullException_GivenNullSource() { IEnumerable source = null!; Assert.Throws(() => source.Except(42)); } [Test] public void MinMax_ShouldReturnCorrectValues_UsingDefaultComparer() { IEnumerable source = Enumerable.Range(1, 10); (int minimum, int maximum) = source.MinMax(); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(1)); Assert.That(maximum, Is.EqualTo(10)); }); source = Enumerable.Range(1, 10).ToArray(); (minimum, maximum) = source.MinMax(); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(1)); Assert.That(maximum, Is.EqualTo(10)); }); } [Test] public void MinMax_ShouldReturnCorrectSelectedValues_UsingDefaultComparer() { IEnumerable source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}); (int minimum, int maximum) = source.MinMax(p => p.Age); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(1)); Assert.That(maximum, Is.EqualTo(10)); }); source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray(); (minimum, maximum) = source.MinMax(p => p.Age); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(1)); Assert.That(maximum, Is.EqualTo(10)); }); } [Test] public void MinMax_ShouldReturnOppositeSelectedValues_UsingInverseComparer() { IEnumerable source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}); (int minimum, int maximum) = source.MinMax(p => p.Age, new InverseComparer()); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(10)); Assert.That(maximum, Is.EqualTo(1)); }); source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray(); (minimum, maximum) = source.MinMax(p => p.Age, new InverseComparer()); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(10)); Assert.That(maximum, Is.EqualTo(1)); }); } [Test] public void MinMax_ShouldReturnOppositeValues_UsingInverseComparer() { (int minimum, int maximum) = Enumerable.Range(1, 10).MinMax(new InverseComparer()); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(10)); Assert.That(maximum, Is.EqualTo(1)); }); (minimum, maximum) = Enumerable.Range(1, 10).ToArray().MinMax(new InverseComparer()); Assert.Multiple(() => { Assert.That(minimum, Is.EqualTo(10)); Assert.That(maximum, Is.EqualTo(1)); }); } [Test] public void MinMax_ShouldThrowArgumentNullException_GivenNullSelector() { IEnumerable source = Enumerable.Empty(); Assert.Throws(() => source.MinMax((Func)(null!))); Assert.Throws(() => source.MinMax((Func)(null!), null)); } [Test] public void MinMax_ShouldThrowArgumentNullException_GivenNullSource() { IEnumerable? source = null; Assert.Throws(() => source!.MinMax()); Assert.Throws(() => source!.MinMax(v => v)); Assert.Throws(() => source!.MinMax(null)); Assert.Throws(() => source!.MinMax(v => v, null)); } [Test] public void MinMax_ShouldThrowInvalidOperationException_GivenEmptySource() { Assert.Throws(() => Enumerable.Empty().MinMax()); Assert.Throws(() => Array.Empty().MinMax()); Assert.Throws(() => new List().MinMax()); Assert.Throws(() => Enumerable.Empty().MinMax(i => i * 2)); Assert.Throws(() => Array.Empty().MinMax(i => i * 2)); Assert.Throws(() => new List().MinMax(i => i * 2)); } [Test] public void MinMaxBy_ShouldReturnCorrectSelectedValues_UsingDefaultComparer() { IEnumerable source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}); (Person minimum, Person maximum) = source.MinMaxBy(p => p.Age); Assert.Multiple(() => { Assert.That(minimum.Age, Is.EqualTo(1)); Assert.That(maximum.Age, Is.EqualTo(10)); }); source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray(); (minimum, maximum) = source.MinMaxBy(p => p.Age); Assert.Multiple(() => { Assert.That(minimum.Age, Is.EqualTo(1)); Assert.That(maximum.Age, Is.EqualTo(10)); }); } [Test] public void MinMaxBy_ShouldReturnOppositeSelectedValues_UsingInverseComparer() { IEnumerable source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}); (Person minimum, Person maximum) = source.MinMaxBy(p => p.Age, new InverseComparer()); Assert.Multiple(() => { Assert.That(minimum.Age, Is.EqualTo(10)); Assert.That(maximum.Age, Is.EqualTo(1)); }); source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray(); (minimum, maximum) = source.MinMaxBy(p => p.Age, new InverseComparer()); Assert.Multiple(() => { Assert.That(minimum.Age, Is.EqualTo(10)); Assert.That(maximum.Age, Is.EqualTo(1)); }); } [Test] public void MinMaxBy_ShouldThrowArgumentNullException_GivenNullSelector() { Person[] source = Enumerable.Range(1, 10).Select(i => new Person {Age = i}).ToArray(); Assert.Throws(() => source.MinMaxBy((Func)null!)); Assert.Throws(() => source.MinMaxBy((Func)null!, null)); } [Test] public void MinMaxBy_ShouldThrowArgumentNullException_GivenNullSource() { IEnumerable? source = null; Assert.Throws(() => source!.MinMaxBy(p => p.Age)); Assert.Throws(() => source!.MinMaxBy(p => p.Age, null)); } [Test] public void MinMaxBy_ShouldThrowInvalidOperationException_GivenEmptySource() { Assert.Throws(() => { IEnumerable source = Enumerable.Empty(); _ = source.MinMaxBy(p => p.Age); }); Assert.Throws(() => { Person[] source = Array.Empty(); _ = source.MinMaxBy(p => p.Age); }); } private struct InverseComparer : IComparer where T : IComparable { public int Compare(T? x, T? y) { if (x is null) { return y is null ? 0 : 1; } return y is null ? -1 : y.CompareTo(x); } } private struct Person : IComparable, IComparable { public int Age { get; set; } public static bool operator <(Person left, Person right) { return left.CompareTo(right) < 0; } public static bool operator >(Person left, Person right) { return left.CompareTo(right) > 0; } public static bool operator <=(Person left, Person right) { return left.CompareTo(right) <= 0; } public static bool operator >=(Person left, Person right) { return left.CompareTo(right) >= 0; } public int CompareTo(Person other) { return Age.CompareTo(other.Age); } public int CompareTo(object? obj) { if (ReferenceEquals(null, obj)) { return 1; } return obj is Person other ? CompareTo(other) : throw new ArgumentException(ExceptionMessages.ObjectIsNotAValidType); } } }