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 Xunit;
8 
9 namespace System.Linq.Parallel.Tests
10 {
11     public static class ConcatTests
12     {
ConcatUnorderedData(int[] counts)13         public static IEnumerable<object[]> ConcatUnorderedData(int[] counts)
14         {
15             foreach (int leftCount in counts)
16             {
17                 foreach (int rightCount in counts)
18                 {
19                     yield return new object[] { leftCount, rightCount };
20                 }
21             }
22         }
23 
ConcatData(int[] counts)24         public static IEnumerable<object[]> ConcatData(int[] counts)
25         {
26             foreach (object[] parms in UnorderedSources.BinaryRanges(counts.DefaultIfEmpty(Sources.OuterLoopCount), (left, right) => left, counts))
27             {
28                 yield return new object[] { ((Labeled<ParallelQuery<int>>)parms[0]).Order(), parms[1], ((Labeled<ParallelQuery<int>>)parms[2]).Order(), parms[3] };
29                 yield return new object[] { ((Labeled<ParallelQuery<int>>)parms[0]).Order(), parms[1], parms[2], parms[3] };
30                 yield return new object[] { parms[0], parms[1], ((Labeled<ParallelQuery<int>>)parms[2]).Order(), parms[3] };
31             }
32         }
33 
34         //
35         // Concat
36         //
37         [Theory]
38         [MemberData(nameof(ConcatUnorderedData), new[] { 0, 1, 2, 16 })]
Concat_Unordered(int leftCount, int rightCount)39         public static void Concat_Unordered(int leftCount, int rightCount)
40         {
41             IntegerRangeSet seen = new IntegerRangeSet(0, leftCount + rightCount);
42             foreach (int i in UnorderedSources.Default(0, leftCount).Concat(UnorderedSources.Default(leftCount, rightCount)))
43             {
44                 seen.Add(i);
45             }
46             seen.AssertComplete();
47         }
48 
49         [Fact]
50         [OuterLoop]
Concat_Unordered_Longrunning()51         public static void Concat_Unordered_Longrunning()
52         {
53             Concat_Unordered(Sources.OuterLoopCount, Sources.OuterLoopCount);
54         }
55 
56         [Theory]
57         [MemberData(nameof(ConcatData), new[] { 0, 1, 2, 16 })]
Concat(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)58         public static void Concat(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)
59         {
60             // The ordering of Concat is only guaranteed when both operands are ordered,
61             // however the current implementation manages to perform ordering if either operand is ordered _in most cases_.
62             // If this test starts failing, consider revising the operators and mention the change in release notes.
63             ParallelQuery<int> leftQuery = left.Item;
64             ParallelQuery<int> rightQuery = right.Item;
65             int seen = 0;
66             foreach (int i in leftQuery.Concat(rightQuery))
67             {
68                 Assert.Equal(seen++, i);
69             }
70             Assert.Equal(seen, leftCount + rightCount);
71         }
72 
73         [Theory]
74         [OuterLoop]
75         [MemberData(nameof(ConcatData), new int[] { /* Sources.OuterLoopCount */ })]
Concat_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)76         public static void Concat_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)
77         {
78             Concat(left, leftCount, right, rightCount);
79         }
80 
81         [Theory]
82         [MemberData(nameof(ConcatUnorderedData), new[] { 0, 1, 2, 16 })]
Concat_Unordered_NotPipelined(int leftCount, int rightCount)83         public static void Concat_Unordered_NotPipelined(int leftCount, int rightCount)
84         {
85             IntegerRangeSet seen = new IntegerRangeSet(0, leftCount + rightCount);
86             Assert.All(UnorderedSources.Default(leftCount).Concat(UnorderedSources.Default(leftCount, rightCount)).ToList(), x => seen.Add(x));
87             seen.AssertComplete();
88         }
89 
90         [Fact]
91         [OuterLoop]
Concat_Unordered_NotPipelined_Longrunning()92         public static void Concat_Unordered_NotPipelined_Longrunning()
93         {
94             Concat_Unordered_NotPipelined(Sources.OuterLoopCount, Sources.OuterLoopCount);
95         }
96 
97         [Theory]
98         [MemberData(nameof(ConcatData), new[] { 0, 1, 2, 16 })]
Concat_NotPipelined(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)99         public static void Concat_NotPipelined(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)
100         {
101             // The ordering of Concat is only guaranteed when both operands are ordered,
102             // however the current implementation manages to perform ordering if either operand is ordered _in most cases_.
103             // If this test starts failing, consider revising the operators and mention the change in release notes.
104             ParallelQuery<int> leftQuery = left.Item;
105             ParallelQuery<int> rightQuery = right.Item;
106             int seen = 0;
107             Assert.All(leftQuery.Concat(rightQuery).ToList(), x => Assert.Equal(seen++, x));
108             Assert.Equal(seen, leftCount + rightCount);
109         }
110 
111         [Theory]
112         [OuterLoop]
113         [MemberData(nameof(ConcatData), new int[] { /* Sources.OuterLoopCount */ })]
Concat_NotPipelined_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)114         public static void Concat_NotPipelined_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)
115         {
116             Concat_NotPipelined(left, leftCount, right, rightCount);
117         }
118 
119         [Fact]
Concat_NotSupportedException()120         public static void Concat_NotSupportedException()
121         {
122 #pragma warning disable 618
123             Assert.Throws<NotSupportedException>(() => ParallelEnumerable.Range(0, 1).Concat(Enumerable.Range(0, 1)));
124 #pragma warning restore 618
125         }
126 
127         [Fact]
128         // Should not get the same setting from both operands.
Concat_NoDuplicateSettings()129         public static void Concat_NoDuplicateSettings()
130         {
131             CancellationToken t = new CancellationTokenSource().Token;
132             Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithCancellation(t).Concat(ParallelEnumerable.Range(0, 1).WithCancellation(t)));
133             Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithDegreeOfParallelism(1).Concat(ParallelEnumerable.Range(0, 1).WithDegreeOfParallelism(1)));
134             Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithExecutionMode(ParallelExecutionMode.Default).Concat(ParallelEnumerable.Range(0, 1).WithExecutionMode(ParallelExecutionMode.Default)));
135             Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithMergeOptions(ParallelMergeOptions.Default).Concat(ParallelEnumerable.Range(0, 1).WithMergeOptions(ParallelMergeOptions.Default)));
136         }
137 
138         [Fact]
Concat_ArgumentNullException()139         public static void Concat_ArgumentNullException()
140         {
141             AssertExtensions.Throws<ArgumentNullException>("first", () => ((ParallelQuery<int>)null).Concat(ParallelEnumerable.Range(0, 1)));
142             AssertExtensions.Throws<ArgumentNullException>("second", () => ParallelEnumerable.Range(0, 1).Concat(null));
143         }
144 
145         [Fact]
Concat_UnionSources_PrematureMerges()146         public static void Concat_UnionSources_PrematureMerges()
147         {
148             const int ElementCount = 2048;
149             ParallelQuery<int> leftQuery = ParallelEnumerable.Range(0, ElementCount / 4).Union(ParallelEnumerable.Range(ElementCount / 4, ElementCount / 4));
150             ParallelQuery<int> rightQuery = ParallelEnumerable.Range(2 * ElementCount / 4, ElementCount / 4).Union(ParallelEnumerable.Range(3 * ElementCount / 4, ElementCount / 4));
151 
152             var results = new HashSet<int>(leftQuery.Concat(rightQuery));
153             Assert.Equal(ElementCount, results.Count);
154         }
155     }
156 }
157