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