diff --git a/src/System.Linq/tests/Helpers/TestCollection.cs b/src/System.Linq/tests/Helpers/TestCollection.cs new file mode 100644 index 000000000000..b03c04789621 --- /dev/null +++ b/src/System.Linq/tests/Helpers/TestCollection.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.Linq.Tests.Helpers +{ + public class TestCollection : ICollection + { + public T[] Items = new T[0]; + public int CountTouched = 0; + public int CopyToTouched = 0; + public TestCollection(T[] items) { Items = items; } + + public virtual int Count { get { CountTouched++; return Items.Length; } } + public bool IsReadOnly { get { return false; } } + public void Add(T item) { throw new NotImplementedException(); } + public void Clear() { throw new NotImplementedException(); } + public bool Contains(T item) { return Items.Contains(item); } + public bool Remove(T item) { throw new NotImplementedException(); } + public void CopyTo(T[] array, int arrayIndex) { CopyToTouched++; Items.CopyTo(array, arrayIndex); } + public IEnumerator GetEnumerator() { return ((IEnumerable)Items).GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } + } +} diff --git a/src/System.Linq/tests/Helpers/TestEnumerable.cs b/src/System.Linq/tests/Helpers/TestEnumerable.cs new file mode 100644 index 000000000000..bc9423e937c4 --- /dev/null +++ b/src/System.Linq/tests/Helpers/TestEnumerable.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.Linq.Tests.Helpers +{ + public class TestEnumerable : IEnumerable + { + public T[] Items = new T[0]; + public TestEnumerable(T[] items) { Items = items; } + + public IEnumerator GetEnumerator() { return ((IEnumerable)Items).GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } + } +} diff --git a/src/System.Linq/tests/Helpers/TestReadOnlyCollection.cs b/src/System.Linq/tests/Helpers/TestReadOnlyCollection.cs new file mode 100644 index 000000000000..16f6c21f1b4e --- /dev/null +++ b/src/System.Linq/tests/Helpers/TestReadOnlyCollection.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.Linq.Tests.Helpers +{ + public class TestReadOnlyCollection : IReadOnlyCollection + { + public T[] Items = new T[0]; + public int CountTouched = 0; + public TestReadOnlyCollection(T[] items) { Items = items; } + + public int Count { get { CountTouched++; return Items.Length; } } + public IEnumerator GetEnumerator() { return ((IEnumerable)Items).GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } + } +} diff --git a/src/System.Linq/tests/System.Linq.Tests.csproj b/src/System.Linq/tests/System.Linq.Tests.csproj index 809a88eaff18..e716afed7e10 100644 --- a/src/System.Linq/tests/System.Linq.Tests.csproj +++ b/src/System.Linq/tests/System.Linq.Tests.csproj @@ -21,6 +21,9 @@ + + + @@ -30,6 +33,9 @@ + + + diff --git a/src/System.Linq/tests/ToArrayTests.cs b/src/System.Linq/tests/ToArrayTests.cs new file mode 100644 index 000000000000..cb20c9781952 --- /dev/null +++ b/src/System.Linq/tests/ToArrayTests.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Tests.Helpers; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Linq.Tests +{ + public class ToArrayTests + { + private class TestLargeSequence : IEnumerable + { + public long MaxSize = 2 * (long)int.MaxValue; + public IEnumerator GetEnumerator() + { + for (long i = 0; i < MaxSize; i++) yield return (byte)1; + } + IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } + } + + /// + /// Emulation of async collection change. + /// It adds a new element to the sequence each time the Count property touched, + /// so the further call of CopyTo method will fail. + /// + private class GrowingAfterCountReadCollection : TestCollection + { + public GrowingAfterCountReadCollection(int[] items) : base(items) { } + + public override int Count + { + get + { + var result = base.Count; + Array.Resize(ref Items, Items.Length + 1); + return result; + } + } + } + + // ===================== + + + [Fact] + public void ToArray_AlwaysCreateACopy() + { + int[] sourceArray = new int[] { 1, 2, 3, 4, 5 }; + int[] resultArray = sourceArray.ToArray(); + + Assert.NotSame(sourceArray, resultArray); + Assert.Equal(sourceArray, resultArray); + } + + + private void RunToArrayOnAllCollectionTypes(T[] items, Action validation) + { + validation(Enumerable.ToArray(items)); + validation(Enumerable.ToArray(new List(items))); + validation(new TestEnumerable(items).ToArray()); + validation(new TestReadOnlyCollection(items).ToArray()); + validation(new TestCollection(items).ToArray()); + } + + + [Fact] + public void ToArray_WorkWithEmptyCollection() + { + RunToArrayOnAllCollectionTypes(new int[0], + resultArray => + { + Assert.NotNull(resultArray); + Assert.Equal(0, resultArray.Length); + }); + } + + [Fact] + public void ToArray_ProduceCorrectArray() + { + int[] sourceArray = new int[] { 1, 2, 3, 4, 5, 6, 7 }; + RunToArrayOnAllCollectionTypes(sourceArray, + resultArray => + { + Assert.Equal(sourceArray.Length, resultArray.Length); + Assert.Equal(sourceArray, resultArray); + }); + + string[] sourceStringArray = new string[] { "1", "2", "3", "4", "5", "6", "7", "8" }; + RunToArrayOnAllCollectionTypes(sourceStringArray, + resultStringArray => + { + Assert.Equal(sourceStringArray.Length, resultStringArray.Length); + for (int i = 0; i < sourceStringArray.Length; i++) + Assert.Same(sourceStringArray[i], resultStringArray[i]); + }); + } + + + [Fact] + public void ToArray_TouchCountWithICollection() + { + TestCollection source = new TestCollection(new int[] { 1, 2, 3, 4 }); + var resultArray = source.ToArray(); + + Assert.Equal(source, resultArray); + Assert.Equal(1, source.CountTouched); + } + + + [Fact] + public void ToArray_ThrowArgumentNullExceptionWhenSourceIsNull() + { + int[] source = null; + Assert.Throws(() => source.ToArray()); + } + + + // Later this behaviour can be changed + [Fact] + [ActiveIssue(1561)] + public void ToArray_UseCopyToWithICollection() + { + TestCollection source = new TestCollection(new int[] { 1, 2, 3, 4 }); + var resultArray = source.ToArray(); + + Assert.Equal(source, resultArray); + Assert.Equal(1, source.CopyToTouched); + } + + + [Fact] + [ActiveIssue(1561)] + public void ToArray_WorkWhenCountChangedAsynchronously() + { + GrowingAfterCountReadCollection source = new GrowingAfterCountReadCollection(new int[] { 1, 2, 3, 4 }); + var resultArray = source.ToArray(); + + Assert.True(resultArray.Length >= 4); + Assert.Equal(1, resultArray[0]); + Assert.Equal(2, resultArray[0]); + Assert.Equal(3, resultArray[0]); + Assert.Equal(4, resultArray[0]); + } + + + [Fact] + [OuterLoop] + public void ToArray_FailOnExtremelyLargeCollection() + { + TestLargeSequence largeSeq = new TestLargeSequence(); + var thrownException = Assert.ThrowsAny(() => { largeSeq.ToArray(); }); + Assert.True(thrownException.GetType() == typeof(OverflowException) || thrownException.GetType() == typeof(OutOfMemoryException)); + } + } +} diff --git a/src/System.Linq/tests/ToDictionaryTests.cs b/src/System.Linq/tests/ToDictionaryTests.cs new file mode 100644 index 000000000000..4f09851fdd4a --- /dev/null +++ b/src/System.Linq/tests/ToDictionaryTests.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Tests.Helpers; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Linq.Tests +{ + public class ToDictionaryTests + { + private class CustomComparer : IEqualityComparer + { + public bool Equals(T x, T y) { return EqualityComparer.Default.Equals(x, y); } + public int GetHashCode(T obj) { return EqualityComparer.Default.GetHashCode(obj); } + } + + // ===================== + + + [Fact] + public void ToDictionary_AlwaysCreateACopy() + { + Dictionary source = new Dictionary() { { 1, 1 }, { 2, 2 }, { 3, 3 } }; + Dictionary result = source.ToDictionary(key => key.Key, val => val.Value); + + Assert.NotSame(source, result); + Assert.Equal(source, result); + } + + + private void RunToDictionaryOnAllCollectionTypes(T[] items, Action> validation) + { + validation(Enumerable.ToDictionary(items, key => key)); + validation(Enumerable.ToDictionary(items, key => key, value => value)); + validation(Enumerable.ToDictionary(new List(items), key => key)); + validation(Enumerable.ToDictionary(new List(items), key => key, value => value)); + validation(new TestEnumerable(items).ToDictionary(key => key)); + validation(new TestEnumerable(items).ToDictionary(key => key, value => value)); + validation(new TestReadOnlyCollection(items).ToDictionary(key => key)); + validation(new TestReadOnlyCollection(items).ToDictionary(key => key, value => value)); + validation(new TestCollection(items).ToDictionary(key => key)); + validation(new TestCollection(items).ToDictionary(key => key, value => value)); + } + + + [Fact] + public void ToDictionary_WorkWithEmptyCollection() + { + RunToDictionaryOnAllCollectionTypes(new int[0], + resultDictionary => + { + Assert.NotNull(resultDictionary); + Assert.Equal(0, resultDictionary.Count); + }); + } + + + [Fact] + public void ToDictionary_ProduceCorrectDictionary() + { + int[] sourceArray = new int[] { 1, 2, 3, 4, 5, 6, 7 }; + RunToDictionaryOnAllCollectionTypes(sourceArray, + resultDictionary => + { + Assert.Equal(sourceArray.Length, resultDictionary.Count); + Assert.Equal(sourceArray, resultDictionary.Keys); + Assert.Equal(sourceArray, resultDictionary.Values); + }); + + string[] sourceStringArray = new string[] { "1", "2", "3", "4", "5", "6", "7", "8" }; + RunToDictionaryOnAllCollectionTypes(sourceStringArray, + resultDictionary => + { + Assert.Equal(sourceStringArray.Length, resultDictionary.Count); + for (int i = 0; i < sourceStringArray.Length; i++) + Assert.Same(sourceStringArray[i], resultDictionary[sourceStringArray[i]]); + }); + } + + + + [Fact] + public void ToDictionary_PassCustomComparer() + { + CustomComparer comparer = new CustomComparer(); + TestCollection collection = new TestCollection(new int[] { 1, 2, 3, 4, 5, 6 }); + + Dictionary result1 = collection.ToDictionary(key => key, comparer); + Assert.Same(comparer, result1.Comparer); + + Dictionary result2 = collection.ToDictionary(key => key, val => val, comparer); + Assert.Same(comparer, result2.Comparer); + } + + [Fact] + public void ToDictionary_UseDefaultComparerOnNull() + { + CustomComparer comparer = null; + TestCollection collection = new TestCollection(new int[] { 1, 2, 3, 4, 5, 6 }); + + Dictionary result1 = collection.ToDictionary(key => key, comparer); + Assert.Same(EqualityComparer.Default, result1.Comparer); + + Dictionary result2 = collection.ToDictionary(key => key, val => val, comparer); + Assert.Same(EqualityComparer.Default, result2.Comparer); + } + + [Fact] + public void ToDictionary_KeyValueSelectorsWork() + { + TestCollection collection = new TestCollection(new int[] { 1, 2, 3, 4, 5, 6 }); + + Dictionary result = collection.ToDictionary(key => key + 10, val => val + 100); + + Assert.Equal(collection.Items.Select(o => o + 10), result.Keys); + Assert.Equal(collection.Items.Select(o => o + 100), result.Values); + } + + + [Fact] + public void ToDictionary_ThrowArgumentNullExceptionWhenSourceIsNull() + { + int[] source = null; + Assert.Throws(() => source.ToDictionary(key => key)); + } + + + [Fact] + public void ToDictionary_ThrowArgumentNullExceptionWhenKeySelectorIsNull() + { + int[] source = new int[0]; + Func keySelector = null; + Assert.Throws(() => source.ToDictionary(keySelector)); + } + + [Fact] + public void ToDictionary_ThrowArgumentNullExceptionWhenValueSelectorIsNull() + { + int[] source = new int[0]; + Func keySelector = key => key; + Func valueSelector = null; + Assert.Throws(() => source.ToDictionary(keySelector, valueSelector)); + } + + + [Fact] + public void ToDictionary_KeySelectorThrowException() + { + int[] source = new int[] { 1, 2, 3 }; + Func keySelector = key => + { + if (key == 1) + throw new InvalidOperationException(); + return key; + }; + + + Assert.Throws(() => source.ToDictionary(keySelector)); + } + + [Fact] + public void ToDictionary_ThrowWhenKeySelectorReturnNull() + { + int[] source = new int[] { 1, 2, 3 }; + Func keySelector = key => null; + + Assert.Throws(() => source.ToDictionary(keySelector)); + } + + [Fact] + public void ToDictionary_ThrowWhenKeySelectorReturnSameValueTwice() + { + int[] source = new int[] { 1, 2, 3 }; + Func keySelector = key => 1; + + Assert.Throws(() => source.ToDictionary(keySelector)); + } + + [Fact] + public void ToDictionary_ValueSelectorThrowException() + { + int[] source = new int[] { 1, 2, 3 }; + Func keySelector = key => key; + Func valueSelector = value => + { + if (value == 1) + throw new InvalidOperationException(); + return value; + }; + + Assert.Throws(() => source.ToDictionary(keySelector, valueSelector)); + } + } +} diff --git a/src/System.Linq/tests/ToListTests.cs b/src/System.Linq/tests/ToListTests.cs new file mode 100644 index 000000000000..24f5dccea24f --- /dev/null +++ b/src/System.Linq/tests/ToListTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Tests.Helpers; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Linq.Tests +{ + public class ToListTests + { + /// + /// Emulation of async collection change. + /// It adds a new element to the sequence each time the Count property touched, + /// so the further call of CopyTo method will fail. + /// + private class GrowingAfterCountReadCollection : TestCollection + { + public GrowingAfterCountReadCollection(int[] items) : base(items) { } + + public override int Count + { + get + { + var result = base.Count; + Array.Resize(ref Items, Items.Length + 1); + return result; + } + } + } + + // ============================ + + + [Fact] + public void ToList_AlwaysCreateACopy() + { + List sourceList = new List() { 1, 2, 3, 4, 5 }; + List resultList = sourceList.ToList(); + + Assert.NotSame(sourceList, resultList); + Assert.Equal(sourceList, resultList); + } + + + private void RunToListOnAllCollectionTypes(T[] items, Action> validation) + { + validation(Enumerable.ToList(items)); + validation(Enumerable.ToList(new List(items))); + validation(new TestEnumerable(items).ToList()); + validation(new TestReadOnlyCollection(items).ToList()); + validation(new TestCollection(items).ToList()); + } + + + [Fact] + public void ToList_WorkWithEmptyCollection() + { + RunToListOnAllCollectionTypes(new int[0], + resultList => + { + Assert.NotNull(resultList); + Assert.Equal(0, resultList.Count); + }); + } + + [Fact] + public void ToList_ProduceCorrectList() + { + int[] sourceArray = new int[] { 1, 2, 3, 4, 5, 6, 7 }; + RunToListOnAllCollectionTypes(sourceArray, + resultList => + { + Assert.Equal(sourceArray.Length, resultList.Count); + Assert.Equal(sourceArray, resultList); + }); + + string[] sourceStringArray = new string[] { "1", "2", "3", "4", "5", "6", "7", "8" }; + RunToListOnAllCollectionTypes(sourceStringArray, + resultStringList => + { + Assert.Equal(sourceStringArray.Length, resultStringList.Count); + for (int i = 0; i < sourceStringArray.Length; i++) + Assert.Same(sourceStringArray[i], resultStringList[i]); + }); + } + + + [Fact] + public void ToList_TouchCountWithICollection() + { + TestCollection source = new TestCollection(new int[] { 1, 2, 3, 4 }); + var resultList = source.ToList(); + + Assert.Equal(source, resultList); + Assert.Equal(1, source.CountTouched); + } + + + [Fact] + public void ToList_ThrowArgumentNullExceptionWhenSourceIsNull() + { + int[] source = null; + Assert.Throws(() => source.ToList()); + } + + + // Later this behaviour can be changed + [Fact] + [ActiveIssue(1561)] + public void ToList_UseCopyToWithICollection() + { + TestCollection source = new TestCollection(new int[] { 1, 2, 3, 4 }); + var resultList = source.ToList(); + + Assert.Equal(source, resultList); + Assert.Equal(1, source.CopyToTouched); + } + + + [Fact] + [ActiveIssue(1561)] + public void ToList_WorkWhenCountChangedAsynchronously() + { + GrowingAfterCountReadCollection source = new GrowingAfterCountReadCollection(new int[] { 1, 2, 3, 4 }); + var resultList = source.ToList(); + + Assert.True(resultList.Count >= 4); + Assert.Equal(1, resultList[0]); + Assert.Equal(2, resultList[0]); + Assert.Equal(3, resultList[0]); + Assert.Equal(4, resultList[0]); + } + } +} diff --git a/src/System.Linq/tests/project.json b/src/System.Linq/tests/project.json index 7cf60a47035b..25d3ca18fda3 100644 --- a/src/System.Linq/tests/project.json +++ b/src/System.Linq/tests/project.json @@ -8,7 +8,8 @@ "xunit": "2.0.0-beta5-build2785", "xunit.abstractions.netcore": "1.0.0-prerelease", "xunit.assert": "2.0.0-beta5-build2785", - "xunit.core.netcore": "1.0.1-prerelease" + "xunit.core.netcore": "1.0.1-prerelease", + "xunit.netcore.extensions": "1.0.0-prerelease" }, "frameworks": { "dnxcore50": {}