1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Linq;
11 using System.Text;
12 using System.Threading.Tasks;
13 using Xunit;
14 
15 namespace System.Linq.Tests
16 {
17     public class ToArrayTests : EnumerableTests
18     {
19         [Fact]
ToArray_CreateACopyWhenNotEmpty()20         public void ToArray_CreateACopyWhenNotEmpty()
21         {
22             int[] sourceArray = new int[] { 1, 2, 3, 4, 5 };
23             int[] resultArray = sourceArray.ToArray();
24 
25             Assert.NotSame(sourceArray, resultArray);
26             Assert.Equal(sourceArray, resultArray);
27         }
28 
29         [Fact]
ToArray_UseArrayEmptyWhenEmpty()30         public void ToArray_UseArrayEmptyWhenEmpty()
31         {
32             int[] emptySourceArray = Array.Empty<int>();
33 
34             // .NET Core returns the instance as an optimization.
35             // see https://github.com/dotnet/corefx/pull/2401.
36             Action<object, object> assertSame = (objA, objB) =>
37                 Assert.Equal(!PlatformDetection.IsFullFramework, ReferenceEquals(objA, objB));
38 
39 
40             assertSame(emptySourceArray.ToArray(), emptySourceArray.ToArray());
41 
42             assertSame(emptySourceArray.Select(i => i).ToArray(), emptySourceArray.Select(i => i).ToArray());
43             assertSame(emptySourceArray.ToList().Select(i => i).ToArray(), emptySourceArray.ToList().Select(i => i).ToArray());
44             assertSame(new Collection<int>(emptySourceArray).Select(i => i).ToArray(), new Collection<int>(emptySourceArray).Select(i => i).ToArray());
45             assertSame(emptySourceArray.OrderBy(i => i).ToArray(), emptySourceArray.OrderBy(i => i).ToArray());
46 
47             assertSame(Enumerable.Range(5, 0).ToArray(), Enumerable.Range(3, 0).ToArray());
48             assertSame(Enumerable.Range(5, 3).Take(0).ToArray(), Enumerable.Range(3, 0).ToArray());
49             assertSame(Enumerable.Range(5, 3).Skip(3).ToArray(), Enumerable.Range(3, 0).ToArray());
50 
51             assertSame(Enumerable.Repeat(42, 0).ToArray(), Enumerable.Range(84, 0).ToArray());
52             assertSame(Enumerable.Repeat(42, 3).Take(0).ToArray(), Enumerable.Range(84, 3).Take(0).ToArray());
53             assertSame(Enumerable.Repeat(42, 3).Skip(3).ToArray(), Enumerable.Range(84, 3).Skip(3).ToArray());
54         }
55 
56 
RunToArrayOnAllCollectionTypes(T[] items, Action<T[]> validation)57         private void RunToArrayOnAllCollectionTypes<T>(T[] items, Action<T[]> validation)
58         {
59             validation(Enumerable.ToArray(items));
60             validation(Enumerable.ToArray(new List<T>(items)));
61             validation(new TestEnumerable<T>(items).ToArray());
62             validation(new TestReadOnlyCollection<T>(items).ToArray());
63             validation(new TestCollection<T>(items).ToArray());
64         }
65 
66 
67         [Fact]
ToArray_WorkWithEmptyCollection()68         public void ToArray_WorkWithEmptyCollection()
69         {
70             RunToArrayOnAllCollectionTypes(new int[0],
71                 resultArray =>
72                 {
73                     Assert.NotNull(resultArray);
74                     Assert.Equal(0, resultArray.Length);
75                 });
76         }
77 
78         [Fact]
ToArray_ProduceCorrectArray()79         public void ToArray_ProduceCorrectArray()
80         {
81             int[] sourceArray = new int[] { 1, 2, 3, 4, 5, 6, 7 };
82             RunToArrayOnAllCollectionTypes(sourceArray,
83                 resultArray =>
84                 {
85                     Assert.Equal(sourceArray.Length, resultArray.Length);
86                     Assert.Equal(sourceArray, resultArray);
87                 });
88 
89             string[] sourceStringArray = new string[] { "1", "2", "3", "4", "5", "6", "7", "8" };
90             RunToArrayOnAllCollectionTypes(sourceStringArray,
91                 resultStringArray =>
92                 {
93                     Assert.Equal(sourceStringArray.Length, resultStringArray.Length);
94                     for (int i = 0; i < sourceStringArray.Length; i++)
95                         Assert.Same(sourceStringArray[i], resultStringArray[i]);
96                 });
97         }
98 
99         [Fact]
RunOnce()100         public void RunOnce()
101         {
102             Assert.Equal(new int[] {1, 2, 3, 4, 5, 6, 7}, Enumerable.Range(1, 7).RunOnce().ToArray());
103             Assert.Equal(
104                 new string[] {"1", "2", "3", "4", "5", "6", "7", "8"},
105                 Enumerable.Range(1, 8).Select(i => i.ToString()).RunOnce().ToArray());
106         }
107 
108         [Fact]
ToArray_TouchCountWithICollection()109         public void ToArray_TouchCountWithICollection()
110         {
111             TestCollection<int> source = new TestCollection<int>(new int[] { 1, 2, 3, 4 });
112             var resultArray = source.ToArray();
113 
114             Assert.Equal(source, resultArray);
115             Assert.Equal(1, source.CountTouched);
116         }
117 
118 
119         [Fact]
ToArray_ThrowArgumentNullExceptionWhenSourceIsNull()120         public void ToArray_ThrowArgumentNullExceptionWhenSourceIsNull()
121         {
122             int[] source = null;
123             AssertExtensions.Throws<ArgumentNullException>("source", () => source.ToArray());
124         }
125 
126         // Generally the optimal approach. Anything that breaks this should be confirmed as not harming performance.
127         [Fact]
ToArray_UseCopyToWithICollection()128         public void ToArray_UseCopyToWithICollection()
129         {
130             TestCollection<int> source = new TestCollection<int>(new int[] { 1, 2, 3, 4 });
131             var resultArray = source.ToArray();
132 
133             Assert.Equal(source, resultArray);
134             Assert.Equal(1, source.CopyToTouched);
135         }
136 
137         [Fact(Skip = "Valid test but too intensive to enable even in OuterLoop")]
ToArray_FailOnExtremelyLargeCollection()138         public void ToArray_FailOnExtremelyLargeCollection()
139         {
140             var largeSeq = new FastInfiniteEnumerator<byte>();
141             var thrownException = Assert.ThrowsAny<Exception>(() => { largeSeq.ToArray(); });
142             Assert.True(thrownException.GetType() == typeof(OverflowException) || thrownException.GetType() == typeof(OutOfMemoryException));
143         }
144 
145         [Theory]
146         [InlineData(new int[] { }, new string[] { })]
147         [InlineData(new int[] { 1 }, new string[] { "1" })]
148         [InlineData(new int[] { 1, 2, 3 }, new string[] { "1", "2", "3" })]
ToArray_ArrayWhereSelect(int[] sourceIntegers, string[] convertedStrings)149         public void ToArray_ArrayWhereSelect(int[] sourceIntegers, string[] convertedStrings)
150         {
151             Assert.Equal(convertedStrings, sourceIntegers.Select(i => i.ToString()).ToArray());
152 
153             Assert.Equal(sourceIntegers, sourceIntegers.Where(i => true).ToArray());
154             Assert.Equal(Array.Empty<int>(), sourceIntegers.Where(i => false).ToArray());
155 
156             Assert.Equal(convertedStrings, sourceIntegers.Where(i => true).Select(i => i.ToString()).ToArray());
157             Assert.Equal(Array.Empty<string>(), sourceIntegers.Where(i => false).Select(i => i.ToString()).ToArray());
158 
159             Assert.Equal(convertedStrings, sourceIntegers.Select(i => i.ToString()).Where(s => s != null).ToArray());
160             Assert.Equal(Array.Empty<string>(), sourceIntegers.Select(i => i.ToString()).Where(s => s == null).ToArray());
161         }
162 
163         [Theory]
164         [InlineData(new int[] { }, new string[] { })]
165         [InlineData(new int[] { 1 }, new string[] { "1" })]
166         [InlineData(new int[] { 1, 2, 3 }, new string[] { "1", "2", "3" })]
ToArray_ListWhereSelect(int[] sourceIntegers, string[] convertedStrings)167         public void ToArray_ListWhereSelect(int[] sourceIntegers, string[] convertedStrings)
168         {
169             var sourceList = new List<int>(sourceIntegers);
170 
171             Assert.Equal(convertedStrings, sourceList.Select(i => i.ToString()).ToArray());
172 
173             Assert.Equal(sourceList, sourceList.Where(i => true).ToArray());
174             Assert.Equal(Array.Empty<int>(), sourceList.Where(i => false).ToArray());
175 
176             Assert.Equal(convertedStrings, sourceList.Where(i => true).Select(i => i.ToString()).ToArray());
177             Assert.Equal(Array.Empty<string>(), sourceList.Where(i => false).Select(i => i.ToString()).ToArray());
178 
179             Assert.Equal(convertedStrings, sourceList.Select(i => i.ToString()).Where(s => s != null).ToArray());
180             Assert.Equal(Array.Empty<string>(), sourceList.Select(i => i.ToString()).Where(s => s == null).ToArray());
181         }
182 
183         [Fact]
SameResultsRepeatCallsFromWhereOnIntQuery()184         public void SameResultsRepeatCallsFromWhereOnIntQuery()
185         {
186             var q = from x in new[] { 9999, 0, 888, -1, 66, -777, 1, 2, -12345 }
187                     where x > Int32.MinValue
188                     select x;
189 
190             Assert.Equal(q.ToArray(), q.ToArray());
191         }
192 
193         [Fact]
SameResultsRepeatCallsFromWhereOnStringQuery()194         public void SameResultsRepeatCallsFromWhereOnStringQuery()
195         {
196             var q = from x in new[] { "!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", String.Empty }
197                         where !String.IsNullOrEmpty(x)
198                         select x;
199 
200             Assert.Equal(q.ToArray(), q.ToArray());
201         }
202 
203         [Fact]
SameResultsButNotSameObject()204         public void SameResultsButNotSameObject()
205         {
206             var qInt = from x in new[] { 9999, 0, 888, -1, 66, -777, 1, 2, -12345 }
207                     where x > Int32.MinValue
208                     select x;
209 
210             var qString = from x in new[] { "!@#$%^", "C", "AAA", "", "Calling Twice", "SoS", String.Empty }
211                         where !String.IsNullOrEmpty(x)
212                         select x;
213 
214             Assert.NotSame(qInt.ToArray(), qInt.ToArray());
215             Assert.NotSame(qString.ToArray(), qString.ToArray());
216         }
217 
218         [Fact]
EmptyArraysSameObject()219         public void EmptyArraysSameObject()
220         {
221             // .NET Core returns the instance as an optimization.
222             // see https://github.com/dotnet/corefx/pull/2401.
223             Assert.Equal(!PlatformDetection.IsFullFramework, ReferenceEquals(Enumerable.Empty<int>().ToArray(), Enumerable.Empty<int>().ToArray()));
224 
225             var array = new int[0];
226             Assert.NotSame(array, array.ToArray());
227         }
228 
229         [Fact]
SourceIsEmptyICollectionT()230         public void SourceIsEmptyICollectionT()
231         {
232             int[] source = { };
233 
234             ICollection<int> collection = source as ICollection<int>;
235 
236             Assert.Empty(source.ToArray());
237             Assert.Empty(collection.ToArray());
238         }
239 
240         [Fact]
SourceIsICollectionTWithFewElements()241         public void SourceIsICollectionTWithFewElements()
242         {
243             int?[] source = { -5, null, 0, 10, 3, -1, null, 4, 9 };
244             int?[] expected = { -5, null, 0, 10, 3, -1, null, 4, 9 };
245 
246             ICollection<int?> collection = source as ICollection<int?>;
247 
248             Assert.Equal(expected, source.ToArray());
249             Assert.Equal(expected, collection.ToArray());
250         }
251 
252         [Fact]
SourceNotICollectionAndIsEmpty()253         public void SourceNotICollectionAndIsEmpty()
254         {
255             IEnumerable<int> source = NumberRangeGuaranteedNotCollectionType(-4, 0);
256 
257             Assert.Null(source as ICollection<int>);
258 
259             Assert.Empty(source.ToArray());
260         }
261 
262         [Fact]
SourceNotICollectionAndHasElements()263         public void SourceNotICollectionAndHasElements()
264         {
265             IEnumerable<int> source = NumberRangeGuaranteedNotCollectionType(-4, 10);
266             int[] expected = { -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 };
267 
268             Assert.Null(source as ICollection<int>);
269 
270             Assert.Equal(expected, source.ToArray());
271         }
272 
273         [Fact]
SourceNotICollectionAndAllNull()274         public void SourceNotICollectionAndAllNull()
275         {
276             IEnumerable<int?> source = RepeatedNullableNumberGuaranteedNotCollectionType(null, 5);
277             int?[] expected = { null, null, null, null, null };
278 
279             Assert.Null(source as ICollection<int>);
280 
281             Assert.Equal(expected, source.ToArray());
282         }
283 
284         [Fact]
ConstantTimeCountPartitionSelectSameTypeToArray()285         public void ConstantTimeCountPartitionSelectSameTypeToArray()
286         {
287             var source = Enumerable.Range(0, 100).Select(i => i * 2).Skip(1).Take(5);
288             Assert.Equal(new[] { 2, 4, 6, 8, 10 }, source.ToArray());
289         }
290 
291         [Fact]
ConstantTimeCountPartitionSelectDiffTypeToArray()292         public void ConstantTimeCountPartitionSelectDiffTypeToArray()
293         {
294             var source = Enumerable.Range(0, 100).Select(i => i.ToString()).Skip(1).Take(5);
295             Assert.Equal(new[] { "1", "2", "3", "4", "5" }, source.ToArray());
296         }
297 
298         [Fact]
ConstantTimeCountEmptyPartitionSelectSameTypeToArray()299         public void ConstantTimeCountEmptyPartitionSelectSameTypeToArray()
300         {
301             var source = Enumerable.Range(0, 100).Select(i => i * 2).Skip(1000);
302             Assert.Empty(source.ToArray());
303         }
304 
305         [Fact]
ConstantTimeCountEmptyPartitionSelectDiffTypeToArray()306         public void ConstantTimeCountEmptyPartitionSelectDiffTypeToArray()
307         {
308             var source = Enumerable.Range(0, 100).Select(i => i.ToString()).Skip(1000);
309             Assert.Empty(source.ToArray());
310         }
311 
312         [Fact]
NonConstantTimeCountPartitionSelectSameTypeToArray()313         public void NonConstantTimeCountPartitionSelectSameTypeToArray()
314         {
315             var source = NumberRangeGuaranteedNotCollectionType(0, 100).OrderBy(i => i).Select(i => i * 2).Skip(1).Take(5);
316             Assert.Equal(new[] { 2, 4, 6, 8, 10 }, source.ToArray());
317         }
318 
319         [Fact]
NonConstantTimeCountPartitionSelectDiffTypeToArray()320         public void NonConstantTimeCountPartitionSelectDiffTypeToArray()
321         {
322             var source = NumberRangeGuaranteedNotCollectionType(0, 100).OrderBy(i => i).Select(i => i.ToString()).Skip(1).Take(5);
323             Assert.Equal(new[] { "1", "2", "3", "4", "5" }, source.ToArray());
324         }
325 
326         [Fact]
NonConstantTimeCountEmptyPartitionSelectSameTypeToArray()327         public void NonConstantTimeCountEmptyPartitionSelectSameTypeToArray()
328         {
329             var source = NumberRangeGuaranteedNotCollectionType(0, 100).OrderBy(i => i).Select(i => i * 2).Skip(1000);
330             Assert.Empty(source.ToArray());
331         }
332 
333         [Fact]
NonConstantTimeCountEmptyPartitionSelectDiffTypeToArray()334         public void NonConstantTimeCountEmptyPartitionSelectDiffTypeToArray()
335         {
336             var source = NumberRangeGuaranteedNotCollectionType(0, 100).OrderBy(i => i).Select(i => i.ToString()).Skip(1000);
337             Assert.Empty(source.ToArray());
338         }
339 
340         [Theory]
341         [MemberData(nameof(JustBelowPowersOfTwoLengths))]
342         [MemberData(nameof(PowersOfTwoLengths))]
343         [MemberData(nameof(JustAbovePowersOfTwoLengths))]
ToArrayShouldWorkWithSpecialLengthLazyEnumerables(int length)344         public void ToArrayShouldWorkWithSpecialLengthLazyEnumerables(int length)
345         {
346             Debug.Assert(length >= 0);
347 
348             var range = Enumerable.Range(0, length);
349             var lazyEnumerable = ForceNotCollection(range); // We won't go down the IIListProvider path
350             Assert.Equal(range, lazyEnumerable.ToArray());
351         }
352 
353         // Consider that two very similar enums is not unheard of, if e.g. two assemblies map the
354         // same external source of numbers (codes, response codes, colour codes, etc.) to values.
355         private enum Enum0
356         {
357             First,
358             Second,
359             Third
360         }
361 
362         private enum Enum1
363         {
364             First,
365             Second,
366             Third
367         }
368 
369         [Fact, SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "The full .NET framework disallows copying assignable value types. See corefx#13816")]
ToArray_Cast()370         public void ToArray_Cast()
371         {
372             Enum0[] source = { Enum0.First, Enum0.Second, Enum0.Third };
373             var cast = source.Cast<Enum1>();
374             Assert.IsType<Enum0[]>(cast);
375             var castArray = cast.ToArray();
376             Assert.IsType<Enum1[]>(castArray);
377             Assert.Equal(new[] { Enum1.First, Enum1.Second, Enum1.Third }, castArray);
378         }
379 
JustBelowPowersOfTwoLengths()380         public static IEnumerable<object[]> JustBelowPowersOfTwoLengths()
381         {
382             return SmallPowersOfTwo.Select(p => new object[] { p - 1 });
383         }
384 
PowersOfTwoLengths()385         public static IEnumerable<object[]> PowersOfTwoLengths()
386         {
387             return SmallPowersOfTwo.Select(p => new object[] { p });
388         }
389 
JustAbovePowersOfTwoLengths()390         public static IEnumerable<object[]> JustAbovePowersOfTwoLengths()
391         {
392             return SmallPowersOfTwo.Select(p => new object[] { p + 1 });
393         }
394 
395         private static IEnumerable<int> SmallPowersOfTwo
396         {
397             get
398             {
399                 // By N being "small" we mean that allocating an array of
400                 // size N doesn't come close to the risk of causing an OOME
401 
402                 const int MaxPower = 18;
403 
404                 for (int i = 0; i <= MaxPower; i++)
405                 {
406                     yield return 1 << i; // equivalent to pow(2, i)
407                 }
408             }
409         }
410     }
411 }
412