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