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