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 Xunit;
7 
8 namespace System.Linq.Parallel.Tests
9 {
10     public static class LastLastOrDefaultTests
11     {
12         private static Func<int, IEnumerable<int>> Positions = x => new[] { 1, x / 2 + 1, Math.Max(1, x - 1) }.Distinct();
13 
LastUnorderedData(int[] counts)14         public static IEnumerable<object[]> LastUnorderedData(int[] counts)
15         {
16             foreach (int count in counts.DefaultIfEmpty(Sources.OuterLoopCount))
17             {
18                 foreach (int position in Positions(count))
19                 {
20                     yield return new object[] { Labeled.Label("UnorderedDefault", UnorderedSources.Default(count)), count, position };
21                 }
22             }
23         }
24 
LastData(int[] counts)25         public static IEnumerable<object[]> LastData(int[] counts)
26         {
27             foreach (object[] results in Sources.Ranges(counts.DefaultIfEmpty(Sources.OuterLoopCount), Positions)) yield return results;
28         }
29 
30         //
31         // Last and LastOrDefault
32         //
33         [Theory]
34         [MemberData(nameof(LastUnorderedData), new[] { 1, 2, 16 })]
35         [MemberData(nameof(LastData), new[] { 1, 2, 16 })]
Last(Labeled<ParallelQuery<int>> labeled, int count, int position)36         public static void Last(Labeled<ParallelQuery<int>> labeled, int count, int position)
37         {
38             // For unordered collections, which element is chosen isn't actually guaranteed, but an effect of the implementation.
39             // If this test starts failing it should be split, and possibly mentioned in release notes.
40             ParallelQuery<int> query = labeled.Item;
41             Assert.Equal(count - 1, query.Last());
42             Assert.Equal(position - 1, query.Last(x => x < position));
43         }
44 
45         [Theory]
46         [OuterLoop]
47         [MemberData(nameof(LastUnorderedData), new int[] { /* Sources.OuterLoopCount */ })]
48         [MemberData(nameof(LastData), new int[] { /* Sources.OuterLoopCount */ })]
Last_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)49         public static void Last_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)
50         {
51             Last(labeled, count, position);
52         }
53 
54         [Theory]
55         [MemberData(nameof(LastUnorderedData), new[] { 1, 2, 16 })]
56         [MemberData(nameof(LastData), new[] { 1, 2, 16 })]
LastOrDefault(Labeled<ParallelQuery<int>> labeled, int count, int position)57         public static void LastOrDefault(Labeled<ParallelQuery<int>> labeled, int count, int position)
58         {
59             // For unordered collections, which element is chosen isn't actually guaranteed, but an effect of the implementation.
60             // If this test starts failing it should be split, and possibly mentioned in release notes.
61             ParallelQuery<int> query = labeled.Item;
62             Assert.Equal(count - 1, query.Last());
63             Assert.Equal(position - 1, query.Last(x => x < position));
64         }
65 
66         [Theory]
67         [OuterLoop]
68         [MemberData(nameof(LastUnorderedData), new int[] { /* Sources.OuterLoopCount */ })]
69         [MemberData(nameof(LastData), new int[] { /* Sources.OuterLoopCount */ })]
LastOrDefault_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)70         public static void LastOrDefault_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)
71         {
72             LastOrDefault(labeled, count, position);
73         }
74 
75         [Theory]
76         [MemberData(nameof(LastUnorderedData), new[] { 0 })]
77         [MemberData(nameof(LastData), new[] { 0 })]
Last_Empty(Labeled<ParallelQuery<int>> labeled, int count, int position)78         public static void Last_Empty(Labeled<ParallelQuery<int>> labeled, int count, int position)
79         {
80             ParallelQuery<int> query = labeled.Item;
81             Assert.Throws<InvalidOperationException>(() => query.Last());
82         }
83 
84         [Theory]
85         [MemberData(nameof(LastUnorderedData), new[] { 0 })]
86         [MemberData(nameof(LastData), new[] { 0 })]
LastOrDefault_Empty(Labeled<ParallelQuery<int>> labeled, int count, int position)87         public static void LastOrDefault_Empty(Labeled<ParallelQuery<int>> labeled, int count, int position)
88         {
89             ParallelQuery<int> query = labeled.Item;
90             Assert.Equal(default(int), query.LastOrDefault());
91         }
92 
93         [Theory]
94         [MemberData(nameof(LastUnorderedData), new[] { 1, 2, 16 })]
95         [MemberData(nameof(LastData), new[] { 1, 2, 16 })]
Last_NoMatch(Labeled<ParallelQuery<int>> labeled, int count, int position)96         public static void Last_NoMatch(Labeled<ParallelQuery<int>> labeled, int count, int position)
97         {
98             ParallelQuery<int> query = labeled.Item;
99             IntegerRangeSet seen = new IntegerRangeSet(0, count);
100             Assert.Throws<InvalidOperationException>(() => query.Last(x => !seen.Add(x)));
101             seen.AssertComplete();
102         }
103 
104         [Theory]
105         [OuterLoop]
106         [MemberData(nameof(LastUnorderedData), new int[] { /* Sources.OuterLoopCount */ })]
107         [MemberData(nameof(LastData), new int[] { /* Sources.OuterLoopCount */ })]
Last_NoMatch_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)108         public static void Last_NoMatch_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)
109         {
110             Last_NoMatch(labeled, count, position);
111         }
112 
113         [Theory]
114         [MemberData(nameof(LastUnorderedData), new[] { 1, 2, 16 })]
115         [MemberData(nameof(LastData), new[] { 1, 2, 16 })]
LastOrDefault_NoMatch(Labeled<ParallelQuery<int>> labeled, int count, int position)116         public static void LastOrDefault_NoMatch(Labeled<ParallelQuery<int>> labeled, int count, int position)
117         {
118             ParallelQuery<int> query = labeled.Item;
119             IntegerRangeSet seen = new IntegerRangeSet(0, count);
120             Assert.Equal(default(int), query.LastOrDefault(x => !seen.Add(x)));
121             seen.AssertComplete();
122         }
123 
124         [Theory]
125         [OuterLoop]
126         [MemberData(nameof(LastUnorderedData), new int[] { /* Sources.OuterLoopCount */ })]
127         [MemberData(nameof(LastData), new int[] { /* Sources.OuterLoopCount */ })]
LastOrDefault_NoMatch_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)128         public static void LastOrDefault_NoMatch_Longrunning(Labeled<ParallelQuery<int>> labeled, int count, int position)
129         {
130             LastOrDefault_NoMatch(labeled, count, position);
131         }
132 
133         [Fact]
Last_OperationCanceledException()134         public static void Last_OperationCanceledException()
135         {
136             AssertThrows.EventuallyCanceled((source, canceler) => source.Last(x => { canceler(); return true; }));
137             AssertThrows.EventuallyCanceled((source, canceler) => source.LastOrDefault(x => { canceler(); return true; }));
138         }
139 
140         [Fact]
Last_AggregateException_Wraps_OperationCanceledException()141         public static void Last_AggregateException_Wraps_OperationCanceledException()
142         {
143             AssertThrows.OtherTokenCanceled((source, canceler) => source.Last(x => { canceler(); return true; }));
144             AssertThrows.OtherTokenCanceled((source, canceler) => source.LastOrDefault(x => { canceler(); return false; }));
145             AssertThrows.SameTokenNotCanceled((source, canceler) => source.Last(x => { canceler(); return true; }));
146             AssertThrows.SameTokenNotCanceled((source, canceler) => source.LastOrDefault(x => { canceler(); return false; }));
147         }
148 
149         [Fact]
Last_OperationCanceledException_PreCanceled()150         public static void Last_OperationCanceledException_PreCanceled()
151         {
152             AssertThrows.AlreadyCanceled(source => source.Last());
153             AssertThrows.AlreadyCanceled(source => source.Last(x => true));
154 
155             AssertThrows.AlreadyCanceled(source => source.LastOrDefault());
156             AssertThrows.AlreadyCanceled(source => source.LastOrDefault(x => true));
157         }
158 
159         [Fact]
Last_AggregateException()160         public static void Last_AggregateException()
161         {
162             AssertThrows.Wrapped<DeliberateTestException>(() => UnorderedSources.Default(1).Last(x => { throw new DeliberateTestException(); }));
163             AssertThrows.Wrapped<DeliberateTestException>(() => UnorderedSources.Default(1).LastOrDefault(x => { throw new DeliberateTestException(); }));
164         }
165 
166         [Fact]
Last_ArgumentNullException()167         public static void Last_ArgumentNullException()
168         {
169             AssertExtensions.Throws<ArgumentNullException>("source", () => ((ParallelQuery<bool>)null).Last());
170             AssertExtensions.Throws<ArgumentNullException>("source", () => ((ParallelQuery<bool>)null).LastOrDefault());
171 
172             AssertExtensions.Throws<ArgumentNullException>("predicate", () => ParallelEnumerable.Empty<int>().Last(null));
173             AssertExtensions.Throws<ArgumentNullException>("predicate", () => ParallelEnumerable.Empty<int>().LastOrDefault(null));
174         }
175     }
176 }
177