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 GroupJoinTests 12 { 13 private const int KeyFactor = 8; 14 private const int ElementFactor = 4; 15 GroupJoinUnorderedData(int[] counts)16 public static IEnumerable<object[]> GroupJoinUnorderedData(int[] counts) 17 { 18 foreach (int leftCount in counts) 19 { 20 foreach (int rightCount in counts) 21 { 22 yield return new object[] { leftCount, rightCount }; 23 } 24 } 25 } 26 GroupJoinData(int[] counts)27 public static IEnumerable<object[]> GroupJoinData(int[] counts) 28 { 29 counts = counts.DefaultIfEmpty(Sources.OuterLoopCount / 64).ToArray(); 30 // When dealing with joins, if there aren't multiple matches the ordering of the second operand is immaterial. 31 foreach (object[] parms in Sources.Ranges(counts, i => counts)) 32 { 33 yield return parms; 34 } 35 } 36 GroupJoinMultipleData(int[] counts)37 public static IEnumerable<object[]> GroupJoinMultipleData(int[] counts) 38 { 39 counts = counts.DefaultIfEmpty(Sources.OuterLoopCount / 64).ToArray(); 40 foreach (object[] parms in UnorderedSources.BinaryRanges(counts, counts)) 41 { 42 yield return new object[] { ((Labeled<ParallelQuery<int>>)parms[0]).Order(), parms[1], ((Labeled<ParallelQuery<int>>)parms[2]).Order(), parms[3] }; 43 } 44 } 45 46 // 47 // GroupJoin 48 // 49 [Theory] 50 [MemberData(nameof(GroupJoinUnorderedData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin_Unordered(int leftCount, int rightCount)51 public static void GroupJoin_Unordered(int leftCount, int rightCount) 52 { 53 ParallelQuery<int> leftQuery = UnorderedSources.Default(leftCount); 54 ParallelQuery<int> rightQuery = UnorderedSources.Default(rightCount); 55 IntegerRangeSet seen = new IntegerRangeSet(0, leftCount); 56 foreach (var p in leftQuery.GroupJoin(rightQuery, x => x * KeyFactor, y => y, (x, y) => KeyValuePair.Create(x, y))) 57 { 58 seen.Add(p.Key); 59 if (p.Key < (rightCount + (KeyFactor - 1)) / KeyFactor) 60 { 61 Assert.Equal(p.Key * KeyFactor, Assert.Single(p.Value)); 62 } 63 else 64 { 65 Assert.Empty(p.Value); 66 } 67 } 68 seen.AssertComplete(); 69 } 70 71 [Fact] 72 [OuterLoop] GroupJoin_Unordered_Longrunning()73 public static void GroupJoin_Unordered_Longrunning() 74 { 75 GroupJoin_Unordered(Sources.OuterLoopCount / 64, Sources.OuterLoopCount / 64); 76 } 77 78 [Theory] 79 [MemberData(nameof(GroupJoinData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount)80 public static void GroupJoin(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount) 81 { 82 ParallelQuery<int> leftQuery = left.Item; 83 int seen = 0; 84 foreach (var p in leftQuery.GroupJoin(UnorderedSources.Default(rightCount), x => x * KeyFactor, y => y, (x, y) => KeyValuePair.Create(x, y))) 85 { 86 Assert.Equal(seen++, p.Key); 87 if (p.Key < (rightCount + (KeyFactor - 1)) / KeyFactor) 88 { 89 Assert.Equal(p.Key * KeyFactor, Assert.Single(p.Value)); 90 } 91 else 92 { 93 Assert.Empty(p.Value); 94 } 95 } 96 Assert.Equal(leftCount, seen); 97 } 98 99 [Theory] 100 [OuterLoop] 101 [MemberData(nameof(GroupJoinData), new int[] { /* Sources.OuterLoopCount */ })] GroupJoin_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount)102 public static void GroupJoin_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount) 103 { 104 GroupJoin(left, leftCount, rightCount); 105 } 106 107 [Theory] 108 [MemberData(nameof(GroupJoinUnorderedData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin_Unordered_NotPipelined(int leftCount, int rightCount)109 public static void GroupJoin_Unordered_NotPipelined(int leftCount, int rightCount) 110 { 111 ParallelQuery<int> leftQuery = UnorderedSources.Default(leftCount); 112 ParallelQuery<int> rightQuery = UnorderedSources.Default(rightCount); 113 IntegerRangeSet seen = new IntegerRangeSet(0, leftCount); 114 Assert.All(leftQuery.GroupJoin(rightQuery, x => x * KeyFactor, y => y, (x, y) => KeyValuePair.Create(x, y)).ToList(), 115 p => 116 { 117 seen.Add(p.Key); 118 if (p.Key < (rightCount + (KeyFactor - 1)) / KeyFactor) 119 { 120 Assert.Equal(p.Key * KeyFactor, Assert.Single(p.Value)); 121 } 122 else 123 { 124 Assert.Empty(p.Value); 125 } 126 }); 127 seen.AssertComplete(); 128 } 129 130 [Fact] 131 [OuterLoop] GroupJoin_Unordered_NotPipelined_Longrunning()132 public static void GroupJoin_Unordered_NotPipelined_Longrunning() 133 { 134 GroupJoin_Unordered_NotPipelined(Sources.OuterLoopCount / 64, Sources.OuterLoopCount / 64); 135 } 136 137 [Theory] 138 [MemberData(nameof(GroupJoinData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin_NotPipelined(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount)139 public static void GroupJoin_NotPipelined(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount) 140 { 141 ParallelQuery<int> leftQuery = left.Item; 142 int seen = 0; 143 Assert.All(leftQuery.GroupJoin(UnorderedSources.Default(rightCount), x => x * KeyFactor, y => y, (x, y) => KeyValuePair.Create(x, y)).ToList(), 144 p => 145 { 146 Assert.Equal(seen++, p.Key); 147 if (p.Key < (rightCount + (KeyFactor - 1)) / KeyFactor) 148 { 149 Assert.Equal(p.Key * KeyFactor, Assert.Single(p.Value)); 150 } 151 else 152 { 153 Assert.Empty(p.Value); 154 } 155 }); 156 Assert.Equal(leftCount, seen); 157 } 158 159 [Theory] 160 [OuterLoop] 161 [MemberData(nameof(GroupJoinData), new int[] { /* Sources.OuterLoopCount */ })] GroupJoin_NotPipelined_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount)162 public static void GroupJoin_NotPipelined_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, int rightCount) 163 { 164 GroupJoin_NotPipelined(left, leftCount, rightCount); 165 } 166 167 [Theory] 168 [MemberData(nameof(GroupJoinUnorderedData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin_Unordered_Multiple(int leftCount, int rightCount)169 public static void GroupJoin_Unordered_Multiple(int leftCount, int rightCount) 170 { 171 ParallelQuery<int> leftQuery = UnorderedSources.Default(leftCount); 172 ParallelQuery<int> rightQuery = UnorderedSources.Default(rightCount); 173 IntegerRangeSet seenOuter = new IntegerRangeSet(0, leftCount); 174 Assert.All(leftQuery.GroupJoin(rightQuery, x => x, y => y / KeyFactor, (x, y) => KeyValuePair.Create(x, y)), 175 p => 176 { 177 seenOuter.Add(p.Key); 178 if (p.Key < (rightCount + (KeyFactor - 1)) / KeyFactor) 179 { 180 IntegerRangeSet seenInner = new IntegerRangeSet(p.Key * KeyFactor, Math.Min(rightCount - p.Key * KeyFactor, KeyFactor)); 181 Assert.All(p.Value, y => { Assert.Equal(p.Key, y / KeyFactor); seenInner.Add(y); }); 182 seenInner.AssertComplete(); 183 } 184 else 185 { 186 Assert.Empty(p.Value); 187 } 188 }); 189 seenOuter.AssertComplete(); 190 } 191 192 [Fact] 193 [OuterLoop] GroupJoin_Unordered_Multiple_Longrunning()194 public static void GroupJoin_Unordered_Multiple_Longrunning() 195 { 196 GroupJoin_Unordered_Multiple(Sources.OuterLoopCount / 64, Sources.OuterLoopCount / 64); 197 } 198 199 [Theory] 200 [ActiveIssue(1155)] 201 [MemberData(nameof(GroupJoinMultipleData), new[] { 0, 1, 2, KeyFactor * 2 - 1, KeyFactor * 2 })] GroupJoin_Multiple(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)202 public static void GroupJoin_Multiple(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount) 203 { 204 ParallelQuery<int> leftQuery = left.Item; 205 ParallelQuery<int> rightQuery = right.Item; 206 int seenOuter = 0; 207 Assert.All(leftQuery.GroupJoin(rightQuery, x => x, y => y / KeyFactor, (x, y) => KeyValuePair.Create(x, y)), 208 p => 209 { 210 Assert.Equal(seenOuter++, p.Key); 211 if (p.Key < (rightCount + (KeyFactor - 1)) / KeyFactor) 212 { 213 int seenInner = p.Key * KeyFactor; 214 Assert.All(p.Value, y => 215 { 216 Assert.Equal(p.Key, y / KeyFactor); 217 Assert.Equal(seenInner++, y); 218 }); 219 Assert.Equal(Math.Min((p.Key + 1) * KeyFactor, rightCount), seenInner); 220 } 221 else 222 { 223 Assert.Empty(p.Value); 224 } 225 }); 226 Assert.Equal(leftCount, seenOuter); 227 } 228 229 [Theory] 230 [ActiveIssue(1155)] 231 [OuterLoop] 232 [MemberData(nameof(GroupJoinMultipleData), new int[] { /* Sources.OuterLoopCount */ })] GroupJoin_Multiple_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)233 public static void GroupJoin_Multiple_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount) 234 { 235 GroupJoin_Multiple(left, leftCount, right, rightCount); 236 } 237 238 [Theory] 239 [MemberData(nameof(GroupJoinUnorderedData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin_Unordered_CustomComparator(int leftCount, int rightCount)240 public static void GroupJoin_Unordered_CustomComparator(int leftCount, int rightCount) 241 { 242 ParallelQuery<int> leftQuery = UnorderedSources.Default(leftCount); 243 ParallelQuery<int> rightQuery = UnorderedSources.Default(rightCount); 244 IntegerRangeSet seenOuter = new IntegerRangeSet(0, leftCount); 245 Assert.All(leftQuery.GroupJoin(rightQuery, x => x, y => y % ElementFactor, (x, y) => KeyValuePair.Create(x, y), new ModularCongruenceComparer(KeyFactor)), 246 p => 247 { 248 seenOuter.Add(p.Key); 249 if (p.Key % KeyFactor < Math.Min(ElementFactor, rightCount)) 250 { 251 IntegerRangeSet seenInner = new IntegerRangeSet(0, (rightCount + (ElementFactor - 1) - p.Key % ElementFactor) / ElementFactor); 252 Assert.All(p.Value, y => { Assert.Equal(p.Key % KeyFactor, y % ElementFactor); seenInner.Add(y / ElementFactor); }); 253 seenInner.AssertComplete(); 254 } 255 else 256 { 257 Assert.Empty(p.Value); 258 } 259 }); 260 seenOuter.AssertComplete(); 261 } 262 263 [Fact] 264 [OuterLoop] GroupJoin_Unordered_CustomComparator_Longrunning()265 public static void GroupJoin_Unordered_CustomComparator_Longrunning() 266 { 267 GroupJoin_Unordered_CustomComparator(Sources.OuterLoopCount / 64, Sources.OuterLoopCount / 64); 268 } 269 270 [Theory] 271 [ActiveIssue(1155)] 272 [MemberData(nameof(GroupJoinMultipleData), new[] { 0, 1, 2, KeyFactor * 2 })] GroupJoin_CustomComparator(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)273 public static void GroupJoin_CustomComparator(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount) 274 { 275 ParallelQuery<int> leftQuery = left.Item; 276 ParallelQuery<int> rightQuery = right.Item; 277 int seenOuter = 0; 278 Assert.All(leftQuery.GroupJoin(rightQuery, x => x, y => y % ElementFactor, (x, y) => KeyValuePair.Create(x, y), new ModularCongruenceComparer(KeyFactor)), 279 p => 280 { 281 Assert.Equal(seenOuter++, p.Key); 282 if (p.Key % KeyFactor < Math.Min(ElementFactor, rightCount)) 283 { 284 int seenInner = p.Key % (KeyFactor / 2) - (KeyFactor / 2); 285 Assert.All(p.Value, y => 286 { 287 Assert.Equal(p.Key % KeyFactor, y % (KeyFactor / 2)); 288 Assert.Equal(seenInner += (KeyFactor / 2), y); 289 }); 290 Assert.Equal(Math.Max(p.Key % (KeyFactor / 2), rightCount + (p.Key % (KeyFactor / 2) - (KeyFactor / 2))), seenInner); 291 } 292 else 293 { 294 Assert.Empty(p.Value); 295 } 296 }); 297 Assert.Equal(leftCount, seenOuter); 298 } 299 300 [Theory] 301 [ActiveIssue(1155)] 302 [OuterLoop] 303 [MemberData(nameof(GroupJoinMultipleData), new int[] { /* Sources.OuterLoopCount */ })] GroupJoin_CustomComparator_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount)304 public static void GroupJoin_CustomComparator_Longrunning(Labeled<ParallelQuery<int>> left, int leftCount, Labeled<ParallelQuery<int>> right, int rightCount) 305 { 306 GroupJoin_CustomComparator(left, leftCount, right, rightCount); 307 } 308 309 [Fact] GroupJoin_NotSupportedException()310 public static void GroupJoin_NotSupportedException() 311 { 312 #pragma warning disable 618 313 Assert.Throws<NotSupportedException>(() => ParallelEnumerable.Range(0, 1).GroupJoin(Enumerable.Range(0, 1), i => i, i => i, (i, j) => i)); 314 Assert.Throws<NotSupportedException>(() => ParallelEnumerable.Range(0, 1).GroupJoin(Enumerable.Range(0, 1), i => i, i => i, (i, j) => i, null)); 315 #pragma warning restore 618 316 } 317 318 [Fact] 319 // Should not get the same setting from both operands. GroupJoin_NoDuplicateSettings()320 public static void GroupJoin_NoDuplicateSettings() 321 { 322 CancellationToken t = new CancellationTokenSource().Token; 323 Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithCancellation(t).GroupJoin(ParallelEnumerable.Range(0, 1).WithCancellation(t), x => x, y => y, (x, e) => e)); 324 Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithDegreeOfParallelism(1).GroupJoin(ParallelEnumerable.Range(0, 1).WithDegreeOfParallelism(1), x => x, y => y, (x, e) => e)); 325 Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithExecutionMode(ParallelExecutionMode.Default).GroupJoin(ParallelEnumerable.Range(0, 1).WithExecutionMode(ParallelExecutionMode.Default), x => x, y => y, (x, e) => e)); 326 Assert.Throws<InvalidOperationException>(() => ParallelEnumerable.Range(0, 1).WithMergeOptions(ParallelMergeOptions.Default).GroupJoin(ParallelEnumerable.Range(0, 1).WithMergeOptions(ParallelMergeOptions.Default), x => x, y => y, (x, e) => e)); 327 } 328 329 [Fact] GroupJoin_ArgumentNullException()330 public static void GroupJoin_ArgumentNullException() 331 { 332 AssertExtensions.Throws<ArgumentNullException>("outer", () => ((ParallelQuery<int>)null).GroupJoin(ParallelEnumerable.Range(0, 1), i => i, i => i, (i, j) => i)); 333 AssertExtensions.Throws<ArgumentNullException>("inner", () => ParallelEnumerable.Range(0, 1).GroupJoin((ParallelQuery<int>)null, i => i, i => i, (i, j) => i)); 334 AssertExtensions.Throws<ArgumentNullException>("outerKeySelector", () => ParallelEnumerable.Range(0, 1).GroupJoin(ParallelEnumerable.Range(0, 1), (Func<int, int>)null, i => i, (i, j) => i)); 335 AssertExtensions.Throws<ArgumentNullException>("innerKeySelector", () => ParallelEnumerable.Range(0, 1).GroupJoin(ParallelEnumerable.Range(0, 1), i => i, (Func<int, int>)null, (i, j) => i)); 336 AssertExtensions.Throws<ArgumentNullException>("resultSelector", () => ParallelEnumerable.Range(0, 1).GroupJoin(ParallelEnumerable.Range(0, 1), i => i, i => i, (Func<int, IEnumerable<int>, int>)null)); 337 AssertExtensions.Throws<ArgumentNullException>("outer", () => ((ParallelQuery<int>)null).GroupJoin(ParallelEnumerable.Range(0, 1), i => i, i => i, (i, j) => i, EqualityComparer<int>.Default)); 338 AssertExtensions.Throws<ArgumentNullException>("inner", () => ParallelEnumerable.Range(0, 1).GroupJoin((ParallelQuery<int>)null, i => i, i => i, (i, j) => i, EqualityComparer<int>.Default)); 339 AssertExtensions.Throws<ArgumentNullException>("outerKeySelector", () => ParallelEnumerable.Range(0, 1).GroupJoin(ParallelEnumerable.Range(0, 1), (Func<int, int>)null, i => i, (i, j) => i, EqualityComparer<int>.Default)); 340 AssertExtensions.Throws<ArgumentNullException>("innerKeySelector", () => ParallelEnumerable.Range(0, 1).GroupJoin(ParallelEnumerable.Range(0, 1), i => i, (Func<int, int>)null, (i, j) => i, EqualityComparer<int>.Default)); 341 AssertExtensions.Throws<ArgumentNullException>("resultSelector", () => ParallelEnumerable.Range(0, 1).GroupJoin(ParallelEnumerable.Range(0, 1), i => i, i => i, (Func<int, IEnumerable<int>, int>)null, EqualityComparer<int>.Default)); 342 } 343 } 344 } 345