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.Collections.Generic; 6 using System.Threading; 7 using System.Threading.Tasks; 8 using Xunit; 9 10 namespace System.Linq.Parallel.Tests 11 { 12 public static class GetEnumeratorTests 13 { 14 [Theory] 15 [MemberData(nameof(UnorderedSources.Ranges), new[] { 0, 1, 2, 16 }, MemberType = typeof(UnorderedSources))] GetEnumerator_Unordered(Labeled<ParallelQuery<int>> labeled, int count)16 public static void GetEnumerator_Unordered(Labeled<ParallelQuery<int>> labeled, int count) 17 { 18 IntegerRangeSet seen = new IntegerRangeSet(0, count); 19 IEnumerator<int> enumerator = labeled.Item.GetEnumerator(); 20 while (enumerator.MoveNext()) 21 { 22 int current = enumerator.Current; 23 seen.Add(current); 24 Assert.Equal(current, enumerator.Current); 25 } 26 seen.AssertComplete(); 27 28 if (labeled.ToString().StartsWith("Enumerable.Range") || labeled.ToString().StartsWith("Partitioner")) 29 { 30 Assert.Throws<NotSupportedException>(() => enumerator.Reset()); 31 } 32 else 33 { 34 enumerator.Reset(); 35 seen = new IntegerRangeSet(0, count); 36 while (enumerator.MoveNext()) 37 { 38 Assert.True(seen.Add(enumerator.Current)); 39 } 40 seen.AssertComplete(); 41 } 42 } 43 44 [Theory] 45 [OuterLoop] 46 [MemberData(nameof(UnorderedSources.OuterLoopRanges), MemberType = typeof(UnorderedSources))] GetEnumerator_Unordered_Longrunning(Labeled<ParallelQuery<int>> labeled, int count)47 public static void GetEnumerator_Unordered_Longrunning(Labeled<ParallelQuery<int>> labeled, int count) 48 { 49 GetEnumerator_Unordered(labeled, count); 50 } 51 52 [Theory] 53 [MemberData(nameof(Sources.Ranges), new[] { 0, 1, 2, 16 }, MemberType = typeof(Sources))] GetEnumerator(Labeled<ParallelQuery<int>> labeled, int count)54 public static void GetEnumerator(Labeled<ParallelQuery<int>> labeled, int count) 55 { 56 int seen = 0; 57 IEnumerator<int> enumerator = labeled.Item.GetEnumerator(); 58 while (enumerator.MoveNext()) 59 { 60 int current = enumerator.Current; 61 Assert.Equal(seen++, current); 62 Assert.Equal(current, enumerator.Current); 63 } 64 Assert.Equal(count, seen); 65 66 if (labeled.ToString().StartsWith("Enumerable.Range") || labeled.ToString().StartsWith("Partitioner")) 67 { 68 Assert.Throws<NotSupportedException>(() => enumerator.Reset()); 69 } 70 else 71 { 72 enumerator.Reset(); 73 seen = 0; 74 while (enumerator.MoveNext()) 75 { 76 Assert.Equal(seen++, enumerator.Current); 77 } 78 Assert.Equal(count, seen); 79 } 80 } 81 82 [Theory] 83 [OuterLoop] 84 [MemberData(nameof(Sources.OuterLoopRanges), MemberType = typeof(Sources))] GetEnumerator_Longrunning(Labeled<ParallelQuery<int>> labeled, int count)85 public static void GetEnumerator_Longrunning(Labeled<ParallelQuery<int>> labeled, int count) 86 { 87 GetEnumerator(labeled, count); 88 } 89 90 [Theory] 91 [MemberData(nameof(UnorderedSources.Ranges), new[] { 128 }, MemberType = typeof(UnorderedSources))] 92 [MemberData(nameof(Sources.Ranges), new[] { 128 }, MemberType = typeof(Sources))] GetEnumerator_OperationCanceledException(Labeled<ParallelQuery<int>> labeled, int count)93 public static void GetEnumerator_OperationCanceledException(Labeled<ParallelQuery<int>> labeled, int count) 94 { 95 CancellationTokenSource source = new CancellationTokenSource(); 96 int countdown = 4; 97 Action cancel = () => { if (Interlocked.Decrement(ref countdown) == 0) source.Cancel(); }; 98 99 OperationCanceledException oce = Assert.Throws<OperationCanceledException>(() => { foreach (var i in labeled.Item.WithCancellation(source.Token)) cancel(); }); 100 Assert.Equal(source.Token, oce.CancellationToken); 101 } 102 103 [Theory] 104 [MemberData(nameof(UnorderedSources.Ranges), new[] { 1 }, MemberType = typeof(UnorderedSources))] 105 [MemberData(nameof(Sources.Ranges), new[] { 1 }, MemberType = typeof(Sources))] GetEnumerator_OperationCanceledException_PreCanceled(Labeled<ParallelQuery<int>> labeled, int count)106 public static void GetEnumerator_OperationCanceledException_PreCanceled(Labeled<ParallelQuery<int>> labeled, int count) 107 { 108 Assert.Throws<OperationCanceledException>(() => { foreach (var i in labeled.Item.WithCancellation(new CancellationToken(canceled: true))) { throw new ShouldNotBeInvokedException(); }; }); 109 } 110 111 [Theory] 112 [MemberData(nameof(UnorderedSources.Ranges), new[] { 1, 2, 16 }, MemberType = typeof(UnorderedSources))] GetEnumerator_MoveNextAfterQueryOpeningFailsIsIllegal(Labeled<ParallelQuery<int>> labeled, int count)113 public static void GetEnumerator_MoveNextAfterQueryOpeningFailsIsIllegal(Labeled<ParallelQuery<int>> labeled, int count) 114 { 115 ParallelQuery<int> query = labeled.Item.Select<int, int>(x => { throw new DeliberateTestException(); }).OrderBy(x => x); 116 117 IEnumerator<int> enumerator = query.GetEnumerator(); 118 119 //moveNext will cause queryOpening to fail (no element generated) 120 AssertThrows.Wrapped<DeliberateTestException>(() => enumerator.MoveNext()); 121 122 //moveNext after queryOpening failed 123 Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext()); 124 } 125 126 [Theory] 127 [MemberData(nameof(UnorderedSources.Ranges), new[] { 16 }, MemberType = typeof(UnorderedSources))] 128 [MemberData(nameof(Sources.Ranges), new[] { 16 }, MemberType = typeof(Sources))] GetEnumerator_CurrentBeforeMoveNext(Labeled<ParallelQuery<int>> labeled, int count)129 public static void GetEnumerator_CurrentBeforeMoveNext(Labeled<ParallelQuery<int>> labeled, int count) 130 { 131 IEnumerator<int> enumerator = labeled.Item.GetEnumerator(); 132 if (labeled.ToString().StartsWith("Partitioner") 133 || labeled.ToString().StartsWith("Array")) 134 { 135 Assert.Throws<InvalidOperationException>(() => enumerator.Current); 136 } 137 else 138 { 139 Assert.InRange(enumerator.Current, 0, count); 140 } 141 } 142 143 [Theory] 144 [MemberData(nameof(UnorderedSources.Ranges), new[] { 0, 1, 2, 16 }, MemberType = typeof(UnorderedSources))] 145 [MemberData(nameof(Sources.Ranges), new[] { 0, 1, 2, 16 }, MemberType = typeof(Sources))] GetEnumerator_MoveNextAfterEnd(Labeled<ParallelQuery<int>> labeled, int count)146 public static void GetEnumerator_MoveNextAfterEnd(Labeled<ParallelQuery<int>> labeled, int count) 147 { 148 IEnumerator<int> enumerator = labeled.Item.GetEnumerator(); 149 while (enumerator.MoveNext()) 150 { 151 count--; 152 } 153 Assert.Equal(0, count); 154 Assert.False(enumerator.MoveNext()); 155 } 156 157 [Fact] GetEnumerator_LargeQuery_PauseAfterOpening()158 public static void GetEnumerator_LargeQuery_PauseAfterOpening() 159 { 160 using (IEnumerator<int> e = Enumerable.Range(0, 8192).AsParallel().SkipWhile(i => true).GetEnumerator()) 161 { 162 e.MoveNext(); 163 Task.Delay(100).Wait(); // verify nothing goes haywire when the internal buffer is allowed to fill 164 while (e.MoveNext()) ; 165 Assert.False(e.MoveNext()); 166 } 167 } 168 169 [Theory] 170 [MemberData(nameof(UnorderedSources.Ranges), new[] { 0, 1, 2 }, MemberType = typeof(UnorderedSources))] GetEnumerator_DisposeBeforeFirstMoveNext(Labeled<ParallelQuery<int>> labeled, int count)171 public static void GetEnumerator_DisposeBeforeFirstMoveNext(Labeled<ParallelQuery<int>> labeled, int count) 172 { 173 IEnumerator<int> e = labeled.Item.Select(i => i).GetEnumerator(); 174 e.Dispose(); 175 Assert.Throws<ObjectDisposedException>(() => e.MoveNext()); 176 } 177 178 [Theory] 179 [MemberData(nameof(UnorderedSources.Ranges), new[] { 1, 2 }, MemberType = typeof(UnorderedSources))] GetEnumerator_DisposeAfterMoveNext(Labeled<ParallelQuery<int>> labeled, int count)180 public static void GetEnumerator_DisposeAfterMoveNext(Labeled<ParallelQuery<int>> labeled, int count) 181 { 182 IEnumerator<int> e = labeled.Item.Select(i => i).GetEnumerator(); 183 e.MoveNext(); 184 e.Dispose(); 185 Assert.Throws<ObjectDisposedException>(() => e.MoveNext()); 186 } 187 188 } 189 } 190