From c9aa710f68c43c0e86b7efa14e5aa8d28b0cc2de Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 14 Nov 2019 09:00:00 +0100 Subject: [PATCH 001/157] Add TestInterleaveDoNoCallMoveNextEagerly. And it does. --- MoreLinq.Test/InterleaveTest.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 598c3bd05..627dd0b7d 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -35,6 +35,23 @@ public void TestInterleaveIsLazy() new BreakingSequence().Interleave(new BreakingSequence()); } + /// + /// Verify that interleaving do not call enumerators MoveNext method eagerly + /// + [Test] + public void TestInterleaveDoNoCallMoveNextEagerly() + { + void Code() + { + var sequenceA = Enumerable.Range(1, 1); + var sequenceB = MoreEnumerable.From(() => throw new TestException()); + + sequenceA.Interleave(sequenceB).Take(1).Consume(); + } + + Assert.DoesNotThrow(Code); + } + /// /// Verify that interleaving disposes those enumerators that it managed /// to open successfully From 66d1228bf30b3597a87f403fda247b4027d74e6d Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 14 Nov 2019 09:54:09 +0100 Subject: [PATCH 002/157] Fix interleave implementation so it doen't call MoveNext eagerly. --- MoreLinq/Interleave.cs | 150 ++++++++++++----------------------------- 1 file changed, 42 insertions(+), 108 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index cd19e19a6..0c29c8bfc 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -19,8 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; public static partial class MoreEnumerable { @@ -45,127 +43,63 @@ public static partial class MoreEnumerable /// A sequence of interleaved elements from all of the source sequences public static IEnumerable Interleave(this IEnumerable sequence, params IEnumerable[] otherSequences) - { - return Interleave(sequence, ImbalancedInterleaveStrategy.Skip, otherSequences); - } - - /// - /// Interleaves the elements of two or more sequences into a single sequence, applying the specified strategy when sequences are of unequal length - /// - /// - /// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed - /// by the second, then the third, and so on. So, for example:
- /// { 1,2,3,1,2,3,1,2,3 } - /// ]]> - /// This operator behaves in a deferred and streaming manner.
- /// When sequences are of unequal length, this method will use the imbalance strategy specified to - /// decide how to continue interleaving the remaining sequences. See - /// for more information.
- /// The sequences are interleaved in the order that they appear in the - /// collection, with as the first sequence. - ///
- /// The type of the elements of the source sequences - /// The first sequence in the interleave group - /// Defines the behavior of the operator when sequences are of unequal length - /// The other sequences in the interleave group - /// A sequence of interleaved elements from all of the source sequences - - static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInterleaveStrategy imbalanceStrategy, params IEnumerable[] otherSequences) { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); - if (otherSequences.Any(s => s == null)) - throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); - return _(); IEnumerable _() - { - var sequences = new[] { sequence }.Concat(otherSequences); + return InterleaveSkip(otherSequences.Prepend(sequence)); + } - // produce an iterator collection for all IEnumerable instancess passed to us - var iterators = sequences.Select(e => e.GetEnumerator()).Acquire(); - List> iteratorList = null; + private static IEnumerable InterleaveSkip(this IEnumerable> sequences) + { + var enumerators = new LinkedList>(); - try + try + { + // First pass. create enumerators. + foreach (var sequence in sequences) { - iteratorList = new List>(iterators); - iterators = null; - var shouldContinue = true; - var consumedIterators = 0; - var iterCount = iteratorList.Count; - - while (shouldContinue) - { - // advance every iterator and verify a value exists to be yielded - for (var index = 0; index < iterCount; index++) - { - if (!iteratorList[index].MoveNext()) - { - // check if all iterators have been consumed and we can terminate - // or if the imbalance strategy informs us that we MUST terminate - if (++consumedIterators == iterCount || imbalanceStrategy == ImbalancedInterleaveStrategy.Stop) - { - shouldContinue = false; - break; - } + if (sequence == null) + throw new ArgumentException("An item is null.", nameof(sequences)); - iteratorList[index].Dispose(); // dispose the iterator sice we no longer need it + var enumerator = sequence.GetEnumerator(); - // otherwise, apply the imbalance strategy - switch (imbalanceStrategy) - { - case ImbalancedInterleaveStrategy.Pad: - var newIter = iteratorList[index] = Generate(default(T), x => default).GetEnumerator(); - newIter.MoveNext(); - break; + // Immediately dispose enumerators of empty sequences. + if (!enumerator.MoveNext()) + { + enumerator.Dispose(); + continue; + } - case ImbalancedInterleaveStrategy.Skip: - iteratorList.RemoveAt(index); // no longer visit this particular iterator - --iterCount; // reduce the expected number of iterators to visit - --index; // decrement iterator index to compensate for index shifting - --consumedIterators; // decrement consumer iterator count to stay in balance - break; - } + yield return enumerator.Current; + enumerators.AddLast(enumerator); + } - } - } + var node = enumerators.First; + while (node != null) + { + var nextNode = node.Next; - if (shouldContinue) // only if all iterators could be advanced - { - // yield the values of each iterator's current position - for (var index = 0; index < iterCount; index++) - { - yield return iteratorList[index].Current; - } - } + var enumerator = node.Value; + if (enumerator.MoveNext()) + { + yield return enumerator.Current; } - } - finally - { - Debug.Assert(iteratorList != null || iterators != null); - foreach (var iter in (iteratorList ?? (IList>) iterators)) - iter.Dispose(); + else + { + enumerator.Dispose(); + enumerators.Remove(node); + } + + // Work on next node or restart from first one. + node = nextNode ?? enumerators.First; } } - } - - /// - /// Defines the strategies available when Interleave is passed sequences of unequal length - /// - enum ImbalancedInterleaveStrategy - { - /// - /// Extends a sequence by padding its tail with default(T) - /// - Pad, - /// - /// Removes the sequence from the interleave set, and continues interleaving remaining sequences. - /// - Skip, - /// - /// Stops the interleave operation. - /// - Stop, + finally + { + foreach (var enumerator in enumerators) + enumerator.Dispose(); + } } } } From 336423acff063453da3196dd6357fe06376c3f8c Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 14 Nov 2019 10:30:08 +0100 Subject: [PATCH 003/157] Update MoreLinq/Interleave.cs Remove useless extension method signature on a private method --- MoreLinq/Interleave.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 0c29c8bfc..80cfbf4f8 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -50,7 +50,7 @@ public static IEnumerable Interleave(this IEnumerable sequence, params return InterleaveSkip(otherSequences.Prepend(sequence)); } - private static IEnumerable InterleaveSkip(this IEnumerable> sequences) + private static IEnumerable InterleaveSkip(IEnumerable> sequences) { var enumerators = new LinkedList>(); From 61e49217d74020fbd57a4ae63747fc884e1d1c80 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 22 Nov 2019 11:22:27 +0100 Subject: [PATCH 004/157] Fix the test --- MoreLinq.Test/InterleaveTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 627dd0b7d..d981979b5 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -44,7 +44,7 @@ public void TestInterleaveDoNoCallMoveNextEagerly() void Code() { var sequenceA = Enumerable.Range(1, 1); - var sequenceB = MoreEnumerable.From(() => throw new TestException()); + var sequenceB = MoreEnumerable.From(() => 2, () => throw new TestException()); sequenceA.Interleave(sequenceB).Take(1).Consume(); } From b790e53103e24ea12a124efaa016ce7a9fa63f44 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 22 Nov 2019 11:23:59 +0100 Subject: [PATCH 005/157] Undo implementation to prove test isn't bokren --- MoreLinq/Interleave.cs | 150 +++++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 42 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 80cfbf4f8..cd19e19a6 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -19,6 +19,8 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; public static partial class MoreEnumerable { @@ -44,62 +46,126 @@ public static partial class MoreEnumerable public static IEnumerable Interleave(this IEnumerable sequence, params IEnumerable[] otherSequences) { - if (sequence == null) throw new ArgumentNullException(nameof(sequence)); - if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); - - return InterleaveSkip(otherSequences.Prepend(sequence)); + return Interleave(sequence, ImbalancedInterleaveStrategy.Skip, otherSequences); } - private static IEnumerable InterleaveSkip(IEnumerable> sequences) + /// + /// Interleaves the elements of two or more sequences into a single sequence, applying the specified strategy when sequences are of unequal length + /// + /// + /// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed + /// by the second, then the third, and so on. So, for example:
+ /// { 1,2,3,1,2,3,1,2,3 } + /// ]]> + /// This operator behaves in a deferred and streaming manner.
+ /// When sequences are of unequal length, this method will use the imbalance strategy specified to + /// decide how to continue interleaving the remaining sequences. See + /// for more information.
+ /// The sequences are interleaved in the order that they appear in the + /// collection, with as the first sequence. + ///
+ /// The type of the elements of the source sequences + /// The first sequence in the interleave group + /// Defines the behavior of the operator when sequences are of unequal length + /// The other sequences in the interleave group + /// A sequence of interleaved elements from all of the source sequences + + static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInterleaveStrategy imbalanceStrategy, params IEnumerable[] otherSequences) { - var enumerators = new LinkedList>(); + if (sequence == null) throw new ArgumentNullException(nameof(sequence)); + if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); + if (otherSequences.Any(s => s == null)) + throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); - try + return _(); IEnumerable _() { - // First pass. create enumerators. - foreach (var sequence in sequences) - { - if (sequence == null) - throw new ArgumentException("An item is null.", nameof(sequences)); + var sequences = new[] { sequence }.Concat(otherSequences); + + // produce an iterator collection for all IEnumerable instancess passed to us + var iterators = sequences.Select(e => e.GetEnumerator()).Acquire(); + List> iteratorList = null; - var enumerator = sequence.GetEnumerator(); + try + { + iteratorList = new List>(iterators); + iterators = null; + var shouldContinue = true; + var consumedIterators = 0; + var iterCount = iteratorList.Count; - // Immediately dispose enumerators of empty sequences. - if (!enumerator.MoveNext()) + while (shouldContinue) { - enumerator.Dispose(); - continue; - } + // advance every iterator and verify a value exists to be yielded + for (var index = 0; index < iterCount; index++) + { + if (!iteratorList[index].MoveNext()) + { + // check if all iterators have been consumed and we can terminate + // or if the imbalance strategy informs us that we MUST terminate + if (++consumedIterators == iterCount || imbalanceStrategy == ImbalancedInterleaveStrategy.Stop) + { + shouldContinue = false; + break; + } - yield return enumerator.Current; - enumerators.AddLast(enumerator); - } + iteratorList[index].Dispose(); // dispose the iterator sice we no longer need it - var node = enumerators.First; - while (node != null) - { - var nextNode = node.Next; + // otherwise, apply the imbalance strategy + switch (imbalanceStrategy) + { + case ImbalancedInterleaveStrategy.Pad: + var newIter = iteratorList[index] = Generate(default(T), x => default).GetEnumerator(); + newIter.MoveNext(); + break; - var enumerator = node.Value; - if (enumerator.MoveNext()) - { - yield return enumerator.Current; - } - else - { - enumerator.Dispose(); - enumerators.Remove(node); - } + case ImbalancedInterleaveStrategy.Skip: + iteratorList.RemoveAt(index); // no longer visit this particular iterator + --iterCount; // reduce the expected number of iterators to visit + --index; // decrement iterator index to compensate for index shifting + --consumedIterators; // decrement consumer iterator count to stay in balance + break; + } + + } + } - // Work on next node or restart from first one. - node = nextNode ?? enumerators.First; + if (shouldContinue) // only if all iterators could be advanced + { + // yield the values of each iterator's current position + for (var index = 0; index < iterCount; index++) + { + yield return iteratorList[index].Current; + } + } + } + } + finally + { + Debug.Assert(iteratorList != null || iterators != null); + foreach (var iter in (iteratorList ?? (IList>) iterators)) + iter.Dispose(); } } - finally - { - foreach (var enumerator in enumerators) - enumerator.Dispose(); - } + } + + /// + /// Defines the strategies available when Interleave is passed sequences of unequal length + /// + enum ImbalancedInterleaveStrategy + { + /// + /// Extends a sequence by padding its tail with default(T) + /// + Pad, + /// + /// Removes the sequence from the interleave set, and continues interleaving remaining sequences. + /// + Skip, + /// + /// Stops the interleave operation. + /// + Stop, } } } From 32f02e49ba4dd2a20fd85995f1c97c682089e590 Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 14 Nov 2019 09:00:00 +0100 Subject: [PATCH 006/157] Add TestInterleaveDoNoCallMoveNextEagerly. And it does. --- MoreLinq.Test/InterleaveTest.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 598c3bd05..627dd0b7d 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -35,6 +35,23 @@ public void TestInterleaveIsLazy() new BreakingSequence().Interleave(new BreakingSequence()); } + /// + /// Verify that interleaving do not call enumerators MoveNext method eagerly + /// + [Test] + public void TestInterleaveDoNoCallMoveNextEagerly() + { + void Code() + { + var sequenceA = Enumerable.Range(1, 1); + var sequenceB = MoreEnumerable.From(() => throw new TestException()); + + sequenceA.Interleave(sequenceB).Take(1).Consume(); + } + + Assert.DoesNotThrow(Code); + } + /// /// Verify that interleaving disposes those enumerators that it managed /// to open successfully From 6ff6d03d2f2d78de49a1de17b83ae9beb302054e Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 14 Nov 2019 09:54:09 +0100 Subject: [PATCH 007/157] Fix interleave implementation so it doen't call MoveNext eagerly. --- MoreLinq/Interleave.cs | 148 ++++++++++++----------------------------- 1 file changed, 42 insertions(+), 106 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index c06a0129c..0c29c8bfc 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -19,8 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; public static partial class MoreEnumerable { @@ -45,125 +43,63 @@ public static partial class MoreEnumerable /// A sequence of interleaved elements from all of the source sequences public static IEnumerable Interleave(this IEnumerable sequence, params IEnumerable[] otherSequences) - { - return Interleave(sequence, ImbalancedInterleaveStrategy.Skip, otherSequences); - } - - /// - /// Interleaves the elements of two or more sequences into a single sequence, applying the specified strategy when sequences are of unequal length - /// - /// - /// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed - /// by the second, then the third, and so on. So, for example:
- /// { 1,2,3,1,2,3,1,2,3 } - /// ]]> - /// This operator behaves in a deferred and streaming manner.
- /// When sequences are of unequal length, this method will use the imbalance strategy specified to - /// decide how to continue interleaving the remaining sequences. See - /// for more information.
- /// The sequences are interleaved in the order that they appear in the - /// collection, with as the first sequence. - ///
- /// The type of the elements of the source sequences - /// The first sequence in the interleave group - /// Defines the behavior of the operator when sequences are of unequal length - /// The other sequences in the interleave group - /// A sequence of interleaved elements from all of the source sequences - - static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInterleaveStrategy imbalanceStrategy, params IEnumerable[] otherSequences) { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); - if (otherSequences.Any(s => s == null)) - throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); - return _(); IEnumerable _() - { - var sequences = new[] { sequence }.Concat(otherSequences); + return InterleaveSkip(otherSequences.Prepend(sequence)); + } - // produce an iterator collection for all IEnumerable instancess passed to us - var iterators = sequences.Select(e => e.GetEnumerator()).Acquire(); - List> iteratorList = null; + private static IEnumerable InterleaveSkip(this IEnumerable> sequences) + { + var enumerators = new LinkedList>(); - try + try + { + // First pass. create enumerators. + foreach (var sequence in sequences) { - iteratorList = new List>(iterators); - iterators = null; - var shouldContinue = true; - var consumedIterators = 0; - var iterCount = iteratorList.Count; - - while (shouldContinue) - { - // advance every iterator and verify a value exists to be yielded - for (var index = 0; index < iterCount; index++) - { - if (!iteratorList[index].MoveNext()) - { - // check if all iterators have been consumed and we can terminate - // or if the imbalance strategy informs us that we MUST terminate - if (++consumedIterators == iterCount || imbalanceStrategy == ImbalancedInterleaveStrategy.Stop) - { - shouldContinue = false; - break; - } + if (sequence == null) + throw new ArgumentException("An item is null.", nameof(sequences)); - iteratorList[index].Dispose(); // dispose the iterator sice we no longer need it + var enumerator = sequence.GetEnumerator(); - // otherwise, apply the imbalance strategy - switch (imbalanceStrategy) - { - case ImbalancedInterleaveStrategy.Pad: - var newIter = iteratorList[index] = Generate(default(T), x => default).GetEnumerator(); - newIter.MoveNext(); - break; + // Immediately dispose enumerators of empty sequences. + if (!enumerator.MoveNext()) + { + enumerator.Dispose(); + continue; + } - case ImbalancedInterleaveStrategy.Skip: - iteratorList.RemoveAt(index); // no longer visit this particular iterator - --iterCount; // reduce the expected number of iterators to visit - --index; // decrement iterator index to compensate for index shifting - --consumedIterators; // decrement consumer iterator count to stay in balance - break; - } + yield return enumerator.Current; + enumerators.AddLast(enumerator); + } - } - } + var node = enumerators.First; + while (node != null) + { + var nextNode = node.Next; - if (shouldContinue) // only if all iterators could be advanced - { - // yield the values of each iterator's current position - foreach (var iterator in iteratorList) - yield return iterator.Current; - } + var enumerator = node.Value; + if (enumerator.MoveNext()) + { + yield return enumerator.Current; } - } - finally - { - Debug.Assert(iteratorList != null || iterators != null); - foreach (var iter in (iteratorList ?? (IList>) iterators)) - iter.Dispose(); + else + { + enumerator.Dispose(); + enumerators.Remove(node); + } + + // Work on next node or restart from first one. + node = nextNode ?? enumerators.First; } } - } - - /// - /// Defines the strategies available when Interleave is passed sequences of unequal length - /// - enum ImbalancedInterleaveStrategy - { - /// - /// Extends a sequence by padding its tail with default(T) - /// - Pad, - /// - /// Removes the sequence from the interleave set, and continues interleaving remaining sequences. - /// - Skip, - /// - /// Stops the interleave operation. - /// - Stop, + finally + { + foreach (var enumerator in enumerators) + enumerator.Dispose(); + } } } } From 1a4270f99981e522ad14ac58232eba07582fba27 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 22 Nov 2019 14:50:56 +0100 Subject: [PATCH 008/157] Interleave: add early test of null elements in otherSequences --- MoreLinq/Interleave.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 0c29c8bfc..d62c25452 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Linq; public static partial class MoreEnumerable { @@ -46,6 +47,8 @@ public static IEnumerable Interleave(this IEnumerable sequence, params { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); + if (otherSequences.Any(s => s == null)) + throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); return InterleaveSkip(otherSequences.Prepend(sequence)); } @@ -59,20 +62,18 @@ private static IEnumerable InterleaveSkip(this IEnumerable> // First pass. create enumerators. foreach (var sequence in sequences) { - if (sequence == null) - throw new ArgumentException("An item is null.", nameof(sequences)); - var enumerator = sequence.GetEnumerator(); // Immediately dispose enumerators of empty sequences. - if (!enumerator.MoveNext()) + if (enumerator.MoveNext()) + { + yield return enumerator.Current; + enumerators.AddLast(enumerator); + } + else { enumerator.Dispose(); - continue; } - - yield return enumerator.Current; - enumerators.AddLast(enumerator); } var node = enumerators.First; From 76e3516f1c8c5289a7fa6d70a14249bbe314a352 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 22 Nov 2019 15:00:06 +0100 Subject: [PATCH 009/157] Added TestInterleaveEarlyThrowOnNullElementInOtherSequences --- MoreLinq.Test/InterleaveTest.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 627dd0b7d..d58fe77d5 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -18,6 +18,7 @@ namespace MoreLinq.Test { using System; + using System.Collections.Generic; using NUnit.Framework; /// @@ -35,6 +36,24 @@ public void TestInterleaveIsLazy() new BreakingSequence().Interleave(new BreakingSequence()); } + /// + /// Verify that Interleave early throw ArgumentNullException when an element + /// of otherSequences is null. + /// + [Test] + public void TestInterleaveEarlyThrowOnNullElementInOtherSequences() + { + void Code() + { + var sequenceA = Enumerable.Range(1, 1); + var otherSequences = new IEnumerable[] {null}; + + sequenceA.Interleave(otherSequences); + } + + Assert.Throws(Code); + } + /// /// Verify that interleaving do not call enumerators MoveNext method eagerly /// From 6f617609bcffd60fb5b1825f79eca5daf78fbcea Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 22 Nov 2019 15:37:09 +0100 Subject: [PATCH 010/157] Remove useless private extension method --- MoreLinq/Interleave.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index d62c25452..d5c57d845 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -53,7 +53,7 @@ public static IEnumerable Interleave(this IEnumerable sequence, params return InterleaveSkip(otherSequences.Prepend(sequence)); } - private static IEnumerable InterleaveSkip(this IEnumerable> sequences) + private static IEnumerable InterleaveSkip(IEnumerable> sequences) { var enumerators = new LinkedList>(); From 81479fb3c25091ada7ee4460fa6cb65226892d87 Mon Sep 17 00:00:00 2001 From: Orace Date: Mon, 25 Nov 2019 11:24:39 +0100 Subject: [PATCH 011/157] Added TestInterleaveDisposesOnPartialEnumeration --- MoreLinq.Test/InterleaveTest.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index d58fe77d5..5945ddf44 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -85,6 +85,24 @@ public void TestInterleaveDisposesOnError() } } + /// + /// Verify that, in case of partial enumeration, interleaving disposes those + /// enumerators that it managed to open successfully + /// + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + public void TestInterleaveDisposesOnPartialEnumeration(int count) + { + using var sequenceA = TestingSequence.Of(1); + using var sequenceB = TestingSequence.Of(2); + using var sequenceC = TestingSequence.Of(3); + + sequenceA.Interleave(sequenceB, sequenceC).Take(count).Consume(); + + } + /// /// Verify that two balanced sequences will interleave all of their elements /// From 00ef3661ce0d6ff4e7b9ec5f1f14563f438f9457 Mon Sep 17 00:00:00 2001 From: Orace Date: Mon, 25 Nov 2019 11:25:34 +0100 Subject: [PATCH 012/157] Made TestInterleaveDisposesOnPartialEnumeration pass --- MoreLinq/Interleave.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index d5c57d845..263fbc445 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -64,14 +64,14 @@ private static IEnumerable InterleaveSkip(IEnumerable> sequ { var enumerator = sequence.GetEnumerator(); - // Immediately dispose enumerators of empty sequences. if (enumerator.MoveNext()) { - yield return enumerator.Current; enumerators.AddLast(enumerator); + yield return enumerator.Current; } else { + // Immediately dispose enumerators of empty sequences. enumerator.Dispose(); } } From d70974b3997db04852657c8994eb131641de6edd Mon Sep 17 00:00:00 2001 From: Orace Date: Mon, 25 Nov 2019 11:44:50 +0100 Subject: [PATCH 013/157] Interleave: simple implementation using Acquire. --- MoreLinq/Interleave.cs | 70 +++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 263fbc445..acb50e748 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -50,56 +50,42 @@ public static IEnumerable Interleave(this IEnumerable sequence, params if (otherSequences.Any(s => s == null)) throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); - return InterleaveSkip(otherSequences.Prepend(sequence)); - } + return _(); IEnumerable _() + { + var sequences = new[] {sequence}.Concat(otherSequences); - private static IEnumerable InterleaveSkip(IEnumerable> sequences) - { - var enumerators = new LinkedList>(); + // produce an enumerators collection for all IEnumerable instances passed to us + var enumerators = sequences.Select(e => e.GetEnumerator()).Acquire(); - try - { - // First pass. create enumerators. - foreach (var sequence in sequences) + try { - var enumerator = sequence.GetEnumerator(); - - if (enumerator.MoveNext()) + var allNull = false; + while (!allNull) { - enumerators.AddLast(enumerator); - yield return enumerator.Current; - } - else - { - // Immediately dispose enumerators of empty sequences. - enumerator.Dispose(); - } - } + allNull = true; + for (var i = 0; i < enumerators.Length; i++) + { + var enumerator = enumerators[i]; + if (enumerator == null) + continue; - var node = enumerators.First; - while (node != null) - { - var nextNode = node.Next; + if (!enumerator.MoveNext()) + { + enumerator.Dispose(); + enumerators[i] = null; + continue; + } - var enumerator = node.Value; - if (enumerator.MoveNext()) - { - yield return enumerator.Current; + allNull = false; + yield return enumerator.Current; + } } - else - { - enumerator.Dispose(); - enumerators.Remove(node); - } - - // Work on next node or restart from first one. - node = nextNode ?? enumerators.First; } - } - finally - { - foreach (var enumerator in enumerators) - enumerator.Dispose(); + finally + { + foreach (var enumerator in enumerators) + enumerator?.Dispose(); + } } } } From 9be11addedd6dc7dbe80f88764e57b6b75569c8c Mon Sep 17 00:00:00 2001 From: Orace Date: Mon, 25 Nov 2019 11:50:16 +0100 Subject: [PATCH 014/157] remove trailing whitespaces --- MoreLinq.Test/InterleaveTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 5945ddf44..9e91c67d7 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -100,7 +100,6 @@ public void TestInterleaveDisposesOnPartialEnumeration(int count) using var sequenceC = TestingSequence.Of(3); sequenceA.Interleave(sequenceB, sequenceC).Take(count).Consume(); - } /// From 349a8e6e5fc1ce0eec2f4a741eb005492b1bceaf Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 13 Dec 2019 09:24:54 +0100 Subject: [PATCH 015/157] Prepare for 3.3.1 --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 3ee4f8020..aaa0380e4 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -117,7 +117,7 @@ $([System.Text.RegularExpressions.Regex]::Replace($(Copyright), `\s+`, ` `).Trim()) MoreLINQ en-US - 3.3.0 + 3.3.1 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 8 From b77df70598ab84c28cd43dcf74594024b6d575e1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 13 Dec 2019 09:40:17 +0100 Subject: [PATCH 016/157] Move TrySingle to Experimental namespace --- MoreLinq.Test/TrySingleTest.cs | 8 +-- MoreLinq/{ => Experimental}/TrySingle.cs | 4 +- MoreLinq/Extensions.g.cs | 82 ------------------------ MoreLinq/MoreEnumerable.cs | 2 +- README.md | 16 ++--- 5 files changed, 15 insertions(+), 97 deletions(-) rename MoreLinq/{ => Experimental}/TrySingle.cs (98%) diff --git a/MoreLinq.Test/TrySingleTest.cs b/MoreLinq.Test/TrySingleTest.cs index 675044c7e..4fe51a3b2 100644 --- a/MoreLinq.Test/TrySingleTest.cs +++ b/MoreLinq.Test/TrySingleTest.cs @@ -15,14 +15,14 @@ // limitations under the License. #endregion -using NUnit.Framework.Interfaces; - namespace MoreLinq.Test { using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework; + using NUnit.Framework.Interfaces; + using Experimental; [TestFixture] public class TrySingleTest @@ -81,7 +81,7 @@ class BreakingSingleElementCollectionBase : IEnumerable public IEnumerator GetEnumerator() { yield return _element; - throw new Exception($"{nameof(MoreEnumerable.TrySingle)} should not have attempted to consume a second element."); + throw new Exception($"{nameof(ExperimentalEnumerable.TrySingle)} should not have attempted to consume a second element."); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -128,7 +128,7 @@ static IEnumerable TestSequence() { yield return 1; yield return 2; - throw new Exception(nameof(MoreEnumerable.TrySingle) + " should not have attempted to consume a third element."); + throw new Exception(nameof(ExperimentalEnumerable.TrySingle) + " should not have attempted to consume a third element."); } var (cardinality, value) = TestSequence().TrySingle("zero", "one", "many"); diff --git a/MoreLinq/TrySingle.cs b/MoreLinq/Experimental/TrySingle.cs similarity index 98% rename from MoreLinq/TrySingle.cs rename to MoreLinq/Experimental/TrySingle.cs index 24bfe35df..6aa591164 100644 --- a/MoreLinq/TrySingle.cs +++ b/MoreLinq/Experimental/TrySingle.cs @@ -15,13 +15,13 @@ // limitations under the License. #endregion -namespace MoreLinq +namespace MoreLinq.Experimental { using System; using System.Collections.Generic; using System.Linq; - partial class MoreEnumerable + partial class ExperimentalEnumerable { /// /// Returns a tuple with the cardinality of the sequence and the diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 0b1211058..5e45fc65f 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -6588,88 +6588,6 @@ public static IEnumerable> Transpose(this IEnumerableTrySingle extension. - - [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] - public static partial class TrySingleExtension - { - /// - /// Returns a tuple with the cardinality of the sequence and the - /// single element in the sequence if it contains exactly one element. - /// similar to . - /// - /// The source sequence. - /// - /// The value that is returned in the tuple if the sequence has zero - /// elements. - /// - /// The value that is returned in the tuple if the sequence has a - /// single element only. - /// - /// The value that is returned in the tuple if the sequence has two or - /// more elements. - /// - /// The type of the elements of . - /// - /// The type that expresses cardinality. - /// - /// A tuple containing the identified - /// and either the single value of in the sequence - /// or its default value. - /// - /// This operator uses immediate execution, but never consumes more - /// than two elements from the sequence. - /// - - public static (TCardinality Cardinality, T Value) - TrySingle(this IEnumerable source, - TCardinality zero, TCardinality one, TCardinality many) => MoreEnumerable. TrySingle(source, zero, one, many); - - /// - /// Returns a result projected from the the cardinality of the sequence - /// and the single element in the sequence if it contains exactly one - /// element. - /// - /// The source sequence. - /// - /// The value that is passed as the first argument to - /// if the sequence has zero - /// elements. - /// - /// The value that is passed as the first argument to - /// if the sequence has a - /// single element only. - /// - /// The value that is passed as the first argument to - /// if the sequence has two or - /// more elements. - /// - /// A function that receives the cardinality and, if the - /// sequence has just one element, the value of that element as - /// argument and projects a resulting value of type - /// . - /// - /// The type of the elements of . - /// - /// The type that expresses cardinality. - /// - /// The type of the result value returned by the - /// function. - /// - /// The value returned by . - /// - /// - /// This operator uses immediate execution, but never consumes more - /// than two elements from the sequence. - /// - - public static TResult TrySingle(this IEnumerable source, - TCardinality zero, TCardinality one, TCardinality many, - Func resultSelector) - => MoreEnumerable.TrySingle(source, zero, one, many, resultSelector); - - } - /// Window extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] diff --git a/MoreLinq/MoreEnumerable.cs b/MoreLinq/MoreEnumerable.cs index 09831c851..118911f81 100644 --- a/MoreLinq/MoreEnumerable.cs +++ b/MoreLinq/MoreEnumerable.cs @@ -27,7 +27,7 @@ namespace MoreLinq public static partial class MoreEnumerable { - static int? TryGetCollectionCount(this IEnumerable source) + internal static int? TryGetCollectionCount(this IEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); diff --git a/README.md b/README.md index a218b1613..03631fdef 100644 --- a/README.md +++ b/README.md @@ -680,14 +680,6 @@ Traces the elements of a source sequence for diagnostics. This method has 3 overloads. -### TrySingle - -Returns the only element of a sequence that has just one element. If the -sequence has zero or multiple elements, then returns a user-defined value -that indicates the cardinality of the result sequence. - -This method has 2 overloads. - ### Unfold Returns a sequence generated by applying a state to the generator function, @@ -770,6 +762,14 @@ Creates a sequence that lazily caches the source as it is iterated for the first time, reusing the cache thereafter for future re-iterations. If the source is already cached or buffered then it is returned verbatim. +### TrySingle + +Returns the only element of a sequence that has just one element. If the +sequence has zero or multiple elements, then returns a user-defined value +that indicates the cardinality of the result sequence. + +This method has 2 overloads. + [#122]: https://github.com/morelinq/MoreLINQ/issues/122 [dict]: https://docs.microsoft.com/en-us/dotnet/api/System.Collections.Generic.Dictionary-2 From 8f72fe112b6f46a2b28b94af1e1fe3b29c1d718b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 27 Dec 2019 09:51:16 +0100 Subject: [PATCH 017/157] Prepare for 3.3.2 --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index aaa0380e4..129eabcda 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -117,7 +117,7 @@ $([System.Text.RegularExpressions.Regex]::Replace($(Copyright), `\s+`, ` `).Trim()) MoreLINQ en-US - 3.3.1 + 3.3.2 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 8 From dc9542ed2064e8b71f26df773bde572ba8eb111a Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 27 Dec 2019 09:53:26 +0100 Subject: [PATCH 018/157] Mark TrySingle as experimental in package description Closes #742 --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 129eabcda..22f0c7434 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -105,7 +105,7 @@ - Transpose - TraverseBreadthFirst - TraverseDepthFirst - - TrySingle + - TrySingle (EXPERIMENTAL) - Unfold - Window - WindowLeft From b8021734c1b78ae861e6df13f9d7de6db26d87f9 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 27 Dec 2019 10:57:07 +0100 Subject: [PATCH 019/157] Fix Batch regression for empty collection This is a squashed merge of PR #743 that fixes #741. --- MoreLinq.Test/BatchTest.cs | 10 ++++++++++ MoreLinq/Batch.cs | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index 09cdadbae..f297b2969 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -117,5 +117,15 @@ public void BatchReadOnlyCollectionSmallerThanSize(int oversize) reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); reader.ReadEnd(); } + + [TestCase(SourceKind.Sequence)] + [TestCase(SourceKind.BreakingList)] + [TestCase(SourceKind.BreakingReadOnlyList)] + [TestCase(SourceKind.BreakingCollection)] + public void BatchEmptySource(SourceKind kind) + { + var batches = Enumerable.Empty().ToSourceKind(kind).Batch(100); + Assert.That(batches, Is.Empty); + } } } diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index c6db41d82..99afb6102 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Linq; static partial class MoreEnumerable { @@ -86,6 +87,10 @@ public static IEnumerable Batch(this IEnumerable collection when collection.Count == 0: + { + return Enumerable.Empty(); + } case ICollection collection when collection.Count <= size: { return _(); IEnumerable _() @@ -95,6 +100,10 @@ public static IEnumerable Batch(this IEnumerable list when list.Count == 0: + { + return Enumerable.Empty(); + } case IReadOnlyList list when list.Count <= size: { return _(); IEnumerable _() From 07bd0861658b381ce97c8b44d3b9f2cd3c9bf769 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 27 Dec 2019 11:42:27 +0100 Subject: [PATCH 020/157] Add some simple tests for OrderedMerge This is a squashed merge of PR #736. --- MoreLinq.Test/OrderedMergeTest.cs | 72 +++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 MoreLinq.Test/OrderedMergeTest.cs diff --git a/MoreLinq.Test/OrderedMergeTest.cs b/MoreLinq.Test/OrderedMergeTest.cs new file mode 100644 index 000000000..f2db2acb7 --- /dev/null +++ b/MoreLinq.Test/OrderedMergeTest.cs @@ -0,0 +1,72 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Pierre Lando. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System.Collections.Generic; + using NUnit.Framework; + using NUnit.Framework.Interfaces; + + [TestFixture] + public class OrderedMergeTest + { + static IEnumerable Seq(params T[] values) => values; + + public static IEnumerable TestData = + from e in new[] + { + new + { + A = Seq(0, 2, 4), + B = Seq(0, 1, 2, 3, 4), + R = Seq(0, 1, 2, 3, 4) + }, + new + { + A = Seq(0, 1, 2, 3, 4), + B = Seq(0, 2, 4), + R = Seq(0, 1, 2, 3, 4) + }, + new + { + A = Seq(0, 2, 4), + B = Seq(1, 3, 5), + R = Seq(0, 1, 2, 3, 4, 5) + }, + new + { + A = Seq(), + B = Seq(0, 1, 2), + R = Seq(0, 1, 2) + }, + new + { + A = Seq(0, 1, 2), + B = Seq(), + R = Seq(0, 1, 2) + } + } + select new TestCaseData(e.A.AsTestingSequence(), e.B.AsTestingSequence()).Returns(e.R); + + [Test, TestCaseSource(nameof(TestData))] + public IEnumerable OrderedMerge(IEnumerable first, + IEnumerable second) + { + return first.OrderedMerge(second); + } + } +} From 8b66c50933381ff30671d35acef6dcc7edc9bbd6 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 10:15:32 +0200 Subject: [PATCH 021/157] Replace explicit type mentions with object pattern --- MoreLinq.Test/AggregateTest.cs | 8 ++++---- MoreLinq.Test/ChooseTest.cs | 2 +- MoreLinq/AssertCount.cs | 2 +- MoreLinq/Backsert.cs | 2 +- MoreLinq/CountDown.cs | 4 ++-- MoreLinq/CountMethods.cs | 4 ++-- MoreLinq/EndsWith.cs | 4 ++-- MoreLinq/Experimental/Await.cs | 2 +- MoreLinq/Experimental/TrySingle.cs | 2 +- MoreLinq/FallbackIfEmpty.cs | 2 +- MoreLinq/Flatten.cs | 2 +- MoreLinq/PadStart.cs | 2 +- MoreLinq/SkipLast.cs | 2 +- MoreLinq/StartsWith.cs | 4 ++-- MoreLinq/TakeLast.cs | 2 +- 15 files changed, 22 insertions(+), 22 deletions(-) diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 55b531658..3a6ca3094 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -110,8 +110,8 @@ public void SevenUniqueAccumulators() 0, (s, e) => s + e.Num, 0, (s, e) => e.Num % 2 == 0 ? s + e.Num : s, 0, (s, _) => s + 1, - (int?)null, (s, e) => s is int n ? Math.Min(n, e.Num) : e.Num, - (int?)null, (s, e) => s is int n ? Math.Max(n, e.Num) : e.Num, + (int?)null, (s, e) => s is {} n ? Math.Min(n, e.Num) : e.Num, + (int?)null, (s, e) => s is {} n ? Math.Max(n, e.Num) : e.Num, new HashSet(), (s, e) => { s.Add(e.Str.Length); return s; }, new List<(int Num, string Str)>(), (s, e) => { s.Add((e.Num, e.Str)); return s; }, (sum, esum, count, min, max, lengths, items) => new @@ -120,8 +120,8 @@ public void SevenUniqueAccumulators() EvenSum = esum, Count = count, Average = (double)sum / count, - Min = min is int mn ? mn : throw new InvalidOperationException(), - Max = max is int mx ? mx : throw new InvalidOperationException(), + Min = min is {} mn ? mn : throw new InvalidOperationException(), + Max = max is {} mx ? mx : throw new InvalidOperationException(), UniqueLengths = lengths, Items = items, } diff --git a/MoreLinq.Test/ChooseTest.cs b/MoreLinq.Test/ChooseTest.cs index 95e2b3c8a..e1c335945 100644 --- a/MoreLinq.Test/ChooseTest.cs +++ b/MoreLinq.Test/ChooseTest.cs @@ -75,7 +75,7 @@ static class Option public void ThoseThatAreIntegers() { new int?[] { 0, 1, 2, null, 4, null, 6, null, null, 9 } - .Choose(e => e is int n ? Option.Some(n) : Option.None) + .Choose(e => e is {} n ? Option.Some(n) : Option.None) .AssertSequenceEqual(0, 1, 2, 4, 6, 9); } diff --git a/MoreLinq/AssertCount.cs b/MoreLinq/AssertCount.cs index 8296e166c..db4ff8e4f 100644 --- a/MoreLinq/AssertCount.cs +++ b/MoreLinq/AssertCount.cs @@ -89,7 +89,7 @@ static IEnumerable AssertCountImpl(IEnumerable source if (errorSelector == null) throw new ArgumentNullException(nameof(errorSelector)); return - source.TryGetCollectionCount() is int collectionCount + source.TryGetCollectionCount() is {} collectionCount ? collectionCount == count ? source : From(() => throw errorSelector(collectionCount.CompareTo(count), count)) diff --git a/MoreLinq/Backsert.cs b/MoreLinq/Backsert.cs index 928eef47a..1ac6a9d4c 100644 --- a/MoreLinq/Backsert.cs +++ b/MoreLinq/Backsert.cs @@ -72,7 +72,7 @@ public static IEnumerable Backsert(this IEnumerable first, IEnumerable< if (e.MoveNext()) { var (_, countdown) = e.Current; - if (countdown is int n && n != index - 1) + if (countdown is {} n && n != index - 1) throw new ArgumentOutOfRangeException(nameof(index), "Insertion index is greater than the length of the first sequence."); do diff --git a/MoreLinq/CountDown.cs b/MoreLinq/CountDown.cs index 94dd43675..f95985898 100644 --- a/MoreLinq/CountDown.cs +++ b/MoreLinq/CountDown.cs @@ -57,9 +57,9 @@ public static IEnumerable CountDown(this IEnumerable sou if (source == null) throw new ArgumentNullException(nameof(source)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.TryAsListLike() is IListLike listLike + return source.TryAsListLike() is {} listLike ? IterateList(listLike) - : source.TryGetCollectionCount() is int collectionCount + : source.TryGetCollectionCount() is {} collectionCount ? IterateCollection(collectionCount) : IterateSequence(); diff --git a/MoreLinq/CountMethods.cs b/MoreLinq/CountMethods.cs index b55ee8656..fd2cb640e 100644 --- a/MoreLinq/CountMethods.cs +++ b/MoreLinq/CountMethods.cs @@ -167,11 +167,11 @@ public static int CompareCount(this IEnumerable first, if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); - if (first.TryGetCollectionCount() is int firstCount) + if (first.TryGetCollectionCount() is {} firstCount) { return firstCount.CompareTo(second.TryGetCollectionCount() ?? second.CountUpTo(firstCount + 1)); } - else if (second.TryGetCollectionCount() is int secondCount) + else if (second.TryGetCollectionCount() is {} secondCount) { return first.CountUpTo(secondCount + 1).CompareTo(secondCount); } diff --git a/MoreLinq/EndsWith.cs b/MoreLinq/EndsWith.cs index ca1de4e66..2d9a2fcf5 100644 --- a/MoreLinq/EndsWith.cs +++ b/MoreLinq/EndsWith.cs @@ -74,8 +74,8 @@ public static bool EndsWith(this IEnumerable first, IEnumerable second, comparer ??= EqualityComparer.Default; List secondList; - return second.TryGetCollectionCount() is int secondCount - ? first.TryGetCollectionCount() is int firstCount && secondCount > firstCount + return second.TryGetCollectionCount() is {} secondCount + ? first.TryGetCollectionCount() is {} firstCount && secondCount > firstCount ? false : Impl(second, secondCount) : Impl(secondList = second.ToList(), secondList.Count); diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index 229fccbcb..834e7f5d3 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -636,7 +636,7 @@ void OnPendingCompleted() onEnd(); } - var concurrencyGate = maxConcurrency is int count + var concurrencyGate = maxConcurrency is {} count ? new ConcurrencyGate(count) : ConcurrencyGate.Unbounded; diff --git a/MoreLinq/Experimental/TrySingle.cs b/MoreLinq/Experimental/TrySingle.cs index 6aa591164..b88197ae5 100644 --- a/MoreLinq/Experimental/TrySingle.cs +++ b/MoreLinq/Experimental/TrySingle.cs @@ -115,7 +115,7 @@ public static TResult TrySingle(this IEnumerable so }; return resultSelector(one, item); } - case int _: + case {}: return resultSelector(many, default); default: { diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 7d0c6915a..8675508c1 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -161,7 +161,7 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, int? count, T fallback1, T fallback2, T fallback3, T fallback4, IEnumerable fallback) { - return source.TryGetCollectionCount() is int collectionCount + return source.TryGetCollectionCount() is {} collectionCount ? collectionCount == 0 ? Fallback() : source : _(); diff --git a/MoreLinq/Flatten.cs b/MoreLinq/Flatten.cs index e98466c3e..ada470af3 100644 --- a/MoreLinq/Flatten.cs +++ b/MoreLinq/Flatten.cs @@ -110,7 +110,7 @@ public static IEnumerable Flatten(this IEnumerable source, Func PadStartImpl(IEnumerable source, int width, T padding, Func paddingSelector) { return - source.TryGetCollectionCount() is int collectionCount + source.TryGetCollectionCount() is {} collectionCount ? collectionCount >= width ? source : Enumerable.Range(0, width - collectionCount) diff --git a/MoreLinq/SkipLast.cs b/MoreLinq/SkipLast.cs index 6aebe4c3f..cfee629b3 100644 --- a/MoreLinq/SkipLast.cs +++ b/MoreLinq/SkipLast.cs @@ -41,7 +41,7 @@ public static IEnumerable SkipLast(this IEnumerable source, int count) return source; return - source.TryGetCollectionCount() is int collectionCount + source.TryGetCollectionCount() is {} collectionCount ? source.Take(collectionCount - count) : source.CountDown(count, (e, cd) => (Element: e, Countdown: cd )) .TakeWhile(e => e.Countdown == null) diff --git a/MoreLinq/StartsWith.cs b/MoreLinq/StartsWith.cs index 2bedebaf0..f6f298005 100644 --- a/MoreLinq/StartsWith.cs +++ b/MoreLinq/StartsWith.cs @@ -73,8 +73,8 @@ public static bool StartsWith(this IEnumerable first, IEnumerable secon if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); - if (first.TryGetCollectionCount() is int firstCount && - second.TryGetCollectionCount() is int secondCount && + if (first.TryGetCollectionCount() is {} firstCount && + second.TryGetCollectionCount() is {} secondCount && secondCount > firstCount) { return false; diff --git a/MoreLinq/TakeLast.cs b/MoreLinq/TakeLast.cs index 257c4d834..dba1d1ebb 100644 --- a/MoreLinq/TakeLast.cs +++ b/MoreLinq/TakeLast.cs @@ -54,7 +54,7 @@ public static IEnumerable TakeLast(this IEnumerable s return Enumerable.Empty(); return - source.TryGetCollectionCount() is int collectionCount + source.TryGetCollectionCount() is {} collectionCount ? source.Slice(Math.Max(0, collectionCount - count), int.MaxValue) : source.CountDown(count, (e, cd) => (Element: e, Countdown: cd)) .SkipWhile(e => e.Countdown == null) From d5675552575249333485b60dd01eb201b8c897f8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 10:01:46 +0200 Subject: [PATCH 022/157] Add terser "Func" instantiation helpers --- MoreLinq.Test/AggregateTest.cs | 3 ++- MoreLinq.Test/FuncModule.cs | 43 ++++++++++++++++++++++++++++++ MoreLinq.Test/MoreLinq.Test.csproj | 1 + 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 MoreLinq.Test/FuncModule.cs diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 3a6ca3094..98b49e8b6 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -26,6 +26,7 @@ namespace MoreLinq.Test using System.Reactive.Linq; using System.Reflection; using NUnit.Framework.Interfaces; + using static FuncModule; [TestFixture] public class AggregateTest @@ -75,7 +76,7 @@ into m Expression.NewArrayInit(typeof(int), m.Parameters), m.Parameters) .Compile() - let accumulator = new Func((s, n) => s + n) + let accumulator = Func((int s, int n) => s + n) select new { Name = $"{name}({m.AccumulatorCount})", diff --git a/MoreLinq.Test/FuncModule.cs b/MoreLinq.Test/FuncModule.cs new file mode 100644 index 000000000..75dbe6f08 --- /dev/null +++ b/MoreLinq.Test/FuncModule.cs @@ -0,0 +1,43 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2020 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System; + + // This type is designed to be imported statically. + // + // Its members enable replacing explicit instantiations of `Func<...>`, + // as in: + // + // new Func((a, b) => a + b) + // + // with the shorter version: + // + // Func((string a, object b) => a + b) + // + // The `new` is no longer required and the return type can be omitted + // as it can be inferred through the type of the lambda expression. + + static class FuncModule + { + public static Func Func(Func f) => f; + public static Func Func(Func f) => f; + public static Func Func(Func f) => f; + public static Func Func(Func f) => f; + } +} diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 4f9d06d1e..539d0484b 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -58,6 +58,7 @@ + From 55111f45bda82127e25343d988914416721c1637 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 13:46:27 +0200 Subject: [PATCH 023/157] Fix typo in Batch test name [ci skip] --- MoreLinq.Test/BatchTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index f297b2969..0002892af 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -51,7 +51,7 @@ public void BatchEvenlyDivisibleSequence() } [Test] - public void BatchUnevenlyDivisbleSequence() + public void BatchUnevenlyDivisibleSequence() { var result = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.Batch(4); using (var reader = result.Read()) From 0961301fb1a7bd76ed90af3456d45e0656c7fb40 Mon Sep 17 00:00:00 2001 From: Leandro Fernandes Date: Thu, 6 Aug 2020 20:04:27 -0300 Subject: [PATCH 024/157] Optimize Batch for empty read-only collection too This is a squashed merge of PR #750. --- MoreLinq.Test/BatchTest.cs | 1 + MoreLinq/Batch.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index 0002892af..9aa45fa82 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -121,6 +121,7 @@ public void BatchReadOnlyCollectionSmallerThanSize(int oversize) [TestCase(SourceKind.Sequence)] [TestCase(SourceKind.BreakingList)] [TestCase(SourceKind.BreakingReadOnlyList)] + [TestCase(SourceKind.BreakingReadOnlyCollection)] [TestCase(SourceKind.BreakingCollection)] public void BatchEmptySource(SourceKind kind) { diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index 99afb6102..d30dc3b7d 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -100,7 +100,7 @@ public static IEnumerable Batch(this IEnumerable list when list.Count == 0: + case IReadOnlyCollection collection when collection.Count == 0: { return Enumerable.Empty(); } From 53f4988d273cbeaf3e83514af65be2445eb6f228 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 6 Aug 2020 14:00:17 +0200 Subject: [PATCH 025/157] Remove unused Batch test cases --- MoreLinq.Test/BatchTest.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index 9aa45fa82..08fa3d34d 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -106,10 +106,8 @@ public void BatchCollectionSmallerThanSize(SourceKind kind, int oversize) reader.ReadEnd(); } - [TestCase(0)] - [TestCase(1)] - [TestCase(2)] - public void BatchReadOnlyCollectionSmallerThanSize(int oversize) + [Test] + public void BatchReadOnlyCollectionSmallerThanSize() { var collection = ReadOnlyCollection.From(1, 2, 3, 4, 5); var result = collection.Batch(collection.Count * 2); From ed10deb35c8aa093f7ad20ba62f62584b66b2100 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 15:31:08 +0200 Subject: [PATCH 026/157] Update to .NET Core SDK 3.1 --- .travis.yml | 2 +- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj | 2 +- global.json | 5 +++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08fa6e26d..db66e8feb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ solution: MoreLinq.sln mono: 5.0.1 dist: xenial sudo: required -dotnet: 3.0.100 +dotnet: 3.1.102 env: - CONFIGURATION=Debug - CONFIGURATION=Release diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 539d0484b..fa1baf752 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - netcoreapp2.1;netcoreapp3.0;net451 + netcoreapp3.1;netcoreapp2.1;net451 true portable MoreLinq.Test diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index b43ac7fd7..f7eb51e48 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -1,7 +1,7 @@  Exe - netcoreapp3.0 + netcoreapp3.1 8 false diff --git a/global.json b/global.json index 2223a05e3..cb62ab482 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,6 @@ { "sdk": { - "version": "3.0.100" + "version": "3.1.102", + "rollForward": "latestFeature" } -} \ No newline at end of file +} From 3e3c8a5977c7cd93b0f819571312f4f9e1f30c21 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 18:03:12 +0200 Subject: [PATCH 027/157] Update .NET Core installation script (#756) [ci skip] --- appveyor.yml | 2 +- tools/dotnet-install.ps1 | 451 +++++++++++++++++++++++++++++++++------ 2 files changed, 381 insertions(+), 72 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 67b83b8ad..c5f5ccfdc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,7 +16,7 @@ install: - eclint check -n "**/*.{cs,tt,cmd,sh,md,txt,yml}" - eclint check -w "**/*.{cs,tt,cmd,sh,md,txt,yml,json,sln,csproj,shfbproj}" - git reset --hard -- ps: tools\dotnet-install.ps1 -Version ((type .\global.json | ConvertFrom-Json).sdk.version) +- ps: tools\dotnet-install.ps1 -JSonFile global.json before_build: - dotnet --info build_script: diff --git a/tools/dotnet-install.ps1 b/tools/dotnet-install.ps1 index 8c59e162a..3b052ce3f 100644 --- a/tools/dotnet-install.ps1 +++ b/tools/dotnet-install.ps1 @@ -15,9 +15,10 @@ - Current - most current release - LTS - most current supported release - 2-part version in a format A.B - represents a specific release - examples: 2.0; 1.0 + examples: 2.0, 1.0 - Branch name - examples: release/2.0.0; Master + examples: release/2.0.0, Master + Note: The version parameter overrides the channel parameter. .PARAMETER Version Default: latest Represents a build version on specific channel. Possible values: @@ -25,26 +26,24 @@ - coherent - most latest coherent build on specific channel coherent applies only to SDK downloads - 3-part version in a format A.B.C - represents specific version of build - examples: 2.0.0-preview2-006120; 1.1.0 + examples: 2.0.0-preview2-006120, 1.1.0 .PARAMETER InstallDir Default: %LocalAppData%\Microsoft\dotnet Path to where to install dotnet. Note that binaries will be placed directly in a given directory. .PARAMETER Architecture Default: - this value represents currently running OS architecture Architecture of dotnet binaries to be installed. - Possible values are: , x64 and x86 + Possible values are: , amd64, x64, x86, arm64, arm .PARAMETER SharedRuntime This parameter is obsolete and may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'. - - Default: false Installs just the shared runtime bits, not the entire SDK. - This is equivalent to specifying `-Runtime dotnet`. .PARAMETER Runtime Installs just a shared runtime, not the entire SDK. Possible values: - dotnet - the Microsoft.NETCore.App shared runtime - aspnetcore - the Microsoft.AspNetCore.App shared runtime + - windowsdesktop - the Microsoft.WindowsDesktop.App shared runtime .PARAMETER DryRun If set it will not perform installation but instead display what command line to use to consistently install currently requested version of dotnet cli. In example if you specify version 'latest' it will display a link @@ -70,19 +69,25 @@ .PARAMETER ProxyUseDefaultCredentials Default: false Use default credentials, when using proxy address. +.PARAMETER ProxyBypassList + If set with ProxyAddress, will provide the list of comma separated urls that will bypass the proxy .PARAMETER SkipNonVersionedFiles Default: false Skips installing non-versioned files if they already exist, such as dotnet.exe. .PARAMETER NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly. +.PARAMETER JSonFile + Determines the SDK version from a user specified global.json file + Note: global.json must have a value for 'SDK:Version' #> [cmdletbinding()] param( [string]$Channel="LTS", [string]$Version="Latest", + [string]$JSonFile, [string]$InstallDir="", [string]$Architecture="", - [ValidateSet("dotnet", "aspnetcore", IgnoreCase = $false)] + [ValidateSet("dotnet", "aspnetcore", "windowsdesktop", IgnoreCase = $false)] [string]$Runtime, [Obsolete("This parameter may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'.")] [switch]$SharedRuntime, @@ -93,6 +98,7 @@ param( [string]$FeedCredential, [string]$ProxyAddress, [switch]$ProxyUseDefaultCredentials, + [string[]]$ProxyBypassList, [switch]$SkipNonVersionedFiles, [switch]$NoCdn ) @@ -116,11 +122,27 @@ $VersionRegEx="/\d+\.\d+[^/]+/" $OverrideNonVersionedFiles = !$SkipNonVersionedFiles function Say($str) { - Write-Host "dotnet-install: $str" + try + { + Write-Host "dotnet-install: $str" + } + catch + { + # Some platforms cannot utilize Write-Host (Azure Functions, for instance). Fall back to Write-Output + Write-Output "dotnet-install: $str" + } } function Say-Verbose($str) { - Write-Verbose "dotnet-install: $str" + try + { + Write-Verbose "dotnet-install: $str" + } + catch + { + # Some platforms cannot utilize Write-Verbose (Azure Functions, for instance). Fall back to Write-Output + Write-Output "dotnet-install: $str" + } } function Say-Invocation($Invocation) { @@ -151,7 +173,16 @@ function Invoke-With-Retry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [in function Get-Machine-Architecture() { Say-Invocation $MyInvocation - # possible values: amd64, x64, x86, arm64, arm + # On PS x86, PROCESSOR_ARCHITECTURE reports x86 even on x64 systems. + # To get the correct architecture, we need to use PROCESSOR_ARCHITEW6432. + # PS x64 doesn't define this, so we fall back to PROCESSOR_ARCHITECTURE. + # Possible values: amd64, x64, x86, arm64, arm + + if( $ENV:PROCESSOR_ARCHITEW6432 -ne $null ) + { + return $ENV:PROCESSOR_ARCHITEW6432 + } + return $ENV:PROCESSOR_ARCHITECTURE } @@ -164,18 +195,25 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) { { $_ -eq "x86" } { return "x86" } { $_ -eq "arm" } { return "arm" } { $_ -eq "arm64" } { return "arm64" } - default { throw "Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" } + default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues" } } } +# The version text returned from the feeds is a 1-line or 2-line string: +# For the SDK and the dotnet runtime (2 lines): +# Line 1: # commit_hash +# Line 2: # 4-part version +# For the aspnetcore runtime (1 line): +# Line 1: # 4-part version function Get-Version-Info-From-Version-Text([string]$VersionText) { Say-Invocation $MyInvocation - $Data = @($VersionText.Split([char[]]@(), [StringSplitOptions]::RemoveEmptyEntries)); + $Data = -split $VersionText - $VersionInfo = @{} - $VersionInfo.CommitHash = $Data[0].Trim() - $VersionInfo.Version = $Data[1].Trim() + $VersionInfo = @{ + CommitHash = $(if ($Data.Count -gt 1) { $Data[0] }) + Version = $Data[-1] # last line is always the version number. + } return $VersionInfo } @@ -218,7 +256,11 @@ function GetHTTPResponse([Uri] $Uri) if($ProxyAddress) { $HttpClientHandler = New-Object System.Net.Http.HttpClientHandler - $HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{Address=$ProxyAddress;UseDefaultCredentials=$ProxyUseDefaultCredentials} + $HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{ + Address=$ProxyAddress; + UseDefaultCredentials=$ProxyUseDefaultCredentials; + BypassList = $ProxyBypassList; + } $HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler } else { @@ -226,8 +268,8 @@ function GetHTTPResponse([Uri] $Uri) $HttpClient = New-Object System.Net.Http.HttpClient } # Default timeout for HttpClient is 100s. For a 50 MB download this assumes 500 KB/s average, any less will time out - # 10 minutes allows it to work over much slower connections. - $HttpClient.Timeout = New-TimeSpan -Minutes 10 + # 20 minutes allows it to work over much slower connections. + $HttpClient.Timeout = New-TimeSpan -Minutes 20 $Response = $HttpClient.GetAsync("${Uri}${FeedCredential}").Result if (($Response -eq $null) -or (-not ($Response.IsSuccessStatusCode))) { # The feed credential is potentially sensitive info. Do not log FeedCredential to console output. @@ -249,7 +291,6 @@ function GetHTTPResponse([Uri] $Uri) }) } - function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) { Say-Invocation $MyInvocation @@ -260,6 +301,10 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Co elseif ($Runtime -eq "aspnetcore") { $VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version" } + # Currently, the WindowsDesktop runtime is manufactured with the .Net core runtime + elseif ($Runtime -eq "windowsdesktop") { + $VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version" + } elseif (-not $Runtime) { if ($Coherent) { $VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.coherent.version" @@ -271,8 +316,12 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Co else { throw "Invalid value for `$Runtime" } - - $Response = GetHTTPResponse -Uri $VersionFileUrl + try { + $Response = GetHTTPResponse -Uri $VersionFileUrl + } + catch { + throw "Could not resolve version information." + } $StringContent = $Response.Content.ReadAsStringAsync().Result switch ($Response.Content.Headers.ContentType) { @@ -287,20 +336,59 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Co return $VersionInfo } - -function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version) { +function Parse-Jsonfile-For-Version([string]$JSonFile) { Say-Invocation $MyInvocation - switch ($Version.ToLower()) { - { $_ -eq "latest" } { - $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False - return $LatestVersionInfo.Version + If (-Not (Test-Path $JSonFile)) { + throw "Unable to find '$JSonFile'" + } + try { + $JSonContent = Get-Content($JSonFile) -Raw | ConvertFrom-Json | Select-Object -expand "sdk" -ErrorAction SilentlyContinue + } + catch { + throw "Json file unreadable: '$JSonFile'" + } + if ($JSonContent) { + try { + $JSonContent.PSObject.Properties | ForEach-Object { + $PropertyName = $_.Name + if ($PropertyName -eq "version") { + $Version = $_.Value + Say-Verbose "Version = $Version" + } + } } - { $_ -eq "coherent" } { - $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True - return $LatestVersionInfo.Version + catch { + throw "Unable to parse the SDK node in '$JSonFile'" } - default { return $Version } + } + else { + throw "Unable to find the SDK node in '$JSonFile'" + } + If ($Version -eq $null) { + throw "Unable to find the SDK:version node in '$JSonFile'" + } + return $Version +} + +function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version, [string]$JSonFile) { + Say-Invocation $MyInvocation + + if (-not $JSonFile) { + switch ($Version.ToLower()) { + { $_ -eq "latest" } { + $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False + return $LatestVersionInfo.Version + } + { $_ -eq "coherent" } { + $LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True + return $LatestVersionInfo.Version + } + default { return $Version } + } + } + else { + return Parse-Jsonfile-For-Version $JSonFile } } @@ -313,6 +401,9 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string elseif ($Runtime -eq "aspnetcore") { $PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificVersion-win-$CLIArchitecture.zip" } + elseif ($Runtime -eq "windowsdesktop") { + $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificVersion-win-$CLIArchitecture.zip" + } elseif (-not $Runtime) { $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip" } @@ -320,7 +411,7 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string throw "Invalid value for `$Runtime" } - Say-Verbose "Constructed primary payload URL: $PayloadURL" + Say-Verbose "Constructed primary named payload URL: $PayloadURL" return $PayloadURL } @@ -338,7 +429,7 @@ function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [ return $null } - Say-Verbose "Constructed legacy payload URL: $PayloadURL" + Say-Verbose "Constructed legacy named payload URL: $PayloadURL" return $PayloadURL } @@ -362,28 +453,11 @@ function Resolve-Installation-Path([string]$InstallDir) { return $InstallDir } -function Get-Version-Info-From-Version-File([string]$InstallRoot, [string]$RelativePathToVersionFile) { - Say-Invocation $MyInvocation - - $VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile - Say-Verbose "Local version file: $VersionFile" - - if (Test-Path $VersionFile) { - $VersionText = cat $VersionFile - Say-Verbose "Local version file text: $VersionText" - return Get-Version-Info-From-Version-Text $VersionText - } - - Say-Verbose "Local version file not found." - - return $null -} - function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) { Say-Invocation $MyInvocation $DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion - Say-Verbose "Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath" + Say-Verbose "Is-Dotnet-Package-Installed: DotnetPackagePath=$DotnetPackagePath" return Test-Path $DotnetPackagePath -PathType Container } @@ -468,17 +542,23 @@ function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) { } } -function DownloadFile([Uri]$Uri, [string]$OutPath) { - if ($Uri -notlike "http*") { - Say-Verbose "Copying file from $Uri to $OutPath" - Copy-Item $Uri.AbsolutePath $OutPath +function DownloadFile($Source, [string]$OutPath) { + if ($Source -notlike "http*") { + # Using System.IO.Path.GetFullPath to get the current directory + # does not work in this context - $pwd gives the current directory + if (![System.IO.Path]::IsPathRooted($Source)) { + $Source = $(Join-Path -Path $pwd -ChildPath $Source) + } + $Source = Get-Absolute-Path $Source + Say "Copying file from $Source to $OutPath" + Copy-Item $Source $OutPath return } $Stream = $null try { - $Response = GetHTTPResponse -Uri $Uri + $Response = GetHTTPResponse -Uri $Source $Stream = $Response.Content.ReadAsStreamAsync().Result $File = [System.IO.File]::Create($OutPath) $Stream.CopyTo($File) @@ -494,8 +574,13 @@ function DownloadFile([Uri]$Uri, [string]$OutPath) { function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolderRelativePath) { $BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath) if (-Not $NoPath) { - Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process." - $env:path = "$BinPath;" + $env:path + $SuffixedBinPath = "$BinPath;" + if (-Not $env:path.Contains($SuffixedBinPath)) { + Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process." + $env:path = $SuffixedBinPath + $env:path + } else { + Say-Verbose "Current process PATH already contains `"$BinPath`"" + } } else { Say "Binaries of dotnet can be found in $BinPath" @@ -503,23 +588,36 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolde } $CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture -$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version +$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile $DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture +$InstallRoot = Resolve-Installation-Path $InstallDir +Say-Verbose "InstallRoot: $InstallRoot" +$ScriptName = $MyInvocation.MyCommand.Name + if ($DryRun) { Say "Payload URLs:" - Say "Primary - $DownloadLink" + Say "Primary named payload URL: $DownloadLink" if ($LegacyDownloadLink) { - Say "Legacy - $LegacyDownloadLink" + Say "Legacy named payload URL: $LegacyDownloadLink" + } + $RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`"" + if ($Runtime -eq "dotnet") { + $RepeatableCommand+=" -Runtime `"dotnet`"" + } + elseif ($Runtime -eq "aspnetcore") { + $RepeatableCommand+=" -Runtime `"aspnetcore`"" + } + foreach ($key in $MyInvocation.BoundParameters.Keys) { + if (-not (@("Architecture","Channel","DryRun","InstallDir","Runtime","SharedRuntime","Version") -contains $key)) { + $RepeatableCommand+=" -$key `"$($MyInvocation.BoundParameters[$key])`"" + } } - Say "Repeatable invocation: .\$($MyInvocation.Line)" + Say "Repeatable invocation: $RepeatableCommand" exit 0 } -$InstallRoot = Resolve-Installation-Path $InstallDir -Say-Verbose "InstallRoot: $InstallRoot" - if ($Runtime -eq "dotnet") { $assetName = ".NET Core Runtime" $dotnetPackageRelativePath = "shared\Microsoft.NETCore.App" @@ -528,6 +626,10 @@ elseif ($Runtime -eq "aspnetcore") { $assetName = "ASP.NET Core Runtime" $dotnetPackageRelativePath = "shared\Microsoft.AspNetCore.App" } +elseif ($Runtime -eq "windowsdesktop") { + $assetName = ".NET Core Windows Desktop Runtime" + $dotnetPackageRelativePath = "shared\Microsoft.WindowsDesktop.App" +} elseif (-not $Runtime) { $assetName = ".NET Core SDK" $dotnetPackageRelativePath = "sdk" @@ -559,7 +661,7 @@ Say-Verbose "Zip path: $ZipPath" $DownloadFailed = $false Say "Downloading link: $DownloadLink" try { - DownloadFile -Uri $DownloadLink -OutPath $ZipPath + DownloadFile -Source $DownloadLink -OutPath $ZipPath } catch { Say "Cannot download: $DownloadLink" @@ -569,7 +671,7 @@ catch { Say-Verbose "Legacy zip path: $ZipPath" Say "Downloading legacy link: $DownloadLink" try { - DownloadFile -Uri $DownloadLink -OutPath $ZipPath + DownloadFile -Source $DownloadLink -OutPath $ZipPath } catch { Say "Cannot download: $DownloadLink" @@ -588,8 +690,22 @@ if ($DownloadFailed) { Say "Extracting zip from $DownloadLink" Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot -# Check if the SDK version is now installed; if not, fail the installation. -$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion +# Check if the SDK version is installed; if not, fail the installation. +$isAssetInstalled = $false + +# if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed. +if ($SpecificVersion -Match "rtm" -or $SpecificVersion -Match "servicing") { + $ReleaseVersion = $SpecificVersion.Split("-")[0] + Say-Verbose "Checking installation: version = $ReleaseVersion" + $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $ReleaseVersion +} + +# Check if the SDK version is installed. +if (!$isAssetInstalled) { + Say-Verbose "Checking installation: version = $SpecificVersion" + $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion +} + if (!$isAssetInstalled) { throw "`"$assetName`" with version = $SpecificVersion failed to install with an unknown error." } @@ -599,4 +715,197 @@ Remove-Item $ZipPath Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath Say "Installation finished" -exit 0 \ No newline at end of file +exit 0 + +# SIG # Begin signature block +# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCpfA/pR7OjIFT3 +# AofDlc6nOYGKjwNIAy3Eyvb16wpECqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX +# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD +# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p +# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw +# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u +# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB +# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH +# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d +# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ +# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV +# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE +# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw +# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 +# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu +# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu +# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w +# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 +# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx +# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy +# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K +# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV +# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr +# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx +# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe +# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g +# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf +# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI +# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 +# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea +# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS +# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK +# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 +# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 +# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla +# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS +# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT +# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB +# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG +# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S +# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz +# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 +# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u +# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 +# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl +# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP +# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB +# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF +# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM +# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ +# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud +# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO +# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 +# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y +# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p +# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y +# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB +# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw +# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA +# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY +# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj +# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd +# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ +# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf +# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ +# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j +# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B +# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 +# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 +# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I +# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG +# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx +# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z +# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN +# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor +# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgzc+/PrE5 +# 4VVWEHbuPUTelP1qUjpv3t9Tr6VG3NE7g5EwQgYKKwYBBAGCNwIBDDE0MDKgFIAS +# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN +# BgkqhkiG9w0BAQEFAASCAQBxJUwvSKHR/18WcqMt7X5KZ2EEH2C3C6OZcJ10VwIQ +# uwqgtGg1ZzaTtbBjdU58wK0URWPdiWv+DI2pLLy/qO6VsAieHd9h1Feqe1JExlh+ +# s43iGMFqiMpZp9qJ29MZe/I/4uC0ZTGatO97ld93tPGiRpbyXxZHbqkxPr1IMjns +# hVxoq0QH8ia699zw/6K6uCfdRlu49ZbyVoZbRRh9Cx3xEVVwiGO2/i2vMBOO0Xwc +# j0SBp/G2vUcmddBnf+DxcBbBmeDpNzJdggF+YtBVubLv1In2MHYOmfB8CXPAi0IS +# 57nptq/BQ1edeN9ytizPOc+gi8pRuwWt4rLTmr2JTEt/oYIS8TCCEu0GCisGAQQB +# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME +# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB +# MDEwDQYJYIZIAWUDBAIBBQAEIPZ740SKNZOpkM9U1riD6qS3XQ9TMXQJTBjlSj6W +# 6sYjAgZfFz3Hj4kYEzIwMjAwNzI5MTIyMDEwLjkyNVowBIACAfSggdSkgdEwgc4x +# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt +# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p +# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg +# VFNTIEVTTjozMkJELUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt +# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABLqjSGQeT9GvoAAAA +# AAEuMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo +# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y +# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw +# MB4XDTE5MTIxOTAxMTUwNVoXDTIxMDMxNzAxMTUwNVowgc4xCzAJBgNVBAYTAlVT +# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK +# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy +# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozMkJE +# LUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj +# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7TTKJRU196LFIjMQ9q +# /UjpPhz43m5RnHgHAVp2YGni74+ltsYoO1nZ58rTbJhCQ8GYHy8B4devgbqqYPQN +# U3i+drpEtEcNLbsMr4MEq3SM+vO3a6QMFd1lDRy7IQLPJNLKvcM69Nt7ku1YyM5N +# nPNDcRJsnUb/8Yx/zcW5cWjnoj8s9fQ93BPf/J74qM1ql2CdzQV74PBisMP/tppA +# nSuNwo8I7+uWr6vfpBynSWDvJeMDrcsa62Xsm7DbB1NnSsPGAGt3RzlBV9KViciz +# e4U3fo4chdoB2+QLu17PaEmj07qq700CG5XJkpEYOjedNFiByApF7YRvQrOZQ07Q +# YiMCAwEAAaOCARswggEXMB0GA1UdDgQWBBSGmokmTguJN7uqSTQ1UhLwt1RObDAf +# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH +# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU +# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF +# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 +# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG +# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCN4ARqpzCuutNqY2nWJDDXj35iaidl +# gtJ/bspYsAX8atJl19IfUKIzTuuSVU3caXZ6/YvMMYMcbsNa/4J28us23K6PWZAl +# jIj0G8QtwDMlQHjrKnrcr4FBAz6ZqvB6SrN3/Wbb0QSK/OlxsU0mfD7z87R2JM4g +# wKJvH6EILuAEtjwUGSB1NKm3Twrm51fCD0jxvWxzaUS2etvMPrh8DNrrHLJBR3UH +# vg/NXS2IzdQn20xjjsW0BUAiTf+NCRpxUvu/j80Nb1++vnejibfpQJ2IlXiJdIi+ +# Hb+OL3XOr8MaDDSYOaRFAIfcoq3VPi4BkvSC8QGrvhjAZafkE7R6L5FJMIIGcTCC +# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC +# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV +# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv +# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN +# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv +# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 +# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw +# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 +# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw +# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe +# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx +# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G +# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA +# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 +# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC +# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX +# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v +# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI +# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j +# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g +# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 +# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB +# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA +# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh +# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS +# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK +# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon +# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi +# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ +# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII +# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 +# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a +# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ +# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ +# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT +# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD +# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP +# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoz +# MkJELUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy +# dmljZaIjCgEBMAcGBSsOAwIaAxUA+1/CN6BILeU1yDGo+b6WkpLoQpuggYMwgYCk +# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH +# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD +# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF +# AOLLn2AwIhgPMjAyMDA3MjkxMTEwMjRaGA8yMDIwMDczMDExMTAyNFowdzA9Bgor +# BgEEAYRZCgQBMS8wLTAKAgUA4sufYAIBADAKAgEAAgImPAIB/zAHAgEAAgIRsTAK +# AgUA4szw4AIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB +# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAI1W0gMAkyYoYB5t +# BiO10MENTa3AeN9Mc5rqjrw99nmLr9S7zq9oTprZ59SQ2eomQpBaIO+33xWK2met +# +NR+bq2vNh+m6W2dacmZ2Ki8jlMfUajJaWX0xbHWaGMpL+9nlgZnNUcH7whVwXxp +# FlMaPFf0CKv8Uo11B4rlZat8fFE4MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC +# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV +# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp +# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEuqNIZB5P0a+gAAAAAAS4wDQYJYIZIAWUD +# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B +# CQQxIgQgJ7FDhQl++eQw1izf/wfOB3EPDCi12dQQe1YoFx8iEckwgfoGCyqGSIb3 +# DQEJEAIvMYHqMIHnMIHkMIG9BCDa/s3O8YhWiqpVN0kTeK+x2m0RAh17JpR6DiFo +# TILJKTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u +# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp +# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB +# LqjSGQeT9GvoAAAAAAEuMCIEIIpoWQMeh7Zef+LuYZIDjkaUw/O5NUX/s7oGyVZj +# W4khMA0GCSqGSIb3DQEBCwUABIIBAJ1oCV3CVki6wRkqMYV9scx3xjFJoNah1g4X +# FPXqezxKaG534hToWOZUmXfvnJwCt136zoR8Z0JSEE6PnWVU7e7EhTrqIkMSHSSL +# GGg5HIHS/nbmsu6VCigAHZn0okwXXy8ftOKRFhMVaOSLqa7qQgrRMHBBduHvq6xw +# bv/aqK6i2TE5Sv/QrhsgVDKWNL3oyhVlp+tJzCAuHILSVkClGXHNjrRDrEHkeWP7 +# xrbtgxDGHATlqra2bW2bAp2B46X9qjZkBmHopXS/VOfSQltQWef9VyuB4wzrNuxG +# gF4uSZOf20eeD1svHaZvTVNwIdLq6WvPVhUB4s6GWFofm1dcDpQ= +# SIG # End signature block From 780d32333bea172c1edd98744becb1248d41ba2c Mon Sep 17 00:00:00 2001 From: Orace Date: Tue, 11 Aug 2020 18:31:00 +0200 Subject: [PATCH 028/157] Fix for lazy iteration of enumerators in Interleave This is a squashed merge of PR #730. --- MoreLinq.Test/InterleaveTest.cs | 48 +++++++++++++++++++++++++-------- MoreLinq/Interleave.cs | 20 +++++++++++--- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 35c3bd863..6c1498262 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -36,15 +36,17 @@ public void TestInterleaveIsLazy() } /// - /// Verify that interleaving do not call enumerators MoveNext method eagerly + /// Verify that interleaving disposes those enumerators that it managed + /// to open successfully /// [Test] - public void TestInterleaveDoNoCallMoveNextEagerly() + public void TestInterleaveDisposesOnErrorAtGetEnumerator() { - var sequenceA = Enumerable.Range(1, 1); - var sequenceB = MoreEnumerable.From(() => throw new TestException()); + using var sequenceA = TestingSequence.Of(); + var sequenceB = new BreakingSequence(); - sequenceA.Interleave(sequenceB).Take(1).Consume(); + // Expected and thrown by BreakingSequence + Assert.Throws(() => sequenceA.Interleave(sequenceB).Consume()); } /// @@ -52,13 +54,37 @@ public void TestInterleaveDoNoCallMoveNextEagerly() /// to open successfully /// [Test] - public void TestInterleaveDisposesOnError() + public void TestInterleaveDisposesOnErrorAtMoveNext() { - using (var sequenceA = TestingSequence.Of()) - { - Assert.Throws(() => // Expected and thrown by BreakingSequence - sequenceA.Interleave(new BreakingSequence()).Consume()); - } + using var sequenceA = TestingSequence.Of(); + using var sequenceB = MoreEnumerable.From(() => throw new TestException()).AsTestingSequence(); + + // Expected and thrown by sequenceB + Assert.Throws(() => sequenceA.Interleave(sequenceB).Consume()); + } + + /// + /// Verify that interleaving do not call enumerable GetEnumerator method eagerly + /// + [Test] + public void TestInterleaveDoNotCallGetEnumeratorEagerly() + { + var sequenceA = TestingSequence.Of(1); + var sequenceB = new BreakingSequence(); + + sequenceA.Interleave(sequenceB).Take(1).Consume(); + } + + /// + /// Verify that interleaving do not call enumerators MoveNext method eagerly + /// + [Test] + public void TestInterleaveDoNoCallMoveNextEagerly() + { + var sequenceA = Enumerable.Range(1, 1); + var sequenceB = MoreEnumerable.From(() => throw new TestException()); + + sequenceA.Interleave(sequenceB).Take(1).Consume(); } /// diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 164fec136..3ffcb26c0 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -53,17 +53,29 @@ public static IEnumerable Interleave(this IEnumerable sequence, params return _(); IEnumerable _() { var sequences = new[] { sequence }.Concat(otherSequences); - - // produce an enumerators collection for all IEnumerable instances passed to us - var enumerators = sequences.Select(e => e.GetEnumerator()).Acquire(); + var enumerators = new List>(); try { + foreach (var enumerator in sequences.Select(s => s.GetEnumerator())) + { + enumerators.Add(enumerator); + if (enumerator.MoveNext()) + { + yield return enumerator.Current; + } + else + { + enumerators.Remove(enumerator); + enumerator.Dispose(); + } + } + var hasNext = true; while (hasNext) { hasNext = false; - for (var i = 0; i < enumerators.Length; i++) + for (var i = 0; i < enumerators.Count; i++) { var enumerator = enumerators[i]; if (enumerator == null) From 2af212c752c30e39d830e497d31e69acd1d6ce64 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 19:28:20 +0200 Subject: [PATCH 029/157] Fix test script to test against netcoreapp3.1 This technically belongs with commit "Update to .NET Core SDK 3.1" (ed10deb35c8aa093f7ad20ba62f62584b66b2100). --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 263c1fea0..b26ecb839 100755 --- a/test.sh +++ b/test.sh @@ -7,7 +7,7 @@ if [[ -z "$1" ]]; then else configs="$1" fi -for v in 2.1 3.0; do +for v in 2.1 3.1; do for c in $configs; do if [[ "$c" == "Debug" ]]; then coverage_args="-p:CollectCoverage=true From b54c285f230684ce418c5ab9b7ccbe30ad8a1da1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 11 Aug 2020 23:34:54 +0200 Subject: [PATCH 030/157] Update all test dependencies This is a squashed merge of PR #755. --- .config/dotnet-tools.json | 2 +- .travis.yml | 2 +- MoreLinq.Test/MoreLinq.Test.csproj | 8 ++++---- appveyor.yml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 962d2ed9f..8ec2b9a94 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "4.3.6", + "version": "4.6.4", "commands": [ "reportgenerator" ] diff --git a/.travis.yml b/.travis.yml index db66e8feb..8946625c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,4 +50,4 @@ script: - # curl -s https://codecov.io/bash > codecov - curl -sSL https://raw.githubusercontent.com/codecov/codecov-bash/14662d32a4862918c31efafe4b450de1305a38e1/codecov > codecov - chmod +x codecov - - ./codecov -f ./MoreLinq.Test/coverage.opencover.xml + - ./codecov -f ./MoreLinq.Test/coverage.netcoreapp3.1.opencover.xml diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index fa1baf752..867d06dff 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -22,12 +22,12 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers all @@ -35,8 +35,8 @@ - - + + diff --git a/appveyor.yml b/appveyor.yml index c5f5ccfdc..8d4f0f7e7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -46,7 +46,7 @@ build_script: } test_script: - cmd: test.cmd -- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) +- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.netcoreapp3.1.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) - ps: Get-ChildItem tmp/cover | Compress-Archive -DestinationPath coverage-report.zip - cmd: | cd tmp\cover From 321205a7b77c8d53385d37b6417970b9c20fc76e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 12 Aug 2020 12:05:23 +0200 Subject: [PATCH 031/157] Deploy package only on CI build of "deploy" [ci ckip] This is a squashed merge of PR #758 that closes #681. --- .travis.yml | 1 + appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8946625c0..99a6754fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +if: NOT branch = deploy language: csharp os: - linux diff --git a/appveyor.yml b/appveyor.yml index 8d4f0f7e7..46a9d5656 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,7 +61,7 @@ deploy: secure: fhGwXyO35FSshRzs5GWmF1LJTrd1sIqmS/jNCSfO2LfOciuYAKiXuFMYZFGiTAl+ symbol_server: https://www.myget.org/F/morelinq/symbols/api/v2/package on: - branch: master + branch: deploy notifications: - provider: Email to: From cfc44cbd701a0a02e9c90c5880433465bef0519c Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 12 Aug 2020 12:55:57 +0200 Subject: [PATCH 032/157] Replace ternary chains with switch expressions --- MoreLinq/ListLike.cs | 13 ++++++++----- MoreLinq/MaxBy.cs | 24 +++++++++++++++--------- MoreLinq/MoreEnumerable.cs | 16 ++++++++-------- MoreLinq/Slice.cs | 9 ++++++--- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/MoreLinq/ListLike.cs b/MoreLinq/ListLike.cs index 45f99cc71..289f02b38 100644 --- a/MoreLinq/ListLike.cs +++ b/MoreLinq/ListLike.cs @@ -36,11 +36,14 @@ static class ListLike public static IListLike ToListLike(this IEnumerable source) => source.TryAsListLike() ?? new List(source.ToList()); - public static IListLike TryAsListLike(this IEnumerable source) - => source is null ? throw new ArgumentNullException(nameof(source)) - : source is IList list ? new List(list) - : source is IReadOnlyList readOnlyList ? new ReadOnlyList(readOnlyList) - : (IListLike) null; + public static IListLike TryAsListLike(this IEnumerable source) => + source switch + { + null => throw new ArgumentNullException(nameof(source)), + IList list => new List(list), + IReadOnlyList list => new ReadOnlyList(list), + _ => null + }; sealed class List : IListLike { diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index f9f3ab3f9..f3b502727 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -235,15 +235,21 @@ public IEnumerator GetEnumerator() => IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerable Take(int count) - => count == 0 ? Enumerable.Empty() - : count == 1 ? ExtremaBy(_source, Extremum.First, 1 , _selector, _comparer) - : ExtremaBy(_source, Extrema.First , count, _selector, _comparer); - - public IEnumerable TakeLast(int count) - => count == 0 ? Enumerable.Empty() - : count == 1 ? ExtremaBy(_source, Extremum.Last, 1 , _selector, _comparer) - : ExtremaBy(_source, Extrema.Last , count, _selector, _comparer); + public IEnumerable Take(int count) => + count switch + { + 0 => Enumerable.Empty(), + 1 => ExtremaBy(_source, Extremum.First, 1 , _selector, _comparer), + _ => ExtremaBy(_source, Extrema.First , count, _selector, _comparer) + }; + + public IEnumerable TakeLast(int count) => + count switch + { + 0 => Enumerable.Empty(), + 1 => ExtremaBy(_source, Extremum.Last, 1 , _selector, _comparer), + _ => ExtremaBy(_source, Extrema.Last , count, _selector, _comparer) + }; static class Extrema { diff --git a/MoreLinq/MoreEnumerable.cs b/MoreLinq/MoreEnumerable.cs index 118911f81..7d0630dbb 100644 --- a/MoreLinq/MoreEnumerable.cs +++ b/MoreLinq/MoreEnumerable.cs @@ -27,14 +27,14 @@ namespace MoreLinq public static partial class MoreEnumerable { - internal static int? TryGetCollectionCount(this IEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - - return source is ICollection collection ? collection.Count - : source is IReadOnlyCollection readOnlyCollection ? readOnlyCollection.Count - : (int?)null; - } + internal static int? TryGetCollectionCount(this IEnumerable source) => + source switch + { + null => throw new ArgumentNullException(nameof(source)), + ICollection collection => collection.Count, + IReadOnlyCollection collection => collection.Count, + _ => null + }; static int CountUpTo(this IEnumerable source, int max) { diff --git a/MoreLinq/Slice.cs b/MoreLinq/Slice.cs index c0db188d8..356ac356d 100644 --- a/MoreLinq/Slice.cs +++ b/MoreLinq/Slice.cs @@ -45,9 +45,12 @@ public static IEnumerable Slice(this IEnumerable sequence, int startInd if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - return sequence is IList list ? SliceList(list.Count, i => list[i]) - : sequence is IReadOnlyList readOnlyList ? SliceList(readOnlyList.Count, i => readOnlyList[i]) - : sequence.Skip(startIndex).Take(count); + return sequence switch + { + IList list => SliceList(list.Count, i => list[i]), + IReadOnlyList list => SliceList(list.Count, i => list[i]), + var seq => seq.Skip(startIndex).Take(count) + }; IEnumerable SliceList(int listCount, Func indexer) { From 8b8cac233c0f68a1f30fc9bcf10eab6da0b7ae20 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 12 Aug 2020 13:20:11 +0200 Subject: [PATCH 033/157] Fix typo in method name: AsWatchable --- MoreLinq.Test/CountDownTest.cs | 2 +- MoreLinq.Test/TestingSequence.cs | 2 +- MoreLinq.Test/WatchableEnumerator.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/CountDownTest.cs b/MoreLinq.Test/CountDownTest.cs index 4e6a6d30f..09bb9d940 100644 --- a/MoreLinq.Test/CountDownTest.cs +++ b/MoreLinq.Test/CountDownTest.cs @@ -108,7 +108,7 @@ IEnumerator Watch(IEnumerator e) { moves = 0; disposed = false; - var te = e.AsWatchtable(); + var te = e.AsWatchable(); te.Disposed += delegate { disposed = true; }; te.MoveNextCalled += delegate { moves++; }; return te; diff --git a/MoreLinq.Test/TestingSequence.cs b/MoreLinq.Test/TestingSequence.cs index cbafe5f0e..9a6e3ed7f 100644 --- a/MoreLinq.Test/TestingSequence.cs +++ b/MoreLinq.Test/TestingSequence.cs @@ -65,7 +65,7 @@ void AssertDisposed() public IEnumerator GetEnumerator() { Assert.That(_sequence, Is.Not.Null, "LINQ operators should not enumerate a sequence more than once."); - var enumerator = _sequence.GetEnumerator().AsWatchtable(); + var enumerator = _sequence.GetEnumerator().AsWatchable(); _disposed = false; enumerator.Disposed += delegate { diff --git a/MoreLinq.Test/WatchableEnumerator.cs b/MoreLinq.Test/WatchableEnumerator.cs index 72fbe0b44..ca22e87be 100644 --- a/MoreLinq.Test/WatchableEnumerator.cs +++ b/MoreLinq.Test/WatchableEnumerator.cs @@ -23,7 +23,7 @@ namespace MoreLinq.Test partial class TestExtensions { - public static WatchableEnumerator AsWatchtable(this IEnumerator source) => + public static WatchableEnumerator AsWatchable(this IEnumerator source) => new WatchableEnumerator(source); } From 86f6f5807a2bb84ecd40b482c9b0bce3f9ea3147 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 14 Aug 2020 10:20:31 +0200 Subject: [PATCH 034/157] Add missing Min/MaxBy tests - MinBy followed by First - MinBy followed by FirstOrDefault - MinBy followed by Last - MinBy followed by LastOrDefault - MaxBy followed by First - MaxBy followed by FirstOrDefault - MaxBy followed by Last - MaxBy followed by LastOrDefault --- MoreLinq.Test/Comparable.cs | 28 ++++ MoreLinq.Test/MaxByTest.cs | 215 ++++++++++++++++++++++++----- MoreLinq.Test/MinByTest.cs | 215 ++++++++++++++++++++++++----- MoreLinq.Test/MoreLinq.Test.csproj | 1 + 4 files changed, 390 insertions(+), 69 deletions(-) create mode 100644 MoreLinq.Test/Comparable.cs diff --git a/MoreLinq.Test/Comparable.cs b/MoreLinq.Test/Comparable.cs new file mode 100644 index 000000000..4ec8d7a62 --- /dev/null +++ b/MoreLinq.Test/Comparable.cs @@ -0,0 +1,28 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2020 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System; + using System.Collections.Generic; + + static class Comparable where T : IComparable + { + public static readonly IComparer DescendingOrderComparer = + Comparer.Create((x, y) => -Math.Sign(x.CompareTo(y))); + } +} diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 0f6c19616..94640c580 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -17,6 +17,7 @@ namespace MoreLinq.Test { + using System; using NUnit.Framework; [TestFixture] @@ -60,56 +61,202 @@ public void MaxByWithComparer() Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MaxBy(x => x[1], SampleData.ReverseCharComparer)); } - [TestCase(0, ExpectedResult = new string[0] )] - [TestCase(1, ExpectedResult = new[] { "hello" })] - [TestCase(2, ExpectedResult = new[] { "hello", "world" })] - [TestCase(3, ExpectedResult = new[] { "hello", "world" })] - public string[] MaxByTakeReturnsMaxima(int count) + public class First { - using (var strings = SampleData.Strings.AsTestingSequence()) - return strings.MaxBy(s => s.Length).Take(count).ToArray(); + [Test] + public void ReturnsMaxima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length).First(), Is.EqualTo("hello")); + } + + [Test] + public void WithComparerReturnsMaxima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) + .First(), + Is.EqualTo("ax")); + } + + [Test] + public void WithEmptySourceThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MaxBy(s => s.Length).First()); + } + + [Test] + public void WithEmptySourceWithComparerThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer).First()); + } } - [TestCase(0, ExpectedResult = new string[0] )] - [TestCase(1, ExpectedResult = new[] { "world" })] - [TestCase(2, ExpectedResult = new[] { "hello", "world" })] - [TestCase(3, ExpectedResult = new[] { "hello", "world" })] - public string[] MaxByTakeLastReturnsMaxima(int count) + public class FirstOrDefault { - using (var strings = SampleData.Strings.AsTestingSequence()) - return strings.MaxBy(s => s.Length).TakeLast(count).ToArray(); + [Test] + public void ReturnsMaxima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length).FirstOrDefault(), Is.EqualTo("hello")); + } + + [Test] + public void WithComparerReturnsMaxima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) + .FirstOrDefault(), + Is.EqualTo("ax")); + } + + [Test] + public void WithEmptySourceReturnDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length).FirstOrDefault(), Is.Null); + } + + [Test] + public void WithEmptySourceWithComparerReturnsDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) + .FirstOrDefault(), + Is.Null); + } + } + + public class Last + { + [Test] + public void ReturnsMaximum() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length).Last(), Is.EqualTo("world")); + } + + [Test] + public void WithComparerReturnsMaximumPerComparer() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) + .Last(), + Is.EqualTo("az")); + } + + [Test] + public void WithEmptySourceThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MaxBy(s => s.Length).Last()); + } + + [Test] + public void WithEmptySourceWithComparerThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer).Last()); + } + } + + public class LastOrDefault + { + [Test] + public void ReturnsMaximum() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length).LastOrDefault(), Is.EqualTo("world")); + } + + [Test] + public void WithComparerReturnsMaximumPerComparer() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) + .LastOrDefault(), + Is.EqualTo("az")); + } + + [Test] + public void WithEmptySourceReturnDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length).LastOrDefault(), Is.Null); + } + + [Test] + public void WithEmptySourceWithComparerReturnsDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) + .LastOrDefault(), + Is.Null); + } } - [TestCase(0, 0, ExpectedResult = new string[0] )] - [TestCase(3, 1, ExpectedResult = new[] { "aa" })] - [TestCase(1, 0, ExpectedResult = new[] { "ax" })] - [TestCase(2, 0, ExpectedResult = new[] { "ax", "aa" })] - [TestCase(3, 0, ExpectedResult = new[] { "ax", "aa", "ab" })] - [TestCase(4, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay" })] - [TestCase(5, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - [TestCase(6, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - public string[] MaxByTakeWithComparerReturnsMaxima(int count, int index) + public class Take { - using (var strings = SampleData.Strings.AsTestingSequence()) + [TestCase(0, ExpectedResult = new string[0] )] + [TestCase(1, ExpectedResult = new[] { "hello" })] + [TestCase(2, ExpectedResult = new[] { "hello", "world" })] + [TestCase(3, ExpectedResult = new[] { "hello", "world" })] + public string[] ReturnsMaxima(int count) + { + using var strings = SampleData.Strings.AsTestingSequence(); + return strings.MaxBy(s => s.Length).Take(count).ToArray(); + } + + [TestCase(0, 0, ExpectedResult = new string[0] )] + [TestCase(3, 1, ExpectedResult = new[] { "aa" })] + [TestCase(1, 0, ExpectedResult = new[] { "ax" })] + [TestCase(2, 0, ExpectedResult = new[] { "ax", "aa" })] + [TestCase(3, 0, ExpectedResult = new[] { "ax", "aa", "ab" })] + [TestCase(4, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay" })] + [TestCase(5, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + [TestCase(6, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + public string[] WithComparerReturnsMaximaPerComparer(int count, int index) + { + using var strings = SampleData.Strings.AsTestingSequence(); return strings.MaxBy(s => s[index], SampleData.ReverseCharComparer) .Take(count) .ToArray(); + } } - [TestCase(0, 0, ExpectedResult = new string[0] )] - [TestCase(3, 1, ExpectedResult = new[] { "aa" })] - [TestCase(1, 0, ExpectedResult = new[] { "az" })] - [TestCase(2, 0, ExpectedResult = new[] { "ay", "az" })] - [TestCase(3, 0, ExpectedResult = new[] { "ab", "ay", "az" })] - [TestCase(4, 0, ExpectedResult = new[] { "aa", "ab", "ay", "az" })] - [TestCase(5, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - [TestCase(6, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - public string[] MaxByTakeLastWithComparerReturnsMaxima(int count, int index) + public class TakeLast { - using (var strings = SampleData.Strings.AsTestingSequence()) + [TestCase(0, ExpectedResult = new string[0] )] + [TestCase(1, ExpectedResult = new[] { "world" })] + [TestCase(2, ExpectedResult = new[] { "hello", "world" })] + [TestCase(3, ExpectedResult = new[] { "hello", "world" })] + public string[] TakeLastReturnsMaxima(int count) + { + using var strings = SampleData.Strings.AsTestingSequence(); + return strings.MaxBy(s => s.Length).TakeLast(count).ToArray(); + } + + [TestCase(0, 0, ExpectedResult = new string[0] )] + [TestCase(3, 1, ExpectedResult = new[] { "aa" })] + [TestCase(1, 0, ExpectedResult = new[] { "az" })] + [TestCase(2, 0, ExpectedResult = new[] { "ay", "az" })] + [TestCase(3, 0, ExpectedResult = new[] { "ab", "ay", "az" })] + [TestCase(4, 0, ExpectedResult = new[] { "aa", "ab", "ay", "az" })] + [TestCase(5, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + [TestCase(6, 0, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + public string[] WithComparerReturnsMaximaPerComparer(int count, int index) + { + using var strings = SampleData.Strings.AsTestingSequence(); return strings.MaxBy(s => s[index], SampleData.ReverseCharComparer) .TakeLast(count) .ToArray(); + } } } } diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 820e05b40..103319f7c 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -18,7 +18,6 @@ namespace MoreLinq.Test { using System; - using System.Collections.Generic; using NUnit.Framework; [TestFixture] @@ -62,54 +61,200 @@ public void MinByWithComparer() Assert.AreEqual(new[] { "az" }, SampleData.Strings.MinBy(x => x[1], SampleData.ReverseCharComparer)); } - [TestCase(0, ExpectedResult = new string[0] )] - [TestCase(1, ExpectedResult = new[] { "ax" })] - [TestCase(2, ExpectedResult = new[] { "ax", "aa" })] - [TestCase(3, ExpectedResult = new[] { "ax", "aa", "ab" })] - [TestCase(4, ExpectedResult = new[] { "ax", "aa", "ab", "ay" })] - [TestCase(5, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - [TestCase(6, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - public string[] MinByTakeReturnsMinima(int count) + public class First { - using (var strings = SampleData.Strings.AsTestingSequence()) - return strings.MinBy(s => s.Length).Take(count).ToArray(); + [Test] + public void ReturnsMinima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length).First(), Is.EqualTo("ax")); + } + + [Test] + public void WithComparerReturnsMinima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + .First(), + Is.EqualTo("hello")); + } + + [Test] + public void WithEmptySourceThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MinBy(s => s.Length).First()); + } + + [Test] + public void WithEmptySourceWithComparerThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer).First()); + } } - [TestCase(0, ExpectedResult = new string[0] )] - [TestCase(1, ExpectedResult = new[] { "az" })] - [TestCase(2, ExpectedResult = new[] { "ay", "az" })] - [TestCase(3, ExpectedResult = new[] { "ab", "ay", "az" })] - [TestCase(4, ExpectedResult = new[] { "aa", "ab", "ay", "az" })] - [TestCase(5, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - [TestCase(6, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] - public string[] MinByTakeLastReturnsMinima(int count) + public class FirstOrDefault { - using (var strings = SampleData.Strings.AsTestingSequence()) - return strings.MinBy(s => s.Length).TakeLast(count).ToArray(); + [Test] + public void ReturnsMinima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length).FirstOrDefault(), Is.EqualTo("ax")); + } + + [Test] + public void WithComparerReturnsMinima() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + .FirstOrDefault(), + Is.EqualTo("hello")); + } + + [Test] + public void WithEmptySourceReturnDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length).FirstOrDefault(), Is.Null); + } + + [Test] + public void WithEmptySourceWithComparerReturnsDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + .FirstOrDefault(), + Is.Null); + } + } + + public class Last + { + [Test] + public void ReturnsMinimum() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length).Last(), Is.EqualTo("az")); + } + + [Test] + public void WithComparerReturnsMinimumPerComparer() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + .Last(), + Is.EqualTo("world")); + } + + [Test] + public void WithEmptySourceThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MinBy(s => s.Length).Last()); + } + + [Test] + public void WithEmptySourceWithComparerThrows() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.Throws(() => + strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer).Last()); + } + } + + public class LastOrDefault + { + [Test] + public void ReturnsMinimum() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length).LastOrDefault(), Is.EqualTo("az")); + } + + [Test] + public void WithComparerReturnsMinimumPerComparer() + { + using var strings = SampleData.Strings.AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + .LastOrDefault(), + Is.EqualTo("world")); + } + + [Test] + public void WithEmptySourceReturnDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length).LastOrDefault(), Is.Null); + } + + [Test] + public void WithEmptySourceWithComparerReturnsDefault() + { + using var strings = Enumerable.Empty().AsTestingSequence(); + Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + .LastOrDefault(), + Is.Null); + } } - [TestCase(0, ExpectedResult = new string[0] )] - [TestCase(1, ExpectedResult = new[] { "hello", })] - [TestCase(2, ExpectedResult = new[] { "hello", "world" })] - [TestCase(3, ExpectedResult = new[] { "hello", "world" })] - public string[] MinByTakeWithComparerReturnsMinima(int count) + public class Take { - using (var strings = SampleData.Strings.AsTestingSequence()) - return strings.MinBy(s => s.Length, Comparer.Create((x, y) => -Math.Sign(x.CompareTo(y)))) + [TestCase(0, ExpectedResult = new string[0] )] + [TestCase(1, ExpectedResult = new[] { "ax" })] + [TestCase(2, ExpectedResult = new[] { "ax", "aa" })] + [TestCase(3, ExpectedResult = new[] { "ax", "aa", "ab" })] + [TestCase(4, ExpectedResult = new[] { "ax", "aa", "ab", "ay" })] + [TestCase(5, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + [TestCase(6, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + public string[] ReturnsMinima(int count) + { + using var strings = SampleData.Strings.AsTestingSequence(); + return strings.MinBy(s => s.Length).Take(count).ToArray(); + } + + [TestCase(0, ExpectedResult = new string[0] )] + [TestCase(1, ExpectedResult = new[] { "hello", })] + [TestCase(2, ExpectedResult = new[] { "hello", "world" })] + [TestCase(3, ExpectedResult = new[] { "hello", "world" })] + public string[] WithComparerReturnsMinimaPerComparer(int count) + { + using var strings = SampleData.Strings.AsTestingSequence(); + return strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) .Take(count) .ToArray(); + } } - [TestCase(0, ExpectedResult = new string[0] )] - [TestCase(1, ExpectedResult = new[] { "world", })] - [TestCase(2, ExpectedResult = new[] { "hello", "world" })] - [TestCase(3, ExpectedResult = new[] { "hello", "world" })] - public string[] MinByTakeLastWithComparerReturnsMinima(int count) + public class TakeLast { - using (var strings = SampleData.Strings.AsTestingSequence()) - return strings.MinBy(s => s.Length, Comparer.Create((x, y) => -Math.Sign(x.CompareTo(y)))) + [TestCase(0, ExpectedResult = new string[0] )] + [TestCase(1, ExpectedResult = new[] { "az" })] + [TestCase(2, ExpectedResult = new[] { "ay", "az" })] + [TestCase(3, ExpectedResult = new[] { "ab", "ay", "az" })] + [TestCase(4, ExpectedResult = new[] { "aa", "ab", "ay", "az" })] + [TestCase(5, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + [TestCase(6, ExpectedResult = new[] { "ax", "aa", "ab", "ay", "az" })] + public string[] ReturnsMinima(int count) + { + using var strings = SampleData.Strings.AsTestingSequence(); + return strings.MinBy(s => s.Length).TakeLast(count).ToArray(); + } + + [TestCase(0, ExpectedResult = new string[0] )] + [TestCase(1, ExpectedResult = new[] { "world", })] + [TestCase(2, ExpectedResult = new[] { "hello", "world" })] + [TestCase(3, ExpectedResult = new[] { "hello", "world" })] + public string[] WithComparerReturnsMinimaPerComparer(int count) + { + using var strings = SampleData.Strings.AsTestingSequence(); + return strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) .TakeLast(count) .ToArray(); + } } } } diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 867d06dff..45f75013c 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -54,6 +54,7 @@ + From 6051a10f28a142e1f9826530d5f4e532f207c362 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 14 Aug 2020 10:24:40 +0200 Subject: [PATCH 035/157] Fix typo in Min/MaxBy test names [ci skip] --- MoreLinq.Test/MaxByTest.cs | 12 ++++++------ MoreLinq.Test/MinByTest.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 94640c580..aee8e6889 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -64,14 +64,14 @@ public void MaxByWithComparer() public class First { [Test] - public void ReturnsMaxima() + public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MaxBy(s => s.Length).First(), Is.EqualTo("hello")); } [Test] - public void WithComparerReturnsMaxima() + public void WithComparerReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) @@ -99,14 +99,14 @@ public void WithEmptySourceWithComparerThrows() public class FirstOrDefault { [Test] - public void ReturnsMaxima() + public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MaxBy(s => s.Length).FirstOrDefault(), Is.EqualTo("hello")); } [Test] - public void WithComparerReturnsMaxima() + public void WithComparerReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) @@ -115,7 +115,7 @@ public void WithComparerReturnsMaxima() } [Test] - public void WithEmptySourceReturnDefault() + public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.That(strings.MaxBy(s => s.Length).FirstOrDefault(), Is.Null); @@ -185,7 +185,7 @@ public void WithComparerReturnsMaximumPerComparer() } [Test] - public void WithEmptySourceReturnDefault() + public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.That(strings.MaxBy(s => s.Length).LastOrDefault(), Is.Null); diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 103319f7c..96e2a19cd 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -64,14 +64,14 @@ public void MinByWithComparer() public class First { [Test] - public void ReturnsMinima() + public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MinBy(s => s.Length).First(), Is.EqualTo("ax")); } [Test] - public void WithComparerReturnsMinima() + public void WithComparerReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) @@ -99,14 +99,14 @@ public void WithEmptySourceWithComparerThrows() public class FirstOrDefault { [Test] - public void ReturnsMinima() + public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MinBy(s => s.Length).FirstOrDefault(), Is.EqualTo("ax")); } [Test] - public void WithComparerReturnsMinima() + public void WithComparerReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) @@ -115,7 +115,7 @@ public void WithComparerReturnsMinima() } [Test] - public void WithEmptySourceReturnDefault() + public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.That(strings.MinBy(s => s.Length).FirstOrDefault(), Is.Null); @@ -185,7 +185,7 @@ public void WithComparerReturnsMinimumPerComparer() } [Test] - public void WithEmptySourceReturnDefault() + public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.That(strings.MinBy(s => s.Length).LastOrDefault(), Is.Null); From dae766ca9f6d3b3cf2be409972c900a061706e3f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 14 Aug 2020 11:43:16 +0200 Subject: [PATCH 036/157] Remove duplicate ReverseCharComparer in tests --- MoreLinq.Test/MaxByTest.cs | 7 ++++--- MoreLinq.Test/MinByTest.cs | 3 ++- MoreLinq.Test/SampleData.cs | 7 ------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index aee8e6889..124ea7e30 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -18,6 +18,7 @@ namespace MoreLinq.Test { using System; + using System.Collections.Generic; using NUnit.Framework; [TestFixture] @@ -58,7 +59,7 @@ public void MaxByWithNaturalComparer() [Test] public void MaxByWithComparer() { - Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MaxBy(x => x[1], SampleData.ReverseCharComparer)); + Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MaxBy(x => x[1], Comparable.DescendingOrderComparer)); } public class First @@ -224,7 +225,7 @@ public string[] ReturnsMaxima(int count) public string[] WithComparerReturnsMaximaPerComparer(int count, int index) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MaxBy(s => s[index], SampleData.ReverseCharComparer) + return strings.MaxBy(s => s[index], Comparable.DescendingOrderComparer) .Take(count) .ToArray(); } @@ -253,7 +254,7 @@ public string[] TakeLastReturnsMaxima(int count) public string[] WithComparerReturnsMaximaPerComparer(int count, int index) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MaxBy(s => s[index], SampleData.ReverseCharComparer) + return strings.MaxBy(s => s[index], Comparable.DescendingOrderComparer) .TakeLast(count) .ToArray(); } diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 96e2a19cd..09c834d72 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -18,6 +18,7 @@ namespace MoreLinq.Test { using System; + using System.Collections.Generic; using NUnit.Framework; [TestFixture] @@ -58,7 +59,7 @@ public void MinByWithNaturalComparer() [Test] public void MinByWithComparer() { - Assert.AreEqual(new[] { "az" }, SampleData.Strings.MinBy(x => x[1], SampleData.ReverseCharComparer)); + Assert.AreEqual(new[] { "az" }, SampleData.Strings.MinBy(x => x[1], Comparable.DescendingOrderComparer)); } public class First diff --git a/MoreLinq.Test/SampleData.cs b/MoreLinq.Test/SampleData.cs index 5a2b4c7f0..b49e36e9d 100644 --- a/MoreLinq.Test/SampleData.cs +++ b/MoreLinq.Test/SampleData.cs @@ -34,12 +34,5 @@ static class SampleData internal static readonly Func Plus = (a, b) => a + b; internal static readonly Func Mul = (a, b) => a * b; - - internal static readonly IComparer ReverseCharComparer = new ReverseCharComparerImpl(); - - class ReverseCharComparerImpl : IComparer - { - public int Compare(char x, char y) => y.CompareTo(x); - } } } From 4674730f3fa705b0ee6d3f610c05f100b2435478 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 14 Aug 2020 11:45:34 +0200 Subject: [PATCH 037/157] Fix bug in LastOrDefault for extrema sequence --- MoreLinq.Test/MaxByTest.cs | 56 +++++++++++++++++++------------------- MoreLinq.Test/MinByTest.cs | 56 +++++++++++++++++++------------------- MoreLinq/MaxBy.cs | 2 +- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 124ea7e30..3e9a7fc0e 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -68,16 +68,16 @@ public class First public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length).First(), Is.EqualTo("hello")); + var maxima = strings.MaxBy(s => s.Length); + Assert.That(MoreEnumerable.First(maxima), Is.EqualTo("hello")); } [Test] public void WithComparerReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) - .First(), - Is.EqualTo("ax")); + var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.First(maxima), Is.EqualTo("ax")); } [Test] @@ -85,7 +85,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MaxBy(s => s.Length).First()); + MoreEnumerable.First(strings.MaxBy(s => s.Length))); } [Test] @@ -93,7 +93,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer).First()); + MoreEnumerable.First(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -103,32 +103,32 @@ public class FirstOrDefault public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length).FirstOrDefault(), Is.EqualTo("hello")); + var maxima = strings.MaxBy(s => s.Length); + Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.EqualTo("hello")); } [Test] public void WithComparerReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) - .FirstOrDefault(), - Is.EqualTo("ax")); + var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.EqualTo("ax")); } [Test] public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length).FirstOrDefault(), Is.Null); + var maxima = strings.MaxBy(s => s.Length); + Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.Null); } [Test] public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) - .FirstOrDefault(), - Is.Null); + var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.Null); } } @@ -138,16 +138,16 @@ public class Last public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length).Last(), Is.EqualTo("world")); + var maxima = strings.MaxBy(s => s.Length); + Assert.That(MoreEnumerable.Last(maxima), Is.EqualTo("world")); } [Test] public void WithComparerReturnsMaximumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) - .Last(), - Is.EqualTo("az")); + var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.Last(maxima), Is.EqualTo("az")); } [Test] @@ -155,7 +155,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MaxBy(s => s.Length).Last()); + MoreEnumerable.Last(strings.MaxBy(s => s.Length))); } [Test] @@ -163,7 +163,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer).Last()); + MoreEnumerable.Last(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -173,32 +173,32 @@ public class LastOrDefault public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length).LastOrDefault(), Is.EqualTo("world")); + var maxima = strings.MaxBy(s => s.Length); + Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.EqualTo("world")); } [Test] public void WithComparerReturnsMaximumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) - .LastOrDefault(), - Is.EqualTo("az")); + var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.EqualTo("az")); } [Test] public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length).LastOrDefault(), Is.Null); + var maxima = strings.MaxBy(s => s.Length); + Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.Null); } [Test] public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer) - .LastOrDefault(), - Is.Null); + var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.Null); } } diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 09c834d72..acd90eb01 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -68,16 +68,16 @@ public class First public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length).First(), Is.EqualTo("ax")); + var minima = MoreEnumerable.First(strings.MinBy(s => s.Length)); + Assert.That(minima, Is.EqualTo("ax")); } [Test] public void WithComparerReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) - .First(), - Is.EqualTo("hello")); + var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.First(minima), Is.EqualTo("hello")); } [Test] @@ -85,7 +85,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MinBy(s => s.Length).First()); + MoreEnumerable.First(strings.MinBy(s => s.Length))); } [Test] @@ -93,7 +93,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer).First()); + MoreEnumerable.First(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -103,32 +103,32 @@ public class FirstOrDefault public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length).FirstOrDefault(), Is.EqualTo("ax")); + var minima = strings.MinBy(s => s.Length); + Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.EqualTo("ax")); } [Test] public void WithComparerReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) - .FirstOrDefault(), - Is.EqualTo("hello")); + var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.EqualTo("hello")); } [Test] public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length).FirstOrDefault(), Is.Null); + var minima = strings.MinBy(s => s.Length); + Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.Null); } [Test] public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) - .FirstOrDefault(), - Is.Null); + var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.Null); } } @@ -138,16 +138,16 @@ public class Last public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length).Last(), Is.EqualTo("az")); + var minima = strings.MinBy(s => s.Length); + Assert.That(MoreEnumerable.Last(minima), Is.EqualTo("az")); } [Test] public void WithComparerReturnsMinimumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) - .Last(), - Is.EqualTo("world")); + var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.Last(minima), Is.EqualTo("world")); } [Test] @@ -155,7 +155,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MinBy(s => s.Length).Last()); + MoreEnumerable.Last(strings.MinBy(s => s.Length))); } [Test] @@ -163,7 +163,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer).Last()); + MoreEnumerable.Last(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -173,32 +173,32 @@ public class LastOrDefault public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length).LastOrDefault(), Is.EqualTo("az")); + var minima = strings.MinBy(s => s.Length); + Assert.That(MoreEnumerable.LastOrDefault(minima), Is.EqualTo("az")); } [Test] public void WithComparerReturnsMinimumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) - .LastOrDefault(), - Is.EqualTo("world")); + var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.LastOrDefault(minima), Is.EqualTo("world")); } [Test] public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length).LastOrDefault(), Is.Null); + var minima = strings.MinBy(s => s.Length); + Assert.That(MoreEnumerable.LastOrDefault(minima), Is.Null); } [Test] public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.That(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) - .LastOrDefault(), - Is.Null); + var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + Assert.That(MoreEnumerable.LastOrDefault(minima), Is.Null); } } diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index f3b502727..ce31a9ec9 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -126,7 +126,7 @@ public static T Last(this IExtremaEnumerable source) public static T LastOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Take(1).AsEnumerable().LastOrDefault(); + return source.TakeLast(1).AsEnumerable().LastOrDefault(); } /// From 8dec9ca04c73fdbeb03c9c95a71b921fa3f51fa7 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 22 Aug 2020 13:01:50 +0200 Subject: [PATCH 038/157] Annotate nullability of reference types This is a squashed merge of PR #582. --- .travis.yml | 2 +- MoreLinq/Assert.cs | 2 +- MoreLinq/Batch.cs | 2 +- MoreLinq/Collections/Dictionary.cs | 72 +++++++++++++++++++ MoreLinq/CountBy.cs | 34 +++------ MoreLinq/Delegating.cs | 14 ++-- MoreLinq/DistinctBy.cs | 2 +- MoreLinq/EmptyArray.cs | 7 ++ MoreLinq/EndsWith.cs | 2 +- MoreLinq/EquiZip.cs | 12 ++-- MoreLinq/ExceptBy.cs | 2 +- MoreLinq/Experimental/Await.cs | 87 ++++++++++++----------- MoreLinq/Experimental/Memoize.cs | 10 +-- MoreLinq/Experimental/TrySingle.cs | 12 ++-- MoreLinq/Extensions.ToDataTable.g.cs | 11 +++ MoreLinq/Extensions.g.cs | 102 +++++++++++++++------------ MoreLinq/FallbackIfEmpty.cs | 20 +++--- MoreLinq/FillBackward.cs | 4 +- MoreLinq/FillForward.cs | 15 ++-- MoreLinq/Flatten.cs | 2 +- MoreLinq/Fold.cs | 64 ++++++++--------- MoreLinq/FullJoin.cs | 4 +- MoreLinq/GroupAdjacent.cs | 52 ++++++-------- MoreLinq/IndexBy.cs | 2 +- MoreLinq/Interleave.cs | 2 +- MoreLinq/Lag.cs | 2 +- MoreLinq/Lead.cs | 2 +- MoreLinq/LeftJoin.cs | 4 +- MoreLinq/ListLike.cs | 2 +- MoreLinq/Lookup.cs | 2 + MoreLinq/MaxBy.cs | 84 +++++++++++----------- MoreLinq/MinBy.cs | 2 +- MoreLinq/MoreLinq.csproj | 8 +++ MoreLinq/OrderBy.cs | 4 +- MoreLinq/OrderedMerge.cs | 6 +- MoreLinq/Pad.cs | 7 +- MoreLinq/PadStart.cs | 6 +- MoreLinq/PartialSort.cs | 52 +++++++------- MoreLinq/Partition.cs | 14 ++-- MoreLinq/PendNode.cs | 10 +-- MoreLinq/Permutations.cs | 22 +++--- MoreLinq/Random.cs | 2 +- MoreLinq/Rank.cs | 2 +- MoreLinq/Reactive/Observable.cs | 2 +- MoreLinq/Reactive/Subject.cs | 10 +-- MoreLinq/RightJoin.cs | 4 +- MoreLinq/RunLengthEncode.cs | 2 +- MoreLinq/ScanBy.cs | 41 ++++------- MoreLinq/SequenceException.cs | 4 +- MoreLinq/SortedMerge.cs | 2 +- MoreLinq/Split.cs | 8 +-- MoreLinq/StartsWith.cs | 2 +- MoreLinq/ToArrayByIndex.cs | 2 +- MoreLinq/ToDataTable.cs | 8 ++- MoreLinq/ToDelimitedString.cs | 5 -- MoreLinq/ToDictionary.cs | 4 +- MoreLinq/ToHashSet.cs | 2 +- MoreLinq/ToLookup.cs | 4 +- MoreLinq/Trace.cs | 8 +-- MoreLinq/Transpose.cs | 11 +-- MoreLinq/ZipImpl.cs | 24 +++---- MoreLinq/ZipShortest.cs | 6 +- bld/ExtensionsGenerator/Program.cs | 11 +++ global.json | 2 +- 64 files changed, 511 insertions(+), 420 deletions(-) create mode 100644 MoreLinq/Collections/Dictionary.cs create mode 100644 MoreLinq/EmptyArray.cs diff --git a/.travis.yml b/.travis.yml index 99a6754fc..9c82769aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ solution: MoreLinq.sln mono: 5.0.1 dist: xenial sudo: required -dotnet: 3.1.102 +dotnet: 3.1.300 env: - CONFIGURATION=Debug - CONFIGURATION=Release diff --git a/MoreLinq/Assert.cs b/MoreLinq/Assert.cs index 722313366..1b272d8ed 100644 --- a/MoreLinq/Assert.cs +++ b/MoreLinq/Assert.cs @@ -60,7 +60,7 @@ public static IEnumerable Assert(this IEnumerable sou /// public static IEnumerable Assert(this IEnumerable source, - Func predicate, Func errorSelector) + Func predicate, Func? errorSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index d30dc3b7d..b55797335 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -125,7 +125,7 @@ public static IEnumerable Batch(this IEnumerable Batch(int size) { - TSource[] bucket = null; + TSource[]? bucket = null; var count = 0; foreach (var item in source) diff --git a/MoreLinq/Collections/Dictionary.cs b/MoreLinq/Collections/Dictionary.cs new file mode 100644 index 000000000..fad4e1a03 --- /dev/null +++ b/MoreLinq/Collections/Dictionary.cs @@ -0,0 +1,72 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2020 Atif Aziz, Leandro F. Vieira (leandromoh). All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Collections +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + + /// + /// A minimal wrapper that + /// allows null keys when is a + /// reference type. + /// + + // Add members if and when needed to keep coverage. + + sealed class Dictionary + { + readonly System.Collections.Generic.Dictionary _dict; + (bool, TValue) _null; + + public Dictionary(IEqualityComparer comparer) + { + _dict = new System.Collections.Generic.Dictionary(comparer); + _null = default; + } + + public TValue this[TKey key] + { + set + { + if (key is null) + _null = (true, value); + else + _dict[key] = value; + } + } + + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + if (key is null) + { + switch (_null) + { + case (true, {} v): + value = v; + return true; + case (false, _): + value = default!; + return false; + } + } + + return _dict.TryGetValue(key, out value); + } + } +} diff --git a/MoreLinq/CountBy.cs b/MoreLinq/CountBy.cs index 11e22ba29..e671fee63 100644 --- a/MoreLinq/CountBy.cs +++ b/MoreLinq/CountBy.cs @@ -50,7 +50,7 @@ public static IEnumerable> CountBy(this I /// If null, the default equality comparer for is used. /// A sequence of unique keys and their number of occurrences in the original sequence. - public static IEnumerable> CountBy(this IEnumerable source, Func keySelector, IEqualityComparer comparer) + public static IEnumerable> CountBy(this IEnumerable source, Func keySelector, IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -78,24 +78,11 @@ public static IEnumerable> CountBy(this I void Loop(IEqualityComparer cmp) { - var dic = new Dictionary(cmp); - var nullIndex = (int?) null; - - bool TryGetIndex(TKey key, out int i) - { - if (key == null) - { - i = nullIndex.GetValueOrDefault(); - return nullIndex.HasValue; - } - - return dic.TryGetValue(key, out i); - } + var dic = new Collections.Dictionary(cmp); keys = new List(); counts = new List(); - var havePrevKey = false; - var prevKey = default(TKey); + (bool, TKey) prevKey = default; var index = 0; foreach (var item in source) @@ -103,26 +90,23 @@ bool TryGetIndex(TKey key, out int i) var key = keySelector(item); if (// key same as the previous? then re-use the index - havePrevKey && cmp.GetHashCode(prevKey) == cmp.GetHashCode(key) - && cmp.Equals(prevKey, key) + prevKey is (true, {} pk) + && cmp.GetHashCode(pk) == cmp.GetHashCode(key) + && cmp.Equals(pk, key) // otherwise try & find index of the key - || TryGetIndex(key, out index)) + || dic.TryGetValue(key, out index)) { counts[index]++; } else { index = keys.Count; - if (key != null) - dic[key] = index; - else - nullIndex = index; + dic[key] = index; keys.Add(key); counts.Add(1); } - prevKey = key; - havePrevKey = true; + prevKey = (true, key); } } } diff --git a/MoreLinq/Delegating.cs b/MoreLinq/Delegating.cs index 6ccbf6885..fcdda4e5a 100644 --- a/MoreLinq/Delegating.cs +++ b/MoreLinq/Delegating.cs @@ -33,14 +33,14 @@ public static IDisposable Disposable(Action delegatee) => new DelegatingDisposable(delegatee); public static IObserver Observer(Action onNext, - Action onError = null, - Action onCompleted = null) => + Action? onError = null, + Action? onCompleted = null) => new DelegatingObserver(onNext, onError, onCompleted); } sealed class DelegatingDisposable : IDisposable { - Action _delegatee; + Action? _delegatee; public DelegatingDisposable(Action delegatee) => _delegatee = delegatee ?? throw new ArgumentNullException(nameof(delegatee)); @@ -57,12 +57,12 @@ public void Dispose() sealed class DelegatingObserver : IObserver { readonly Action _onNext; - readonly Action _onError; - readonly Action _onCompleted; + readonly Action? _onError; + readonly Action? _onCompleted; public DelegatingObserver(Action onNext, - Action onError = null, - Action onCompleted = null) + Action? onError = null, + Action? onCompleted = null) { _onNext = onNext ?? throw new ArgumentNullException(nameof(onNext)); _onError = onError; diff --git a/MoreLinq/DistinctBy.cs b/MoreLinq/DistinctBy.cs index c8dc010a4..a05faab82 100644 --- a/MoreLinq/DistinctBy.cs +++ b/MoreLinq/DistinctBy.cs @@ -63,7 +63,7 @@ public static IEnumerable DistinctBy(this IEnumerable public static IEnumerable DistinctBy(this IEnumerable source, - Func keySelector, IEqualityComparer comparer) + Func keySelector, IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); diff --git a/MoreLinq/EmptyArray.cs b/MoreLinq/EmptyArray.cs new file mode 100644 index 000000000..209912ee0 --- /dev/null +++ b/MoreLinq/EmptyArray.cs @@ -0,0 +1,7 @@ +namespace MoreLinq +{ + static class EmptyArray + { + public static readonly T[] Value = new T[0]; + } +} diff --git a/MoreLinq/EndsWith.cs b/MoreLinq/EndsWith.cs index 2d9a2fcf5..4c9664b79 100644 --- a/MoreLinq/EndsWith.cs +++ b/MoreLinq/EndsWith.cs @@ -66,7 +66,7 @@ public static bool EndsWith(this IEnumerable first, IEnumerable second) /// elements at the same index. /// - public static bool EndsWith(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + public static bool EndsWith(this IEnumerable first, IEnumerable second, IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/EquiZip.cs b/MoreLinq/EquiZip.cs index edede6ef1..9cde2a6d1 100644 --- a/MoreLinq/EquiZip.cs +++ b/MoreLinq/EquiZip.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; using System.Linq; static partial class MoreEnumerable @@ -169,15 +168,12 @@ public static IEnumerable EquiZip( } static IEnumerable EquiZipImpl( - IEnumerable s1, - IEnumerable s2, - IEnumerable s3, - IEnumerable s4, + IEnumerable s1, + IEnumerable s2, + IEnumerable? s3, + IEnumerable? s4, Func resultSelector) { - Debug.Assert(s1 != null); - Debug.Assert(s2 != null); - const int zero = 0, one = 1; var limit = 1 + (s3 != null ? one : zero) diff --git a/MoreLinq/ExceptBy.cs b/MoreLinq/ExceptBy.cs index d204ce8b0..d3faa6ea8 100644 --- a/MoreLinq/ExceptBy.cs +++ b/MoreLinq/ExceptBy.cs @@ -73,7 +73,7 @@ public static IEnumerable ExceptBy(this IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector, - IEqualityComparer keyComparer) + IEqualityComparer? keyComparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index 834e7f5d3..018818f02 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -28,6 +28,7 @@ namespace MoreLinq.Experimental using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; + using Unit = System.ValueTuple; /// /// Represents options for a query whose results evaluate asynchronously. @@ -436,43 +437,10 @@ IEnumerable _(int? maxConcurrency, TaskScheduler scheduler, bool ordere // BlockingCollection.Add throws if called after CompleteAdding // and we want to deliberately tolerate the race condition. - var notices = new BlockingCollection<(Notice, (int, T, Task), ExceptionDispatchInfo)>(); + var notices = new BlockingCollection<(Notice, (int, T, Task), ExceptionDispatchInfo?)>(); var consumerCancellationTokenSource = new CancellationTokenSource(); - (Exception, Exception) lastCriticalErrors = default; - - void PostNotice(Notice notice, - (int, T, Task) item, - Exception error) - { - // If a notice fails to post then assume critical error - // conditions (like low memory), capture the error without - // further allocation of resources and trip the cancellation - // token source used by the main loop waiting on notices. - // Note that only the "last" critical error is reported - // as maintaining a list would incur allocations. The idea - // here is to make a best effort attempt to report any of - // the error conditions that may be occuring, which is still - // better than nothing. - - try - { - var edi = error != null - ? ExceptionDispatchInfo.Capture(error) - : null; - notices.Add((notice, item, edi)); - } - catch (Exception e) - { - // Don't use ExceptionDispatchInfo.Capture here to avoid - // inducing allocations if already under low memory - // conditions. - - lastCriticalErrors = (e, error); - consumerCancellationTokenSource.Cancel(); - throw; - } - } + (Exception?, Exception?) lastCriticalErrors = default; var completed = false; var cancellationTokenSource = new CancellationTokenSource(); @@ -504,6 +472,39 @@ await enumerator.StartAsync( { PostNotice(Notice.Error, default, e); } + + void PostNotice(Notice notice, + (int, T, Task) item, + Exception? error) + { + // If a notice fails to post then assume critical error + // conditions (like low memory), capture the error without + // further allocation of resources and trip the cancellation + // token source used by the main loop waiting on notices. + // Note that only the "last" critical error is reported + // as maintaining a list would incur allocations. The idea + // here is to make a best effort attempt to report any of + // the error conditions that may be occuring, which is still + // better than nothing. + + try + { + var edi = error != null + ? ExceptionDispatchInfo.Capture(error) + : null; + notices.Add((notice, item, edi)); + } + catch (Exception e) + { + // Don't use ExceptionDispatchInfo.Capture here to avoid + // inducing allocations if already under low memory + // conditions. + + lastCriticalErrors = (e, error); + consumerCancellationTokenSource.Cancel(); + throw; + } + } }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, @@ -535,7 +536,9 @@ await enumerator.StartAsync( var (kind, result, error) = notice.Current; if (kind == Notice.Error) - error.Throw(); + { + error!.Throw(); + } if (kind == Notice.End) break; @@ -688,7 +691,7 @@ static class AwaitQuery public static IAwaitQuery Create( Func> impl, - AwaitQueryOptions options = null) => + AwaitQueryOptions? options = null) => new AwaitQuery(impl, options); } @@ -697,7 +700,7 @@ sealed class AwaitQuery : IAwaitQuery readonly Func> _impl; public AwaitQuery(Func> impl, - AwaitQueryOptions options = null) + AwaitQueryOptions? options = null) { _impl = impl; Options = options ?? AwaitQueryOptions.Default; @@ -735,8 +738,8 @@ static class CompletedTask static CompletedTask() { - var tcs = new TaskCompletionSource(); - tcs.SetResult(null); + var tcs = new TaskCompletionSource(); + tcs.SetResult(default); Instance = tcs.Task; } @@ -751,9 +754,9 @@ sealed class ConcurrencyGate { public static readonly ConcurrencyGate Unbounded = new ConcurrencyGate(); - readonly SemaphoreSlim _semaphore; + readonly SemaphoreSlim? _semaphore; - ConcurrencyGate(SemaphoreSlim semaphore = null) => + ConcurrencyGate(SemaphoreSlim? semaphore = null) => _semaphore = semaphore; public ConcurrencyGate(int max) : diff --git a/MoreLinq/Experimental/Memoize.cs b/MoreLinq/Experimental/Memoize.cs index 65d309fda..2ac99aa41 100644 --- a/MoreLinq/Experimental/Memoize.cs +++ b/MoreLinq/Experimental/Memoize.cs @@ -62,12 +62,12 @@ public static IEnumerable Memoize(this IEnumerable source) sealed class MemoizedEnumerable : IEnumerable, IDisposable { - List _cache; + List? _cache; readonly object _locker; readonly IEnumerable _source; - IEnumerator _sourceEnumerator; + IEnumerator? _sourceEnumerator; int? _errorIndex; - ExceptionDispatchInfo _error; + ExceptionDispatchInfo? _error; public MemoizedEnumerable(IEnumerable sequence) { @@ -115,7 +115,9 @@ public IEnumerator GetEnumerator() if (index >= _cache.Count) { if (index == _errorIndex) - _error.Throw(); + { + _error!.Throw(); + } if (_sourceEnumerator == null) break; diff --git a/MoreLinq/Experimental/TrySingle.cs b/MoreLinq/Experimental/TrySingle.cs index b88197ae5..6be3f8212 100644 --- a/MoreLinq/Experimental/TrySingle.cs +++ b/MoreLinq/Experimental/TrySingle.cs @@ -96,6 +96,10 @@ public static (TCardinality Cardinality, T Value) public static TResult TrySingle(this IEnumerable source, TCardinality zero, TCardinality one, TCardinality many, + // TODO review second argument of resultSelector + // ...that can be defaulted to null for nullable references + // so the signature is not quite accurate, but can we do + // something about that? Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -104,7 +108,7 @@ public static TResult TrySingle(this IEnumerable so switch (source.TryGetCollectionCount()) { case 0: - return resultSelector(zero, default); + return resultSelector(zero, default!); case 1: { var item = source switch @@ -116,15 +120,15 @@ public static TResult TrySingle(this IEnumerable so return resultSelector(one, item); } case {}: - return resultSelector(many, default); + return resultSelector(many, default!); default: { using var e = source.GetEnumerator(); if (!e.MoveNext()) - return resultSelector(zero, default); + return resultSelector(zero, default!); var current = e.Current; return !e.MoveNext() ? resultSelector(one, current) - : resultSelector(many, default); + : resultSelector(many, default!); } } } diff --git a/MoreLinq/Extensions.ToDataTable.g.cs b/MoreLinq/Extensions.ToDataTable.g.cs index 7931061c6..66c6f8931 100644 --- a/MoreLinq/Extensions.ToDataTable.g.cs +++ b/MoreLinq/Extensions.ToDataTable.g.cs @@ -17,11 +17,22 @@ // This code was generated by a tool. Any changes made manually will be lost // the next time this code is regenerated. +#nullable enable // required for auto-generated sources (see below why) + +// > Older code generation strategies may not be nullable aware. Setting the +// > project-level nullable context to "enable" could result in many +// > warnings that a user is unable to fix. To support this scenario any syntax +// > tree that is determined to be generated will have its nullable state +// > implicitly set to "disable", regardless of the overall project state. +// +// Source: https://github.com/dotnet/roslyn/blob/70e158ba6c2c99bd3c3fc0754af0dbf82a6d353d/docs/features/nullable-reference-types.md#generated-code + namespace MoreLinq.Extensions { using System; using System.CodeDom.Compiler; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Data; using System.Linq.Expressions; diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 5e45fc65f..2bab62b83 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -17,11 +17,22 @@ // This code was generated by a tool. Any changes made manually will be lost // the next time this code is regenerated. +#nullable enable // required for auto-generated sources (see below why) + +// > Older code generation strategies may not be nullable aware. Setting the +// > project-level nullable context to "enable" could result in many +// > warnings that a user is unable to fix. To support this scenario any syntax +// > tree that is determined to be generated will have its nullable state +// > implicitly set to "disable", regardless of the overall project state. +// +// Source: https://github.com/dotnet/roslyn/blob/70e158ba6c2c99bd3c3fc0754af0dbf82a6d353d/docs/features/nullable-reference-types.md#generated-code + namespace MoreLinq.Extensions { using System; using System.CodeDom.Compiler; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Collections; @@ -477,7 +488,7 @@ public static IEnumerable Assert(this IEnumerable sou /// public static IEnumerable Assert(this IEnumerable source, - Func predicate, Func errorSelector) + Func predicate, Func? errorSelector) => MoreEnumerable.Assert(source, predicate, errorSelector); } @@ -1174,7 +1185,7 @@ public static IEnumerable> CountBy(this I /// If null, the default equality comparer for is used. /// A sequence of unique keys and their number of occurrences in the original sequence. - public static IEnumerable> CountBy(this IEnumerable source, Func keySelector, IEqualityComparer comparer) + public static IEnumerable> CountBy(this IEnumerable source, Func keySelector, IEqualityComparer? comparer) => MoreEnumerable.CountBy(source, keySelector, comparer); } @@ -1263,7 +1274,7 @@ public static IEnumerable DistinctBy(this IEnumerable public static IEnumerable DistinctBy(this IEnumerable source, - Func keySelector, IEqualityComparer comparer) + Func keySelector, IEqualityComparer? comparer) => MoreEnumerable.DistinctBy(source, keySelector, comparer); } @@ -1314,7 +1325,7 @@ public static bool EndsWith(this IEnumerable first, IEnumerable second) /// elements at the same index. /// - public static bool EndsWith(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + public static bool EndsWith(this IEnumerable first, IEnumerable second, IEqualityComparer? comparer) => MoreEnumerable.EndsWith(first, second, comparer); } @@ -1554,7 +1565,7 @@ public static IEnumerable ExceptBy(this IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector, - IEqualityComparer keyComparer) + IEqualityComparer? keyComparer) => MoreEnumerable.ExceptBy(first, second, keySelector, keyComparer); } @@ -1894,6 +1905,7 @@ public static partial class FirstOrDefaultExtension /// otherwise, the first element in source. /// + [return: MaybeNull] public static T FirstOrDefault(this IExtremaEnumerable source) => MoreEnumerable.FirstOrDefault(source); @@ -1963,7 +1975,7 @@ public static IEnumerable Flatten(this IEnumerable source, Func /// is null. - public static IEnumerable Flatten(this IEnumerable source, Func selector) + public static IEnumerable Flatten(this IEnumerable source, Func selector) => MoreEnumerable.Flatten(source, selector); } @@ -2533,7 +2545,7 @@ public static IEnumerable FullJoin( Func firstSelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.FullJoin(first, second, keySelector, firstSelector, secondSelector, bothSelector, comparer); /// @@ -2628,7 +2640,7 @@ public static IEnumerable FullJoin( Func firstSelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.FullJoin(first, second, firstKeySelector, secondKeySelector, firstSelector, secondSelector, bothSelector, comparer); } @@ -2693,7 +2705,7 @@ public static IEnumerable> GroupAdjacent public static IEnumerable> GroupAdjacent( this IEnumerable source, Func keySelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.GroupAdjacent(source, keySelector, comparer); /// @@ -2796,7 +2808,7 @@ public static IEnumerable> GroupAdjacent source, Func keySelector, Func elementSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.GroupAdjacent(source, keySelector, elementSelector, comparer); /// @@ -2832,7 +2844,7 @@ public static IEnumerable GroupAdjacent( this IEnumerable source, Func keySelector, Func, TResult> resultSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.GroupAdjacent(source, keySelector, resultSelector, comparer); } @@ -2923,7 +2935,7 @@ public static IEnumerable> IndexBy( this IEnumerable source, Func keySelector, - IEqualityComparer comparer) => MoreEnumerable. IndexBy(source, keySelector, comparer); + IEqualityComparer? comparer) => MoreEnumerable. IndexBy(source, keySelector, comparer); } @@ -3076,6 +3088,7 @@ public static partial class LastOrDefaultExtension /// otherwise, the last element in source. /// + [return: MaybeNull] public static T LastOrDefault(this IExtremaEnumerable source) => MoreEnumerable.LastOrDefault(source); @@ -3203,7 +3216,7 @@ public static IEnumerable LeftJoin( Func keySelector, Func firstSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.LeftJoin(first, second, keySelector, firstSelector, bothSelector, comparer); /// @@ -3288,7 +3301,7 @@ public static IEnumerable LeftJoin( Func secondKeySelector, Func firstSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.LeftJoin(first, second, firstKeySelector, secondKeySelector, firstSelector, bothSelector, comparer); } @@ -3337,7 +3350,7 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// or is null public static IExtremaEnumerable MaxBy(this IEnumerable source, - Func selector, IComparer comparer) + Func selector, IComparer? comparer) => MoreEnumerable.MaxBy(source, selector, comparer); } @@ -3385,7 +3398,7 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// or is null public static IExtremaEnumerable MinBy(this IEnumerable source, - Func selector, IComparer comparer) + Func selector, IComparer? comparer) => MoreEnumerable.MinBy(source, selector, comparer); } @@ -3454,7 +3467,7 @@ public static IOrderedEnumerable OrderBy(this IEnumerable source, /// A comparer used to define the semantics of element comparison /// An ordered copy of the source sequence - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer, OrderByDirection direction) + public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer? comparer, OrderByDirection direction) => MoreEnumerable.OrderBy(source, keySelector, comparer, direction); } @@ -3508,7 +3521,7 @@ public static IEnumerable OrderedMerge( public static IEnumerable OrderedMerge( this IEnumerable first, IEnumerable second, - IComparer comparer) + IComparer? comparer) => MoreEnumerable.OrderedMerge(first, second, comparer); /// @@ -3612,7 +3625,7 @@ public static IEnumerable OrderedMerge( Func firstSelector, Func secondSelector, Func bothSelector, - IComparer comparer) + IComparer? comparer) => MoreEnumerable.OrderedMerge(first, second, keySelector, firstSelector, secondSelector, bothSelector, comparer); /// @@ -3701,7 +3714,7 @@ public static IEnumerable OrderedMerge( Func firstSelector, Func secondSelector, Func bothSelector, - IComparer comparer) + IComparer? comparer) => MoreEnumerable.OrderedMerge(first, second, firstKeySelector, secondKeySelector, firstSelector, secondSelector, bothSelector, comparer); } @@ -3976,7 +3989,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// public static IEnumerable PartialSort(this IEnumerable source, - int count, IComparer comparer) + int count, IComparer? comparer) => MoreEnumerable.PartialSort(source, count, comparer); /// @@ -3998,7 +4011,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// public static IEnumerable PartialSort(this IEnumerable source, - int count, IComparer comparer, OrderByDirection direction) + int count, IComparer? comparer, OrderByDirection direction) => MoreEnumerable.PartialSort(source, count, comparer, direction); } @@ -4071,7 +4084,7 @@ public static IEnumerable PartialSortBy( public static IEnumerable PartialSortBy( this IEnumerable source, int count, Func keySelector, - IComparer comparer) + IComparer? comparer) => MoreEnumerable.PartialSortBy(source, count, keySelector, comparer); /// @@ -4096,7 +4109,7 @@ public static IEnumerable PartialSortBy( public static IEnumerable PartialSortBy( this IEnumerable source, int count, Func keySelector, - IComparer comparer, + IComparer? comparer, OrderByDirection direction) => MoreEnumerable.PartialSortBy(source, count, keySelector, comparer, direction); @@ -4270,7 +4283,7 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, - TKey key, IEqualityComparer comparer, + TKey key, IEqualityComparer? comparer, Func, IEnumerable>, TResult> resultSelector) => MoreEnumerable.Partition(source, key, comparer, resultSelector); @@ -4324,7 +4337,7 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, - TKey key1, TKey key2, IEqualityComparer comparer, + TKey key1, TKey key2, IEqualityComparer? comparer, Func, IEnumerable, IEnumerable>, TResult> resultSelector) => MoreEnumerable.Partition(source, key1, key2, comparer, resultSelector); @@ -4353,7 +4366,7 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, - TKey key1, TKey key2, TKey key3, IEqualityComparer comparer, + TKey key1, TKey key2, TKey key3, IEqualityComparer? comparer, Func, IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) => MoreEnumerable.Partition(source, key1, key2, key3, comparer, resultSelector); } @@ -4577,7 +4590,7 @@ public static IEnumerable RankBy(this IEnumerable s /// An object that defines the comparison semantics for keys used to rank items /// A sequence of position integers representing the ranks of the corresponding items in the sequence - public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer comparer) + public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer? comparer) => MoreEnumerable.RankBy(source, keySelector, comparer); } @@ -4690,7 +4703,7 @@ public static IEnumerable RightJoin( Func keySelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.RightJoin(first, second, keySelector, secondSelector, bothSelector, comparer); /// @@ -4775,7 +4788,7 @@ public static IEnumerable RightJoin( Func secondKeySelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.RightJoin(first, second, firstKeySelector, secondKeySelector, secondSelector, bothSelector, comparer); } @@ -4806,7 +4819,7 @@ public static IEnumerable> RunLengthEncode(this IEnumera /// The comparer used to identify equivalent items /// A sequence of KeyValuePair{T,int} where they key is the element and the value is the occurrence count - public static IEnumerable> RunLengthEncode(this IEnumerable sequence, IEqualityComparer comparer) + public static IEnumerable> RunLengthEncode(this IEnumerable sequence, IEqualityComparer? comparer) => MoreEnumerable.RunLengthEncode(sequence, comparer); } @@ -4933,7 +4946,7 @@ public static IEnumerable> ScanBy keySelector, Func seedSelector, Func accumulator, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.ScanBy(source, keySelector, seedSelector, accumulator, comparer); } @@ -5139,6 +5152,7 @@ public static partial class SingleOrDefaultExtension /// if the sequence contains no elements. /// + [return: MaybeNull] public static T SingleOrDefault(this IExtremaEnumerable source) => MoreEnumerable.SingleOrDefault(source); @@ -5275,7 +5289,7 @@ public static IEnumerable SortedMerge(this IEnumerableA variable argument array of zero or more other sequences to merge with /// A merged, order-preserving sequence containing al of the elements of the original sequences - public static IEnumerable SortedMerge(this IEnumerable source, OrderByDirection direction, IComparer comparer, params IEnumerable[] otherSequences) + public static IEnumerable SortedMerge(this IEnumerable source, OrderByDirection direction, IComparer? comparer, params IEnumerable[] otherSequences) => MoreEnumerable.SortedMerge(source, direction, comparer, otherSequences); } @@ -5337,7 +5351,7 @@ public static IEnumerable> Split(this IEnumerable< /// A sequence of splits of elements. public static IEnumerable> Split(this IEnumerable source, - TSource separator, IEqualityComparer comparer) + TSource separator, IEqualityComparer? comparer) => MoreEnumerable.Split(source, separator, comparer); /// @@ -5369,7 +5383,7 @@ public static IEnumerable> Split(this IEnumerable< /// A sequence of splits of elements. public static IEnumerable> Split(this IEnumerable source, - TSource separator, IEqualityComparer comparer, int count) + TSource separator, IEqualityComparer? comparer, int count) => MoreEnumerable.Split(source, separator, comparer, count); /// @@ -5494,7 +5508,7 @@ public static IEnumerable Split(this IEnumerable public static IEnumerable Split(this IEnumerable source, - TSource separator, IEqualityComparer comparer, int count, + TSource separator, IEqualityComparer? comparer, int count, Func, TResult> resultSelector) => MoreEnumerable.Split(source, separator, comparer, count, resultSelector); @@ -5548,7 +5562,7 @@ public static bool StartsWith(this IEnumerable first, IEnumerable secon /// of elements at the same index. /// - public static bool StartsWith(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + public static bool StartsWith(this IEnumerable first, IEnumerable second, IEqualityComparer? comparer) => MoreEnumerable.StartsWith(first, second, comparer); } @@ -5776,7 +5790,7 @@ public static IOrderedEnumerable ThenBy(this IOrderedEnumerable s /// A comparer used to define the semantics of element comparison /// An ordered copy of the source sequence - public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, IComparer comparer, OrderByDirection direction) + public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, IComparer? comparer, OrderByDirection direction) => MoreEnumerable.ThenBy(source, keySelector, comparer, direction); } @@ -6354,7 +6368,7 @@ public static partial class ToDictionaryExtension /// public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.ToDictionary(source, comparer); /// @@ -6372,7 +6386,7 @@ public static Dictionary ToDictionary(this IEnumerab /// public static Dictionary ToDictionary(this IEnumerable> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.ToDictionary(source, comparer); } @@ -6410,7 +6424,7 @@ public static HashSet ToHashSet(this IEnumerable sour /// This evaluates the input sequence completely. /// - public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer comparer) + public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer? comparer) => MoreEnumerable.ToHashSet(source, comparer); } @@ -6464,7 +6478,7 @@ public static partial class ToLookupExtension /// public static ILookup ToLookup(this IEnumerable<(TKey Key, TValue Value)> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.ToLookup(source, comparer); /// @@ -6482,7 +6496,7 @@ public static ILookup ToLookup(this IEnumerable<(TKe /// public static ILookup ToLookup(this IEnumerable> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.ToLookup(source, comparer); } @@ -6526,7 +6540,7 @@ public static IEnumerable Trace(this IEnumerable sour /// streams the results. /// - public static IEnumerable Trace(this IEnumerable source, string format) + public static IEnumerable Trace(this IEnumerable source, string? format) => MoreEnumerable.Trace(source, format); /// diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 8675508c1..d872ce192 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Diagnostics; static partial class MoreEnumerable { @@ -45,7 +46,7 @@ static partial class MoreEnumerable public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback) { if (source == null) throw new ArgumentNullException(nameof(source)); - return FallbackIfEmptyImpl(source, 1, fallback, default, default, default, null); + return FallbackIfEmptyImpl(source, 1, fallback, default!, default!, default!, null); } /// @@ -66,7 +67,7 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fa public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2) { if (source == null) throw new ArgumentNullException(nameof(source)); - return FallbackIfEmptyImpl(source, 2, fallback1, fallback2, default, default, null); + return FallbackIfEmptyImpl(source, 2, fallback1, fallback2, default!, default!, null); } /// @@ -89,7 +90,7 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fa public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2, T fallback3) { if (source == null) throw new ArgumentNullException(nameof(source)); - return FallbackIfEmptyImpl(source, 3, fallback1, fallback2, fallback3, default, null); + return FallbackIfEmptyImpl(source, 3, fallback1, fallback2, fallback3, default!, null); } /// @@ -154,12 +155,12 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, IEnu { if (source == null) throw new ArgumentNullException(nameof(source)); if (fallback == null) throw new ArgumentNullException(nameof(fallback)); - return FallbackIfEmptyImpl(source, null, default, default, default, default, fallback); + return FallbackIfEmptyImpl(source, null, default!, default!, default!, default!, fallback); } static IEnumerable FallbackIfEmptyImpl(IEnumerable source, int? count, T fallback1, T fallback2, T fallback3, T fallback4, - IEnumerable fallback) + IEnumerable? fallback) { return source.TryGetCollectionCount() is {} collectionCount ? collectionCount == 0 ? Fallback() : source @@ -183,15 +184,12 @@ IEnumerable _() IEnumerable Fallback() { - switch (count) - { - case null: return fallback; - case int n when n >= 1 && n <= 4: return FallbackOnArgs(); - default: throw new ArgumentOutOfRangeException(nameof(count), count, null); - } + return fallback is {} seq ? seq : FallbackOnArgs(); IEnumerable FallbackOnArgs() { + Debug.Assert(count >= 1 && count <= 4); + yield return fallback1; if (count > 1) yield return fallback2; if (count > 2) yield return fallback3; diff --git a/MoreLinq/FillBackward.cs b/MoreLinq/FillBackward.cs index 1b4cc5514..b7edd86b6 100644 --- a/MoreLinq/FillBackward.cs +++ b/MoreLinq/FillBackward.cs @@ -105,9 +105,9 @@ public static IEnumerable FillBackward(this IEnumerable source, Func FillBackwardImpl(IEnumerable source, Func predicate, Func fillSelector) + static IEnumerable FillBackwardImpl(IEnumerable source, Func predicate, Func? fillSelector) { - List blanks = null; + List? blanks = null; foreach (var item in source) { diff --git a/MoreLinq/FillForward.cs b/MoreLinq/FillForward.cs index f3561d1fd..0577b8053 100644 --- a/MoreLinq/FillForward.cs +++ b/MoreLinq/FillForward.cs @@ -104,24 +104,23 @@ public static IEnumerable FillForward(this IEnumerable source, Func FillForwardImpl(IEnumerable source, Func predicate, Func fillSelector) + static IEnumerable FillForwardImpl(IEnumerable source, Func predicate, Func? fillSelector) { - var seeded = false; - var seed = default(T); + (bool, T) seed = default; + foreach (var item in source) { if (predicate(item)) { - yield return seeded + yield return seed is (true, {} someSeed) ? fillSelector != null - ? fillSelector(item, seed) - : seed + ? fillSelector(item, someSeed) + : someSeed : item; } else { - seeded = true; - seed = item; + seed = (true, item); yield return item; } } diff --git a/MoreLinq/Flatten.cs b/MoreLinq/Flatten.cs index ada470af3..14fd517ea 100644 --- a/MoreLinq/Flatten.cs +++ b/MoreLinq/Flatten.cs @@ -88,7 +88,7 @@ public static IEnumerable Flatten(this IEnumerable source, Func /// is null. - public static IEnumerable Flatten(this IEnumerable source, Func selector) + public static IEnumerable Flatten(this IEnumerable source, Func selector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (selector == null) throw new ArgumentNullException(nameof(selector)); diff --git a/MoreLinq/Fold.cs b/MoreLinq/Fold.cs index bc242d758..ae4337292 100644 --- a/MoreLinq/Fold.cs +++ b/MoreLinq/Fold.cs @@ -23,22 +23,22 @@ namespace MoreLinq static partial class MoreEnumerable { static TResult FoldImpl(IEnumerable source, int count, - Func folder1 = null, - Func folder2 = null, - Func folder3 = null, - Func folder4 = null, - Func folder5 = null, - Func folder6 = null, - Func folder7 = null, - Func folder8 = null, - Func folder9 = null, - Func folder10 = null, - Func folder11 = null, - Func folder12 = null, - Func folder13 = null, - Func folder14 = null, - Func folder15 = null, - Func folder16 = null + Func? folder1 = null, + Func? folder2 = null, + Func? folder3 = null, + Func? folder4 = null, + Func? folder5 = null, + Func? folder6 = null, + Func? folder7 = null, + Func? folder8 = null, + Func? folder9 = null, + Func? folder10 = null, + Func? folder11 = null, + Func? folder12 = null, + Func? folder13 = null, + Func? folder14 = null, + Func? folder15 = null, + Func? folder16 = null ) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -69,22 +69,22 @@ static TResult FoldImpl(IEnumerable source, int count, switch (count) { - case 1: return folder1 (elements[0]); - case 2: return folder2 (elements[0], elements[1]); - case 3: return folder3 (elements[0], elements[1], elements[2]); - case 4: return folder4 (elements[0], elements[1], elements[2], elements[3]); - case 5: return folder5 (elements[0], elements[1], elements[2], elements[3], elements[4]); - case 6: return folder6 (elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]); - case 7: return folder7 (elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6]); - case 8: return folder8 (elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7]); - case 9: return folder9 (elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]); - case 10: return folder10(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9]); - case 11: return folder11(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10]); - case 12: return folder12(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11]); - case 13: return folder13(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12]); - case 14: return folder14(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13]); - case 15: return folder15(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14]); - case 16: return folder16(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14], elements[15]); + case 1: return folder1 !(elements[0]); + case 2: return folder2 !(elements[0], elements[1]); + case 3: return folder3 !(elements[0], elements[1], elements[2]); + case 4: return folder4 !(elements[0], elements[1], elements[2], elements[3]); + case 5: return folder5 !(elements[0], elements[1], elements[2], elements[3], elements[4]); + case 6: return folder6 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]); + case 7: return folder7 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6]); + case 8: return folder8 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7]); + case 9: return folder9 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]); + case 10: return folder10!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9]); + case 11: return folder11!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10]); + case 12: return folder12!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11]); + case 13: return folder13!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12]); + case 14: return folder14!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13]); + case 15: return folder15!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14]); + case 16: return folder16!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14], elements[15]); default: throw new NotSupportedException(); } } diff --git a/MoreLinq/FullJoin.cs b/MoreLinq/FullJoin.cs index 5f3f782c2..251cd6ec5 100644 --- a/MoreLinq/FullJoin.cs +++ b/MoreLinq/FullJoin.cs @@ -113,7 +113,7 @@ public static IEnumerable FullJoin( Func firstSelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return first.FullJoin(second, @@ -218,7 +218,7 @@ public static IEnumerable FullJoin( Func firstSelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/GroupAdjacent.cs b/MoreLinq/GroupAdjacent.cs index 3523bb690..f19eeeb9a 100644 --- a/MoreLinq/GroupAdjacent.cs +++ b/MoreLinq/GroupAdjacent.cs @@ -21,7 +21,6 @@ namespace MoreLinq using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; - using System.Diagnostics; using System.Linq; static partial class MoreEnumerable @@ -83,7 +82,7 @@ public static IEnumerable> GroupAdjacent public static IEnumerable> GroupAdjacent( this IEnumerable source, Func keySelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -160,7 +159,7 @@ public static IEnumerable> GroupAdjacent source, Func keySelector, Func elementSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -246,7 +245,7 @@ public static IEnumerable GroupAdjacent( this IEnumerable source, Func keySelector, Func, TResult> resultSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -265,43 +264,39 @@ static IEnumerable GroupAdjacentImpl( Func, TResult> resultSelector, IEqualityComparer comparer) { - Debug.Assert(source != null); - Debug.Assert(keySelector != null); - Debug.Assert(elementSelector != null); - Debug.Assert(resultSelector != null); - Debug.Assert(comparer != null); - using var iterator = source.GetEnumerator(); - var group = default(TKey); - var members = (List) null; + (TKey, List) group = default; while (iterator.MoveNext()) { var key = keySelector(iterator.Current); var element = elementSelector(iterator.Current); - if (members != null && comparer.Equals(group, key)) - { - members.Add(element); - } - else + + if (group is ({} k, {} members)) { - if (members != null) - yield return resultSelector(group, members); - group = key; - members = new List { element }; + if (comparer.Equals(k, key)) + { + members.Add(element); + continue; + } + else + { + yield return resultSelector(k, members); + } } + + group = (key, new List { element }); } - if (members != null) - yield return resultSelector(group, members); + { + if (group is ({} k, {} members)) + yield return resultSelector(k, members); + } } - static IGrouping CreateGroupAdjacentGrouping(TKey key, IList members) - { - Debug.Assert(members != null); - return Grouping.Create(key, members.IsReadOnly ? members : new ReadOnlyCollection(members)); - } + static IGrouping CreateGroupAdjacentGrouping(TKey key, IList members) => + Grouping.Create(key, members.IsReadOnly ? members : new ReadOnlyCollection(members)); static class Grouping { @@ -318,7 +313,6 @@ sealed class Grouping : IGrouping public Grouping(TKey key, IEnumerable members) { - Debug.Assert(members != null); Key = key; _members = members; } diff --git a/MoreLinq/IndexBy.cs b/MoreLinq/IndexBy.cs index 1a407b07c..64fad32d3 100644 --- a/MoreLinq/IndexBy.cs +++ b/MoreLinq/IndexBy.cs @@ -69,7 +69,7 @@ public static IEnumerable> IndexBy( this IEnumerable source, Func keySelector, - IEqualityComparer comparer) => + IEqualityComparer? comparer) => from e in source.ScanBy(keySelector, k => (Index: -1, Item: default(TSource)), (s, k, e) => (s.Index + 1, e), comparer) select new KeyValuePair(e.Value.Index, e.Value.Item); } diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 3ffcb26c0..a85c20977 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -53,7 +53,7 @@ public static IEnumerable Interleave(this IEnumerable sequence, params return _(); IEnumerable _() { var sequences = new[] { sequence }.Concat(otherSequences); - var enumerators = new List>(); + var enumerators = new List?>(); try { diff --git a/MoreLinq/Lag.cs b/MoreLinq/Lag.cs index 51cdf5808..533bbeac7 100644 --- a/MoreLinq/Lag.cs +++ b/MoreLinq/Lag.cs @@ -38,7 +38,7 @@ public static partial class MoreEnumerable public static IEnumerable Lag(this IEnumerable source, int offset, Func resultSelector) { - return Lag(source, offset, default, resultSelector); + return Lag(source, offset, default!, resultSelector); } /// diff --git a/MoreLinq/Lead.cs b/MoreLinq/Lead.cs index a354aea30..ba627088c 100644 --- a/MoreLinq/Lead.cs +++ b/MoreLinq/Lead.cs @@ -39,7 +39,7 @@ public static partial class MoreEnumerable public static IEnumerable Lead(this IEnumerable source, int offset, Func resultSelector) { - return Lead(source, offset, default, resultSelector); + return Lead(source, offset, default!, resultSelector); } /// diff --git a/MoreLinq/LeftJoin.cs b/MoreLinq/LeftJoin.cs index d4af068fb..6648dcefc 100644 --- a/MoreLinq/LeftJoin.cs +++ b/MoreLinq/LeftJoin.cs @@ -103,7 +103,7 @@ public static IEnumerable LeftJoin( Func keySelector, Func firstSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return first.LeftJoin(second, @@ -198,7 +198,7 @@ public static IEnumerable LeftJoin( Func secondKeySelector, Func firstSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/ListLike.cs b/MoreLinq/ListLike.cs index 289f02b38..d0d4c44de 100644 --- a/MoreLinq/ListLike.cs +++ b/MoreLinq/ListLike.cs @@ -36,7 +36,7 @@ static class ListLike public static IListLike ToListLike(this IEnumerable source) => source.TryAsListLike() ?? new List(source.ToList()); - public static IListLike TryAsListLike(this IEnumerable source) => + public static IListLike? TryAsListLike(this IEnumerable source) => source switch { null => throw new ArgumentNullException(nameof(source)), diff --git a/MoreLinq/Lookup.cs b/MoreLinq/Lookup.cs index 7c873a64e..c576d99a2 100644 --- a/MoreLinq/Lookup.cs +++ b/MoreLinq/Lookup.cs @@ -24,6 +24,8 @@ // SOFTWARE. #endregion +#nullable disable + namespace MoreLinq { using System; diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index ce31a9ec9..faf269b4d 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -20,6 +20,7 @@ namespace MoreLinq using System; using System.Collections; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; /// @@ -87,6 +88,7 @@ public static T First(this IExtremaEnumerable source) /// otherwise, the first element in source. /// + [return: MaybeNull] public static T FirstOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -123,6 +125,7 @@ public static T Last(this IExtremaEnumerable source) /// otherwise, the last element in source. /// + [return: MaybeNull] public static T LastOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -161,6 +164,7 @@ public static T Single(this IExtremaEnumerable source) /// if the sequence contains no elements. /// + [return: MaybeNull] public static T SingleOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -207,7 +211,7 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// or is null public static IExtremaEnumerable MaxBy(this IEnumerable source, - Func selector, IComparer comparer) + Func selector, IComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (selector == null) throw new ArgumentNullException(nameof(selector)); @@ -253,27 +257,38 @@ public IEnumerable TakeLast(int count) => static class Extrema { - public static readonly Extrema , T> First = new FirstExtrema(); - public static readonly Extrema, T> Last = new LastExtrema(); + public static readonly Extrema? , T> First = new FirstExtrema(); + public static readonly Extrema?, T> Last = new LastExtrema(); - sealed class FirstExtrema : Extrema, T> + sealed class FirstExtrema : Extrema?, T> { - protected override IEnumerable GetSomeEnumerable(List store) => store; - protected override int Count(List store) => store?.Count ?? 0; - protected override void Push(ref List store, T item) => (store ??= new List()).Add(item); - protected override bool TryPop(ref List store) => false; + public override List? New() => null; + public override void Restart(ref List? store) => store = null; + public override IEnumerable GetEnumerable(List? store) => store ?? Enumerable.Empty(); + + public override void Add(ref List? store, int? limit, T item) + { + if (limit == null || store is null || store.Count < limit) + (store ??= new List()).Add(item); + } } - sealed class LastExtrema : Extrema, T> + sealed class LastExtrema : Extrema?, T> { - protected override IEnumerable GetSomeEnumerable(Queue store) => store; - protected override int Count(Queue store) => store?.Count ?? 0; - protected override void Push(ref Queue store, T item) => (store ??= new Queue()).Enqueue(item); - protected override bool TryPop(ref Queue store) { store.Dequeue(); return true; } + public override Queue? New() => null; + public override void Restart(ref Queue? store) => store = null; + public override IEnumerable GetEnumerable(Queue? store) => store ?? Enumerable.Empty(); + + public override void Add(ref Queue? store, int? limit, T item) + { + if (limit is {} n && store is {} queue && queue.Count == n) + queue.Dequeue(); + (store ??= new Queue()).Enqueue(item); + } } } - sealed class Extremum : Extrema<(bool HasValue, T Value), T> + sealed class Extremum : Extrema<(bool, T), T> { public static readonly Extrema<(bool, T), T> First = new Extremum(false); public static readonly Extrema<(bool, T), T> Last = new Extremum(true); @@ -281,19 +296,17 @@ sealed class Extremum : Extrema<(bool HasValue, T Value), T> readonly bool _poppable; Extremum(bool poppable) => _poppable = poppable; - protected override IEnumerable GetSomeEnumerable((bool HasValue, T Value) store) => - Enumerable.Repeat(store.Value, 1); + public override (bool, T) New() => default; + public override void Restart(ref (bool, T) store) => store = default; - protected override int Count((bool HasValue, T Value) store) => store.HasValue ? 1 : 0; - protected override void Push(ref (bool, T) store, T item) => store = (true, item); + public override IEnumerable GetEnumerable((bool, T) store) => + store is (true, var item) ? Enumerable.Repeat(item, 1) : Enumerable.Empty(); - protected override bool TryPop(ref (bool, T) store) + public override void Add(ref (bool, T) store, int? limit, T item) { - if (!_poppable) - return false; - - Restart(ref store); - return true; + if (!_poppable && store is (true, _)) + return; + store = (true, item); } } } @@ -346,25 +359,10 @@ IEnumerable Extrema() abstract class Extrema { - public virtual TStore New() => default; - public virtual void Restart(ref TStore store) => store = default; - - public void Add(ref TStore store, int? limit, T item) - { - if (limit == null || Count(store) < limit || TryPop(ref store)) - Push(ref store, item); - } - - protected abstract int Count(TStore store); - protected abstract void Push(ref TStore store, T item); - protected abstract bool TryPop(ref TStore store); - - public virtual IEnumerable GetEnumerable(TStore store) => - Count(store) > 0 - ? GetSomeEnumerable(store) - : Enumerable.Empty(); - - protected abstract IEnumerable GetSomeEnumerable(TStore store); + public abstract TStore New(); + public abstract void Restart(ref TStore store); + public abstract IEnumerable GetEnumerable(TStore store); + public abstract void Add(ref TStore store, int? limit, T item); } } } diff --git a/MoreLinq/MinBy.cs b/MoreLinq/MinBy.cs index ed016132b..3a06bcb98 100644 --- a/MoreLinq/MinBy.cs +++ b/MoreLinq/MinBy.cs @@ -62,7 +62,7 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// or is null public static IExtremaEnumerable MinBy(this IEnumerable source, - Func selector, IComparer comparer) + Func selector, IComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (selector == null) throw new ArgumentNullException(nameof(selector)); diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 22f0c7434..ee4dfcdba 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -121,6 +121,7 @@ MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 8 + enable true portable true @@ -245,4 +246,11 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/MoreLinq/OrderBy.cs b/MoreLinq/OrderBy.cs index 4e5ce8565..02820c4bb 100644 --- a/MoreLinq/OrderBy.cs +++ b/MoreLinq/OrderBy.cs @@ -49,7 +49,7 @@ public static IOrderedEnumerable OrderBy(this IEnumerable source, /// A comparer used to define the semantics of element comparison /// An ordered copy of the source sequence - public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer, OrderByDirection direction) + public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer? comparer, OrderByDirection direction) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -84,7 +84,7 @@ public static IOrderedEnumerable ThenBy(this IOrderedEnumerable s /// A comparer used to define the semantics of element comparison /// An ordered copy of the source sequence - public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, IComparer comparer, OrderByDirection direction) + public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, IComparer? comparer, OrderByDirection direction) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); diff --git a/MoreLinq/OrderedMerge.cs b/MoreLinq/OrderedMerge.cs index bce2a5ac3..bb4551ec7 100644 --- a/MoreLinq/OrderedMerge.cs +++ b/MoreLinq/OrderedMerge.cs @@ -68,7 +68,7 @@ public static IEnumerable OrderedMerge( public static IEnumerable OrderedMerge( this IEnumerable first, IEnumerable second, - IComparer comparer) + IComparer? comparer) { return OrderedMerge(first, second, e => e, f => f, s => s, (a, _) => a, comparer); } @@ -178,7 +178,7 @@ public static IEnumerable OrderedMerge( Func firstSelector, Func secondSelector, Func bothSelector, - IComparer comparer) + IComparer? comparer) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); // Argument name changes to 'firstKeySelector' return OrderedMerge(first, second, keySelector, keySelector, firstSelector, secondSelector, bothSelector, comparer); @@ -272,7 +272,7 @@ public static IEnumerable OrderedMerge( Func firstSelector, Func secondSelector, Func bothSelector, - IComparer comparer) + IComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/Pad.cs b/MoreLinq/Pad.cs index d45f2f380..cfe9c6921 100644 --- a/MoreLinq/Pad.cs +++ b/MoreLinq/Pad.cs @@ -48,7 +48,7 @@ static partial class MoreEnumerable public static IEnumerable Pad(this IEnumerable source, int width) { - return Pad(source, width, default(TSource)); + return Pad(source, width, default(TSource)!); } /// @@ -111,13 +111,12 @@ public static IEnumerable Pad(this IEnumerable source if (source == null) throw new ArgumentNullException(nameof(source)); if (paddingSelector == null) throw new ArgumentNullException(nameof(paddingSelector)); if (width < 0) throw new ArgumentException(null, nameof(width)); - return PadImpl(source, width, default, paddingSelector); + return PadImpl(source, width, default!, paddingSelector); } static IEnumerable PadImpl(IEnumerable source, - int width, T padding, Func paddingSelector) + int width, T padding, Func? paddingSelector) { - Debug.Assert(source != null); Debug.Assert(width >= 0); var count = 0; diff --git a/MoreLinq/PadStart.cs b/MoreLinq/PadStart.cs index a92ab2ad0..e630f3d7d 100644 --- a/MoreLinq/PadStart.cs +++ b/MoreLinq/PadStart.cs @@ -47,7 +47,7 @@ static partial class MoreEnumerable public static IEnumerable PadStart(this IEnumerable source, int width) { - return PadStart(source, width, default(TSource)); + return PadStart(source, width, default(TSource)!); } /// @@ -112,11 +112,11 @@ public static IEnumerable PadStart(this IEnumerable s if (source == null) throw new ArgumentNullException(nameof(source)); if (paddingSelector == null) throw new ArgumentNullException(nameof(paddingSelector)); if (width < 0) throw new ArgumentException(null, nameof(width)); - return PadStartImpl(source, width, default, paddingSelector); + return PadStartImpl(source, width, default!, paddingSelector); } static IEnumerable PadStartImpl(IEnumerable source, - int width, T padding, Func paddingSelector) + int width, T padding, Func? paddingSelector) { return source.TryGetCollectionCount() is {} collectionCount diff --git a/MoreLinq/PartialSort.cs b/MoreLinq/PartialSort.cs index eb09b545e..60076133a 100644 --- a/MoreLinq/PartialSort.cs +++ b/MoreLinq/PartialSort.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; using System.Linq; static partial class MoreEnumerable @@ -82,7 +81,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// public static IEnumerable PartialSort(this IEnumerable source, - int count, IComparer comparer) + int count, IComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); return PartialSortByImpl(source, count, null, null, comparer); @@ -107,7 +106,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// public static IEnumerable PartialSort(this IEnumerable source, - int count, IComparer comparer, OrderByDirection direction) + int count, IComparer? comparer, OrderByDirection direction) { comparer ??= Comparer.Default; if (direction == OrderByDirection.Descending) @@ -181,7 +180,7 @@ public static IEnumerable PartialSortBy( public static IEnumerable PartialSortBy( this IEnumerable source, int count, Func keySelector, - IComparer comparer) + IComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -210,7 +209,7 @@ public static IEnumerable PartialSortBy( public static IEnumerable PartialSortBy( this IEnumerable source, int count, Func keySelector, - IComparer comparer, + IComparer? comparer, OrderByDirection direction) { comparer ??= Comparer.Default; @@ -221,41 +220,42 @@ public static IEnumerable PartialSortBy( static IEnumerable PartialSortByImpl( IEnumerable source, int count, - Func keySelector, - IComparer keyComparer, IComparer comparer) + Func? keySelector, + IComparer? keyComparer, + IComparer? comparer) { - Debug.Assert(source != null); - var keys = keySelector != null ? new List(count) : null; var top = new List(count); + int? Insert(List list, T item, IComparer? comparer) + { + var i = list.BinarySearch(item, comparer); + if (i < 0 && (i = ~i) >= count) + return null; + if (list.Count == count) + list.RemoveAt(count - 1); + list.Insert(i, item); + return i; + } + foreach (var item in source) { - int i; - var key = default(TKey); if (keys != null) { - key = keySelector(item); - i = keys.BinarySearch(key, keyComparer); + var key = keySelector!(item); + if (Insert(keys, key, keyComparer) is {} i) + { + if (top.Count == count) + top.RemoveAt(count - 1); + top.Insert(i, item); + } } else { - i = top.BinarySearch(item, comparer); - } - - if (i < 0 && (i = ~i) >= count) - continue; - - if (top.Count == count) - { - keys?.RemoveAt(top.Count - 1); - top.RemoveAt(top.Count - 1); + _ = Insert(top, item, comparer); } // TODO Stable sorting - - keys?.Insert(i, key); - top.Insert(i, item); } // ReSharper disable once LoopCanBeConvertedToQuery diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 0cda564ab..be4c637e3 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -174,11 +174,11 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, - TKey key, IEqualityComparer comparer, + TKey key, IEqualityComparer? comparer, Func, IEnumerable>, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return PartitionImpl(source, 1, key, default, default, comparer, + return PartitionImpl(source, 1, key, default!, default!, comparer, (a, b, c, rest) => resultSelector(a, rest)); } @@ -232,11 +232,11 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, - TKey key1, TKey key2, IEqualityComparer comparer, + TKey key1, TKey key2, IEqualityComparer? comparer, Func, IEnumerable, IEnumerable>, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return PartitionImpl(source, 2, key1, key2, default, comparer, + return PartitionImpl(source, 2, key1, key2, default!, comparer, (a, b, c, rest) => resultSelector(a, b, rest)); } @@ -292,12 +292,12 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, - TKey key1, TKey key2, TKey key3, IEqualityComparer comparer, + TKey key1, TKey key2, TKey key3, IEqualityComparer? comparer, Func, IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) => PartitionImpl(source, 3, key1, key2, key3, comparer, resultSelector); static TResult PartitionImpl(IEnumerable> source, - int count, TKey key1, TKey key2, TKey key3, IEqualityComparer comparer, + int count, TKey key1, TKey key2, TKey key3, IEqualityComparer? comparer, Func, IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) { Debug.Assert(count > 0 && count <= 3); @@ -307,7 +307,7 @@ static TResult PartitionImpl(IEnumerable.Default; - List> etc = null; + List>? etc = null; var groups = new[] { diff --git a/MoreLinq/PendNode.cs b/MoreLinq/PendNode.cs index 28ebe8c42..9da48e859 100644 --- a/MoreLinq/PendNode.cs +++ b/MoreLinq/PendNode.cs @@ -62,7 +62,7 @@ sealed class Source : PendNode public IEnumerator GetEnumerator() { var i = 0; - T[] concats = null; // Array for > 4 concatenations + T[]? concats = null; // Array for > 4 concatenations var concat1 = default(T); // Slots for up to 4 concatenations var concat2 = default(T); var concat3 = default(T); @@ -108,10 +108,10 @@ public IEnumerator GetEnumerator() if (concats == null) { - if (i == 4) { yield return concat4; i--; } - if (i == 3) { yield return concat3; i--; } - if (i == 2) { yield return concat2; i--; } - if (i == 1) { yield return concat1; i--; } + if (i == 4) { yield return concat4!; i--; } + if (i == 3) { yield return concat3!; i--; } + if (i == 2) { yield return concat2!; i--; } + if (i == 1) { yield return concat1!; i--; } yield break; } diff --git a/MoreLinq/Permutations.cs b/MoreLinq/Permutations.cs index afede3963..3c96161fd 100644 --- a/MoreLinq/Permutations.cs +++ b/MoreLinq/Permutations.cs @@ -73,6 +73,8 @@ class PermutationEnumerator : IEnumerator> IEnumerator _generatorIterator; bool _hasMoreResults; + IList? _current; + public PermutationEnumerator(IEnumerable valueSet) { _valueSet = valueSet.ToArray(); @@ -81,30 +83,34 @@ public PermutationEnumerator(IEnumerable valueSet) // 1) for empty sets and sets of cardinality 1, there exists only a single permutation. // 2) for sets larger than 1 element, the number of nested loops needed is: set.Count-1 _generator = NestedLoops(NextPermutation, Enumerable.Range(2, Math.Max(0, _valueSet.Count - 1))); - Reset(); + Reset(ref _current, ref _generatorIterator, ref _hasMoreResults); } - public void Reset() + public void Reset() => + Reset(ref _current, ref _generatorIterator, ref _hasMoreResults); + + void Reset(ref IList? current, ref IEnumerator generatorIterator, ref bool hasMoreResults) { - _generatorIterator?.Dispose(); + current = null; + generatorIterator?.Dispose(); // restore lexographic ordering of the permutation indexes for (var i = 0; i < _permutation.Length; i++) _permutation[i] = i; // start a newiteration over the nested loop generator - _generatorIterator = _generator.GetEnumerator(); + generatorIterator = _generator.GetEnumerator(); // we must advance the nestedloop iterator to the initial element, // this ensures that we only ever produce N!-1 calls to NextPermutation() - _generatorIterator.MoveNext(); - _hasMoreResults = true; // there's always at least one permutation: the original set itself + generatorIterator.MoveNext(); + hasMoreResults = true; // there's always at least one permutation: the original set itself } - public IList Current { get; private set; } + public IList Current => _current!; object IEnumerator.Current => Current; public bool MoveNext() { - Current = PermuteValueSet(); + _current = PermuteValueSet(); // check if more permutation left to enumerate var prevResult = _hasMoreResults; _hasMoreResults = _generatorIterator.MoveNext(); diff --git a/MoreLinq/Random.cs b/MoreLinq/Random.cs index eaf66bca1..33566a3db 100644 --- a/MoreLinq/Random.cs +++ b/MoreLinq/Random.cs @@ -230,7 +230,7 @@ sealed class GlobalRandom : Random public static readonly Random Instance = new GlobalRandom(); static int _seed = Environment.TickCount; - [ThreadStatic] static Random _threadRandom; + [ThreadStatic] static Random? _threadRandom; static Random ThreadRandom => _threadRandom ??= new Random(Interlocked.Increment(ref _seed)); GlobalRandom() { } diff --git a/MoreLinq/Rank.cs b/MoreLinq/Rank.cs index b0d218276..82b2d6076 100644 --- a/MoreLinq/Rank.cs +++ b/MoreLinq/Rank.cs @@ -72,7 +72,7 @@ public static IEnumerable RankBy(this IEnumerable s /// An object that defines the comparison semantics for keys used to rank items /// A sequence of position integers representing the ranks of the corresponding items in the sequence - public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer comparer) + public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); diff --git a/MoreLinq/Reactive/Observable.cs b/MoreLinq/Reactive/Observable.cs index 405958a0d..0c28c9467 100644 --- a/MoreLinq/Reactive/Observable.cs +++ b/MoreLinq/Reactive/Observable.cs @@ -42,7 +42,7 @@ static partial class Observable /// The subscription, which when disposed, will unsubscribe /// from . - public static IDisposable Subscribe(this IObservable source, Action onNext, Action onError = null, Action onCompleted = null) => + public static IDisposable Subscribe(this IObservable source, Action onNext, Action? onError = null, Action? onCompleted = null) => source == null ? throw new ArgumentNullException(nameof(source)) : source.Subscribe(Delegate.Observer(onNext, onError, onCompleted)); diff --git a/MoreLinq/Reactive/Subject.cs b/MoreLinq/Reactive/Subject.cs index cbb1a9764..2f01e8047 100644 --- a/MoreLinq/Reactive/Subject.cs +++ b/MoreLinq/Reactive/Subject.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Reactive { using System; @@ -23,9 +25,9 @@ namespace MoreLinq.Reactive sealed class Subject : IObservable, IObserver { - List> _observers; + List>? _observers; bool _completed; - Exception _error; + Exception? _error; bool HasObservers => (_observers?.Count ?? 0) > 0; List> Observers => _observers ??= new List>(); @@ -66,7 +68,7 @@ public IDisposable Subscribe(IObserver observer) if (observers[i] == observer) { if (_shouldDeleteObserver) - observers[i] = null; + observers[i] = null!; else observers.RemoveAt(i); break; @@ -114,7 +116,7 @@ public void OnNext(T value) } public void OnError(Exception error) => - OnFinality(ref _error, error, (observer, err) => observer.OnError(err)); + OnFinality(ref _error, error, (observer, err) => observer.OnError(err!)); public void OnCompleted() => OnFinality(ref _completed, true, (observer, _) => observer.OnCompleted()); diff --git a/MoreLinq/RightJoin.cs b/MoreLinq/RightJoin.cs index 3c7fa1738..babbe28c1 100644 --- a/MoreLinq/RightJoin.cs +++ b/MoreLinq/RightJoin.cs @@ -102,7 +102,7 @@ public static IEnumerable RightJoin( Func keySelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return first.RightJoin(second, @@ -197,7 +197,7 @@ public static IEnumerable RightJoin( Func secondKeySelector, Func secondSelector, Func bothSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/RunLengthEncode.cs b/MoreLinq/RunLengthEncode.cs index d60b9f131..566e97a8a 100644 --- a/MoreLinq/RunLengthEncode.cs +++ b/MoreLinq/RunLengthEncode.cs @@ -45,7 +45,7 @@ public static IEnumerable> RunLengthEncode(this IEnumera /// The comparer used to identify equivalent items /// A sequence of KeyValuePair{T,int} where they key is the element and the value is the occurrence count - public static IEnumerable> RunLengthEncode(this IEnumerable sequence, IEqualityComparer comparer) + public static IEnumerable> RunLengthEncode(this IEnumerable sequence, IEqualityComparer? comparer) { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); diff --git a/MoreLinq/ScanBy.cs b/MoreLinq/ScanBy.cs index 2f5de2e03..b91a02dc4 100644 --- a/MoreLinq/ScanBy.cs +++ b/MoreLinq/ScanBy.cs @@ -78,7 +78,7 @@ public static IEnumerable> ScanBy keySelector, Func seedSelector, Func accumulator, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); @@ -89,46 +89,29 @@ public static IEnumerable> ScanBy> _(IEqualityComparer comparer) { - var stateByKey = new Dictionary(comparer); - var prevKey = (HasValue: false, Value: default(TKey)); - var nullKeyState = (HasValue: false, Value: default(TState)); - var state = default(TState); + var stateByKey = new Collections.Dictionary(comparer); - bool TryGetState(TKey key, out TState value) - { - if (key == null) - { - value = nullKeyState.Value; - return nullKeyState.HasValue; - } - - return stateByKey.TryGetValue(key, out value); - } + (bool, TKey, TState) prev = default; foreach (var item in source) { var key = keySelector(item); - if (!(prevKey.HasValue - // key same as the previous? then re-use the state - && comparer.GetHashCode(prevKey.Value) == comparer.GetHashCode(key) - && comparer.Equals(prevKey.Value, key) - // otherwise try & find state of the key - || TryGetState(key, out state))) - { - state = seedSelector(key); - } + var state = // key same as the previous? then re-use the state + prev is (true, {} pk, {} ps) + && comparer.GetHashCode(pk) == comparer.GetHashCode(key) + && comparer.Equals(pk, key) ? ps + : // otherwise try & find state of the key + stateByKey.TryGetValue(key, out var ns) ? ns + : seedSelector(key); state = accumulator(state, key, item); - if (key != null) - stateByKey[key] = state; - else - nullKeyState = (true, state); + stateByKey[key] = state; yield return new KeyValuePair(key, state); - prevKey = (true, key); + prev = (true, key, state); } } } diff --git a/MoreLinq/SequenceException.cs b/MoreLinq/SequenceException.cs index 07fcca571..65a025c92 100644 --- a/MoreLinq/SequenceException.cs +++ b/MoreLinq/SequenceException.cs @@ -46,7 +46,7 @@ public SequenceException() : /// /// A message that describes the error. - public SequenceException(string message) : + public SequenceException(string? message) : this(message, null) { } /// @@ -57,7 +57,7 @@ public SequenceException(string message) : /// A message that describes the error. /// The exception that is the cause of the current exception. - public SequenceException(string message, Exception innerException) : + public SequenceException(string? message, Exception? innerException) : base(string.IsNullOrEmpty(message) ? DefaultMessage : message, innerException) { } #if !NO_EXCEPTION_SERIALIZATION diff --git a/MoreLinq/SortedMerge.cs b/MoreLinq/SortedMerge.cs index 7b68a2327..b19273c15 100644 --- a/MoreLinq/SortedMerge.cs +++ b/MoreLinq/SortedMerge.cs @@ -65,7 +65,7 @@ public static IEnumerable SortedMerge(this IEnumerableA variable argument array of zero or more other sequences to merge with /// A merged, order-preserving sequence containing al of the elements of the original sequences - public static IEnumerable SortedMerge(this IEnumerable source, OrderByDirection direction, IComparer comparer, params IEnumerable[] otherSequences) + public static IEnumerable SortedMerge(this IEnumerable source, OrderByDirection direction, IComparer? comparer, params IEnumerable[] otherSequences) { if (source == null) throw new ArgumentNullException(nameof(source)); if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); diff --git a/MoreLinq/Split.cs b/MoreLinq/Split.cs index 69b1c5f30..86d6e184b 100644 --- a/MoreLinq/Split.cs +++ b/MoreLinq/Split.cs @@ -108,7 +108,7 @@ public static IEnumerable Split(this IEnumerableA sequence of splits of elements. public static IEnumerable> Split(this IEnumerable source, - TSource separator, IEqualityComparer comparer) + TSource separator, IEqualityComparer? comparer) { return Split(source, separator, comparer, int.MaxValue); } @@ -127,7 +127,7 @@ public static IEnumerable> Split(this IEnumerable< /// A sequence of splits of elements. public static IEnumerable> Split(this IEnumerable source, - TSource separator, IEqualityComparer comparer, int count) + TSource separator, IEqualityComparer? comparer, int count) { return Split(source, separator, comparer, count, s => s); } @@ -175,7 +175,7 @@ public static IEnumerable Split(this IEnumerable public static IEnumerable Split(this IEnumerable source, - TSource separator, IEqualityComparer comparer, int count, + TSource separator, IEqualityComparer? comparer, int count, Func, TResult> resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -275,7 +275,7 @@ public static IEnumerable Split(this IEnumerable items = null; + List? items = null; foreach (var item in source) { diff --git a/MoreLinq/StartsWith.cs b/MoreLinq/StartsWith.cs index f6f298005..a15c17479 100644 --- a/MoreLinq/StartsWith.cs +++ b/MoreLinq/StartsWith.cs @@ -68,7 +68,7 @@ public static bool StartsWith(this IEnumerable first, IEnumerable secon /// of elements at the same index. /// - public static bool StartsWith(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + public static bool StartsWith(this IEnumerable first, IEnumerable second, IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/ToArrayByIndex.cs b/MoreLinq/ToArrayByIndex.cs index 4bbad61ab..ad0f503c5 100644 --- a/MoreLinq/ToArrayByIndex.cs +++ b/MoreLinq/ToArrayByIndex.cs @@ -121,7 +121,7 @@ public static TResult[] ToArrayByIndex(this IEnumerable source, if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); var lastIndex = -1; - var indexed = (List>) null; + var indexed = (List>?) null; List> Indexed() => indexed ??= new List>(); foreach (var e in source) diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index fa2719739..fb69640e4 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -41,7 +41,7 @@ static partial class MoreEnumerable public static TTable ToDataTable(this IEnumerable source, TTable table) where TTable : DataTable { - return ToDataTable(source, table, null); + return ToDataTable(source, table, EmptyArray>>.Value); } /// @@ -98,6 +98,10 @@ public static TTable ToDataTable(this IEnumerable source, TTable t if (source == null) throw new ArgumentNullException(nameof(source)); if (table == null) throw new ArgumentNullException(nameof(table)); + // TODO disallow null for "expressions" in next major update + + expressions ??= EmptyArray>>.Value; + var members = PrepareMemberInfos(expressions).ToArray(); members = BuildOrBindSchema(table, members); var shredder = CreateShredder(members); @@ -132,7 +136,7 @@ static IEnumerable PrepareMemberInfos(ICollection(this IEnumerable source static string ToDelimitedStringImpl(IEnumerable source, string delimiter, Func append) { - Debug.Assert(source != null); - Debug.Assert(delimiter != null); - Debug.Assert(append != null); - var sb = new StringBuilder(); var i = 0; diff --git a/MoreLinq/ToDictionary.cs b/MoreLinq/ToDictionary.cs index 82a541317..925dbea7f 100644 --- a/MoreLinq/ToDictionary.cs +++ b/MoreLinq/ToDictionary.cs @@ -53,7 +53,7 @@ public static Dictionary ToDictionary(this IEnumerab /// public static Dictionary ToDictionary(this IEnumerable> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToDictionary(e => e.Key, e => e.Value, comparer); @@ -90,7 +90,7 @@ public static Dictionary ToDictionary(this IEnumerab /// public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToDictionary(e => e.Key, e => e.Value, comparer); diff --git a/MoreLinq/ToHashSet.cs b/MoreLinq/ToHashSet.cs index ef6df582a..7301ed357 100644 --- a/MoreLinq/ToHashSet.cs +++ b/MoreLinq/ToHashSet.cs @@ -53,7 +53,7 @@ public static HashSet ToHashSet(this IEnumerable sour /// This evaluates the input sequence completely. /// - public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer comparer) + public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); return new HashSet(source, comparer); diff --git a/MoreLinq/ToLookup.cs b/MoreLinq/ToLookup.cs index 47b8115f6..16436feb1 100644 --- a/MoreLinq/ToLookup.cs +++ b/MoreLinq/ToLookup.cs @@ -53,7 +53,7 @@ public static ILookup ToLookup(this IEnumerable public static ILookup ToLookup(this IEnumerable> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToLookup(e => e.Key, e => e.Value, comparer); @@ -90,7 +90,7 @@ public static ILookup ToLookup(this IEnumerable<(TKe /// public static ILookup ToLookup(this IEnumerable<(TKey Key, TValue Value)> source, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToLookup(e => e.Key, e => e.Value, comparer); diff --git a/MoreLinq/Trace.cs b/MoreLinq/Trace.cs index 8fac03eda..ff9438a3f 100644 --- a/MoreLinq/Trace.cs +++ b/MoreLinq/Trace.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; static partial class MoreEnumerable { @@ -38,7 +37,7 @@ static partial class MoreEnumerable public static IEnumerable Trace(this IEnumerable source) { - return Trace(source, (string) null); + return Trace(source, (string?) null); } /// @@ -59,7 +58,7 @@ public static IEnumerable Trace(this IEnumerable sour /// streams the results. /// - public static IEnumerable Trace(this IEnumerable source, string format) + public static IEnumerable Trace(this IEnumerable source, string? format) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -93,9 +92,6 @@ public static IEnumerable Trace(this IEnumerable sour static IEnumerable TraceImpl(IEnumerable source, Func formatter) { - Debug.Assert(source != null); - Debug.Assert(formatter != null); - return source #if !NO_TRACING .Pipe(x => System.Diagnostics.Trace.WriteLine(formatter(x))) diff --git a/MoreLinq/Transpose.cs b/MoreLinq/Transpose.cs index 0234c9224..54ff80c14 100644 --- a/MoreLinq/Transpose.cs +++ b/MoreLinq/Transpose.cs @@ -58,7 +58,7 @@ public static IEnumerable> Transpose(this IEnumerable> _() { - var enumerators = source.Select(e => e.GetEnumerator()).Acquire(); + IEnumerator?[] enumerators = source.Select(e => e.GetEnumerator()).Acquire(); try { @@ -68,16 +68,17 @@ public static IEnumerable> Transpose(this IEnumerable(params T[] args); static IEnumerable ZipImpl( - IEnumerable s1, - IEnumerable s2, - IEnumerable s3, - IEnumerable s4, + IEnumerable s1, + IEnumerable s2, + IEnumerable? s3, + IEnumerable? s4, Func resultSelector, int limit, - Folder errorSelector = null) + Folder? errorSelector = null) { - IEnumerator e1 = null; - IEnumerator e2 = null; - IEnumerator e3 = null; - IEnumerator e4 = null; + IEnumerator? e1 = null; + IEnumerator? e2 = null; + IEnumerator? e3 = null; + IEnumerator? e4 = null; var terminations = 0; try @@ -69,10 +69,10 @@ static IEnumerable ZipImpl( e4?.Dispose(); } - T Read(ref IEnumerator e, int n) + T Read(ref IEnumerator? e, int n) { if (e == null || terminations > limit) - return default; + return default!; T value; if (e.MoveNext()) @@ -84,7 +84,7 @@ T Read(ref IEnumerator e, int n) e.Dispose(); e = null; terminations++; - value = default; + value = default!; } if (errorSelector != null && terminations > 0 && terminations < n) diff --git a/MoreLinq/ZipShortest.cs b/MoreLinq/ZipShortest.cs index 06920e2df..0d8039eca 100644 --- a/MoreLinq/ZipShortest.cs +++ b/MoreLinq/ZipShortest.cs @@ -175,8 +175,10 @@ public static IEnumerable ZipShortest( } static IEnumerable ZipImpl( - IEnumerable s1, IEnumerable s2, - IEnumerable s3, IEnumerable s4, + IEnumerable s1, + IEnumerable s2, + IEnumerable? s3, + IEnumerable? s4, Func resultSelector) { return ZipImpl(s1, s2, s3, s4, resultSelector, 0); diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index 6c7820e9b..d047e7a4a 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -234,6 +234,7 @@ into e "System", "System.CodeDom.Compiler", "System.Collections.Generic", + "System.Diagnostics.CodeAnalysis", }; var imports = @@ -303,6 +304,16 @@ public static partial class {m.Name}Extension // This code was generated by a tool. Any changes made manually will be lost // the next time this code is regenerated. +#nullable enable // required for auto-generated sources (see below why) + +// > Older code generation strategies may not be nullable aware. Setting the +// > project-level nullable context to ""enable"" could result in many +// > warnings that a user is unable to fix. To support this scenario any syntax +// > tree that is determined to be generated will have its nullable state +// > implicitly set to ""disable"", regardless of the overall project state. +// +// Source: https://github.com/dotnet/roslyn/blob/70e158ba6c2c99bd3c3fc0754af0dbf82a6d353d/docs/features/nullable-reference-types.md#generated-code + namespace MoreLinq.Extensions {{ {string.Join("\n", imports)} diff --git a/global.json b/global.json index cb62ab482..aca3f3cb6 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "3.1.102", + "version": "3.1.300", "rollForward": "latestFeature" } } From 2acece8e5abdde6955fc91c36b4a46c221732efb Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 22 Aug 2020 13:11:31 +0200 Subject: [PATCH 039/157] Note co-authors of nullability annotation (PR #582) This is a note to credit other contributors of "Annotate nullability of reference types" (commit 8dec9ca04c73fdbeb03c9c95a71b921fa3f51fa7). git shortlog --summary --email --first-parent 8b7e21b..861585c 51 Atif Aziz 3 Orace 5 moh-hassan Co-authored-by: Pierre Lando Co-authored-by: Mohamed Hassan From e384ba07f13d7fd15609da5348dbf4523f93c1e1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 29 Oct 2020 08:40:25 +0100 Subject: [PATCH 040/157] Fix return value docs for MinBy & MaxBy (#765) --- MoreLinq/Extensions.g.cs | 8 ++++---- MoreLinq/MaxBy.cs | 4 ++-- MoreLinq/MinBy.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 2bab62b83..30868b39d 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3325,7 +3325,7 @@ public static partial class MaxByExtension /// Type of the projected element /// Source sequence /// Selector to use to pick the results to compare - /// The maximal element, according to the projection. + /// The sequence of maximal elements, according to the projection. /// or is null public static IExtremaEnumerable MaxBy(this IEnumerable source, @@ -3345,7 +3345,7 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// Source sequence /// Selector to use to pick the results to compare /// Comparer to use to compare projected values - /// The maximal element, according to the projection. + /// The sequence of maximal elements, according to the projection. /// , /// or is null @@ -3373,7 +3373,7 @@ public static partial class MinByExtension /// Type of the projected element /// Source sequence /// Selector to use to pick the results to compare - /// The minimal element, according to the projection. + /// The sequence of minimal elements, according to the projection. /// or is null public static IExtremaEnumerable MinBy(this IEnumerable source, @@ -3393,7 +3393,7 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// Source sequence /// Selector to use to pick the results to compare /// Comparer to use to compare projected values - /// The minimal element, according to the projection. + /// The sequence of minimal elements, according to the projection. /// , /// or is null diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index faf269b4d..098c15f00 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -184,7 +184,7 @@ public static T SingleOrDefault(this IExtremaEnumerable source) /// Type of the projected element /// Source sequence /// Selector to use to pick the results to compare - /// The maximal element, according to the projection. + /// The sequence of maximal elements, according to the projection. /// or is null public static IExtremaEnumerable MaxBy(this IEnumerable source, @@ -206,7 +206,7 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// Source sequence /// Selector to use to pick the results to compare /// Comparer to use to compare projected values - /// The maximal element, according to the projection. + /// The sequence of maximal elements, according to the projection. /// , /// or is null diff --git a/MoreLinq/MinBy.cs b/MoreLinq/MinBy.cs index 3a06bcb98..625d698df 100644 --- a/MoreLinq/MinBy.cs +++ b/MoreLinq/MinBy.cs @@ -35,7 +35,7 @@ static partial class MoreEnumerable /// Type of the projected element /// Source sequence /// Selector to use to pick the results to compare - /// The minimal element, according to the projection. + /// The sequence of minimal elements, according to the projection. /// or is null public static IExtremaEnumerable MinBy(this IEnumerable source, @@ -57,7 +57,7 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// Source sequence /// Selector to use to pick the results to compare /// Comparer to use to compare projected values - /// The minimal element, according to the projection. + /// The sequence of minimal elements, according to the projection. /// , /// or is null From 9869d29fa02cfaabc9ff96fe3d02f526e0103881 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 15 Nov 2020 17:50:43 +0100 Subject: [PATCH 041/157] Fix .NET Core App version in test script --- test.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.cmd b/test.cmd index 7cc660244..bbed99b65 100644 --- a/test.cmd +++ b/test.cmd @@ -9,8 +9,8 @@ setlocal call build ^ && call :test netcoreapp2.1 Debug ^ && call :test netcoreapp2.1 Release ^ - && call :test netcoreapp3.0 Debug ^ - && call :test netcoreapp3.0 Release ^ + && call :test netcoreapp3.1 Debug ^ + && call :test netcoreapp3.1 Release ^ && call :test net451 Debug ^ && call :test net451 Release goto :EOF From 9dd2d5e3bb31a6517f9b826e7ca886646d52a456 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 20 Nov 2020 09:35:58 +0100 Subject: [PATCH 042/157] Update to .NET 5.0 SDK (#766) --- .travis.yml | 6 +++++- MoreLinq/MoreLinq.csproj | 4 ++-- MoreLinq/Permutations.cs | 19 +++++++++---------- global.json | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c82769aa..ae20362cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ solution: MoreLinq.sln mono: 5.0.1 dist: xenial sudo: required -dotnet: 3.1.300 +dotnet: 5.0.100 env: - CONFIGURATION=Debug - CONFIGURATION=Release @@ -19,6 +19,7 @@ addons: key_url: 'https://packages.microsoft.com/keys/microsoft.asc' packages: - dotnet-runtime-2.1 + - dotnet-runtime-3.1 before_install: - | @@ -28,6 +29,9 @@ before_install: # Install dotnet core 2.1 runtime wget --retry-connrefused --waitretry=1 -O /tmp/dn21.pkg 'https://download.visualstudio.microsoft.com/download/pr/9314da31-774c-4d2b-8743-998f2a21f5ab/bc918ca05ab6b650f2991b205c04f623/dotnet-runtime-2.1.13-osx-x64.pkg' sudo installer -pkg /tmp/dn21.pkg -target / + # Install dotnet core 3.1 runtime + wget --retry-connrefused --waitretry=1 -O /tmp/dn31.pkg 'https://download.visualstudio.microsoft.com/download/pr/cf89f4c1-b56a-4250-a927-461f17a828ac/3522d5d7cf26727e416d781533d07b65/dotnet-runtime-3.1.10-osx-x64.pkg' + sudo installer -pkg /tmp/dn31.pkg -target / fi - dotnet --info diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index ee4dfcdba..d61da8e75 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -120,7 +120,7 @@ 3.3.2 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 - 8 + 9 enable true portable @@ -247,7 +247,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/MoreLinq/Permutations.cs b/MoreLinq/Permutations.cs index 3c96161fd..0fac1001b 100644 --- a/MoreLinq/Permutations.cs +++ b/MoreLinq/Permutations.cs @@ -20,6 +20,7 @@ namespace MoreLinq using System; using System.Collections; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Linq; public static partial class MoreEnumerable @@ -83,25 +84,23 @@ public PermutationEnumerator(IEnumerable valueSet) // 1) for empty sets and sets of cardinality 1, there exists only a single permutation. // 2) for sets larger than 1 element, the number of nested loops needed is: set.Count-1 _generator = NestedLoops(NextPermutation, Enumerable.Range(2, Math.Max(0, _valueSet.Count - 1))); - Reset(ref _current, ref _generatorIterator, ref _hasMoreResults); + Reset(); } - public void Reset() => - Reset(ref _current, ref _generatorIterator, ref _hasMoreResults); - - void Reset(ref IList? current, ref IEnumerator generatorIterator, ref bool hasMoreResults) + [MemberNotNull(nameof(_generatorIterator))] + public void Reset() { - current = null; - generatorIterator?.Dispose(); + _current = null; + _generatorIterator?.Dispose(); // restore lexographic ordering of the permutation indexes for (var i = 0; i < _permutation.Length; i++) _permutation[i] = i; // start a newiteration over the nested loop generator - generatorIterator = _generator.GetEnumerator(); + _generatorIterator = _generator.GetEnumerator(); // we must advance the nestedloop iterator to the initial element, // this ensures that we only ever produce N!-1 calls to NextPermutation() - generatorIterator.MoveNext(); - hasMoreResults = true; // there's always at least one permutation: the original set itself + _generatorIterator.MoveNext(); + _hasMoreResults = true; // there's always at least one permutation: the original set itself } public IList Current => _current!; diff --git a/global.json b/global.json index aca3f3cb6..a46dc9b06 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "3.1.300", + "version": "5.0.100", "rollForward": "latestFeature" } } From 31d6709d97b923d4b2e11203455cc16577b42c3b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 20 Nov 2020 13:01:06 +0100 Subject: [PATCH 043/157] Fix duplicate test runs on Windows --- test.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.cmd b/test.cmd index bbed99b65..9d66b4a31 100644 --- a/test.cmd +++ b/test.cmd @@ -24,6 +24,6 @@ if %2==Debug set COVERAGE_ARGS=-p:CollectCoverage=true ^ if %1==net451 ( MoreLinq.Test\bin\%2\net451\MoreLinq.Test.exe ) else ( - dotnet test --no-build MoreLinq.Test -c %2 %COVERAGE_ARGS% + dotnet test --no-build MoreLinq.Test -f %1 -c %2 %COVERAGE_ARGS% ) goto :EOF From 556fa3179c2df76330695b6f6a2068ace9ee4d4f Mon Sep 17 00:00:00 2001 From: David Fallah Date: Fri, 4 Dec 2020 15:55:21 +0000 Subject: [PATCH 044/157] Fix typo in Partition return value documentation (#772) Co-authored-by: Atif Aziz --- MoreLinq/Extensions.g.cs | 2 +- MoreLinq/Partition.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 30868b39d..b40abc470 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -4127,7 +4127,7 @@ public static partial class PartitionExtension /// The predicate function. /// Type of source elements. /// - /// A tuple of elements staisfying the predicate and those that do not, + /// A tuple of elements satisfying the predicate and those that do not, /// respectively. /// /// diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index be4c637e3..9066610f4 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -31,7 +31,7 @@ static partial class MoreEnumerable /// The predicate function. /// Type of source elements. /// - /// A tuple of elements staisfying the predicate and those that do not, + /// A tuple of elements satisfying the predicate and those that do not, /// respectively. /// /// From f669558facbbec9269a466c81594d5db8a19b4d1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 13:05:58 +0100 Subject: [PATCH 045/157] Integrate templated code generation into project file (#776) --- MoreLinq/MoreLinq.csproj | 31 +++++++++++++++++++++++++++++++ MoreLinq/tt.cmd | 8 -------- MoreLinq/tt.sh | 4 ---- build.cmd | 2 +- build.sh | 2 +- 5 files changed, 33 insertions(+), 14 deletions(-) delete mode 100644 MoreLinq/tt.cmd delete mode 100755 MoreLinq/tt.sh diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index d61da8e75..119d27d01 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -156,6 +156,7 @@ + TextTemplatingFileGenerator Aggregate.g.cs @@ -253,4 +254,34 @@ + + + + %(None.LastGenOutput) + + + + + + + + + + + + + + + + + + + + diff --git a/MoreLinq/tt.cmd b/MoreLinq/tt.cmd deleted file mode 100644 index cbb7ac281..000000000 --- a/MoreLinq/tt.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -pushd "%~dp0" -for /f "tokens=*" %%f in ('dir /s /b *.tt') do ( - echo>&2 dotnet t4 "%%f" - dotnet t4 "%%f" || goto :end -) -:end -popd diff --git a/MoreLinq/tt.sh b/MoreLinq/tt.sh deleted file mode 100755 index 49be91c70..000000000 --- a/MoreLinq/tt.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -e -cd "$(dirname "$0")" -find . -name "*.tt" -print0 | xargs -0 -t -L 1 sh -c '(dotnet t4 "$0" || exit 255)' diff --git a/build.cmd b/build.cmd index cedd31964..6f47fd666 100644 --- a/build.cmd +++ b/build.cmd @@ -13,7 +13,7 @@ if "%1"=="docs" shift & goto :docs dotnet restore && dotnet tool restore ^ && call :codegen MoreLinq\Extensions.g.cs -x "[/\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq ^ && call :codegen MoreLinq\Extensions.ToDataTable.g.cs -i "[/\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq ^ - && call MoreLinq\tt ^ + && dotnet build MoreLinq -t:TransformTextTemplates -p:BuildInParallel=false ^ && for %%i in (debug release) do dotnet build -c %%i --no-restore %* || exit /b 1 goto :EOF diff --git a/build.sh b/build.sh index 226dd4559..e96b1eaed 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,7 @@ codegen() { } codegen MoreLinq/Extensions.g.cs -x "[/\\\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq codegen MoreLinq/Extensions.ToDataTable.g.cs -i "[/\\\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq -MoreLinq/tt.sh +dotnet build MoreLinq -t:TransformTextTemplates -p:BuildInParallel=false if [[ -z "$1" ]]; then configs="Debug Release" else From 6d8b34acab93beaf5ce0cb9bd51fca2fd79ac5c7 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 13:10:34 +0100 Subject: [PATCH 046/157] Fix condition formatting inconsistencies in projects --- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- MoreLinq/MoreLinq.csproj | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 45f75013c..8ca5598d2 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -69,7 +69,7 @@ - + diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 119d27d01..b69431b8b 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -129,7 +129,7 @@ Library key.snk true - true + true morelinq linq;extensions https://morelinq.github.io/ @@ -179,7 +179,7 @@ - + @@ -190,11 +190,11 @@ $(DefineConstants);MORELINQ - + $(DefineConstants);MORELINQ;NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC - + From b38c60a173d63d814dba4fe22741a49d64bd2ef8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 13:11:18 +0100 Subject: [PATCH 047/157] Fix missing quotes in project item condition --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index b69431b8b..37b50fe4d 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -256,7 +256,7 @@ - + %(None.LastGenOutput) From 50b7d42450cbba60a13aab86fd93607f275604ca Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 13:16:25 +0100 Subject: [PATCH 048/157] Update outdated dependencies --- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- MoreLinq/MoreLinq.csproj | 2 +- bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 8ca5598d2..e990760a7 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -36,7 +36,7 @@ - + diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 37b50fe4d..b214a896c 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -147,7 +147,7 @@ - + runtime; build; native; contentfiles; analyzers all diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index f7eb51e48..4c7b2856c 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -6,7 +6,7 @@ false - - + + From 3ac72d63444a7318c4a3134f7f2ffbdb72f9d4cd Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 14:17:44 +0100 Subject: [PATCH 049/157] Update dotnet-install script --- tools/dotnet-install.ps1 | 449 ++++++++++++++++++++++----------------- 1 file changed, 257 insertions(+), 192 deletions(-) diff --git a/tools/dotnet-install.ps1 b/tools/dotnet-install.ps1 index 3b052ce3f..99798362c 100644 --- a/tools/dotnet-install.ps1 +++ b/tools/dotnet-install.ps1 @@ -98,7 +98,7 @@ param( [string]$FeedCredential, [string]$ProxyAddress, [switch]$ProxyUseDefaultCredentials, - [string[]]$ProxyBypassList, + [string[]]$ProxyBypassList=@(), [switch]$SkipNonVersionedFiles, [switch]$NoCdn ) @@ -195,7 +195,7 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) { { $_ -eq "x86" } { return "x86" } { $_ -eq "arm" } { return "arm" } { $_ -eq "arm64" } { return "arm64" } - default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues" } + default { throw "Architecture '$Architecture' not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues" } } } @@ -395,17 +395,20 @@ function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) { Say-Invocation $MyInvocation + # If anything fails in this lookup it will default to $SpecificVersion + $SpecificProductVersion = Get-Product-Version -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion + if ($Runtime -eq "dotnet") { - $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip" + $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" } elseif ($Runtime -eq "aspnetcore") { - $PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificVersion-win-$CLIArchitecture.zip" + $PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" } elseif ($Runtime -eq "windowsdesktop") { - $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificVersion-win-$CLIArchitecture.zip" + $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" } elseif (-not $Runtime) { - $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip" + $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificProductVersion-win-$CLIArchitecture.zip" } else { throw "Invalid value for `$Runtime" @@ -413,7 +416,7 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string Say-Verbose "Constructed primary named payload URL: $PayloadURL" - return $PayloadURL + return $PayloadURL, $SpecificProductVersion } function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) { @@ -434,6 +437,51 @@ function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [ return $PayloadURL } +function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion) { + Say-Invocation $MyInvocation + + if ($Runtime -eq "dotnet") { + $ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt" + } + elseif ($Runtime -eq "aspnetcore") { + $ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/productVersion.txt" + } + elseif ($Runtime -eq "windowsdesktop") { + $ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt" + } + elseif (-not $Runtime) { + $ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/productVersion.txt" + } + else { + throw "Invalid value '$Runtime' specified for `$Runtime" + } + + Say-Verbose "Checking for existence of $ProductVersionTxtURL" + + try { + $productVersionResponse = GetHTTPResponse($productVersionTxtUrl) + + if ($productVersionResponse.StatusCode -eq 200) { + $productVersion = $productVersionResponse.Content.ReadAsStringAsync().Result.Trim() + if ($productVersion -ne $SpecificVersion) + { + Say "Using alternate version $productVersion found in $ProductVersionTxtURL" + } + + return $productVersion + } + else { + Say-Verbose "Got StatusCode $($productVersionResponse.StatusCode) trying to get productVersion.txt at $productVersionTxtUrl, so using default value of $SpecificVersion" + $productVersion = $SpecificVersion + } + } catch { + Say-Verbose "Could not read productVersion.txt at $productVersionTxtUrl, so using default value of $SpecificVersion (Exception: '$($_.Exception.Message)' )" + $productVersion = $SpecificVersion + } + + return $productVersion +} + function Get-User-Share-Path() { Say-Invocation $MyInvocation @@ -587,9 +635,14 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolde } } +Say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" +Say "- The SDK needs to be installed without user interaction and without admin rights." +Say "- The SDK installation doesn't need to persist across multiple CI runs." +Say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n" + $CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture $SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile -$DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture +$DownloadLink, $EffectiveVersion = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $InstallRoot = Resolve-Installation-Path $InstallDir @@ -615,6 +668,11 @@ if ($DryRun) { } } Say "Repeatable invocation: $RepeatableCommand" + if ($SpecificVersion -ne $EffectiveVersion) + { + Say "NOTE: Due to finding a version manifest with this runtime, it would actually install with version '$EffectiveVersion'" + } + exit 0 } @@ -638,6 +696,12 @@ else { throw "Invalid value for `$Runtime" } +if ($SpecificVersion -ne $EffectiveVersion) +{ + Say "Performing installation checks for effective version: $EffectiveVersion" + $SpecificVersion = $EffectiveVersion +} + # Check if the SDK version is already installed. $isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion if ($isAssetInstalled) { @@ -714,198 +778,199 @@ Remove-Item $ZipPath Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath +Say "Note that the script does not resolve dependencies during installation." +Say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install/windows#dependencies" Say "Installation finished" exit 0 - # SIG # Begin signature block -# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor +# MIIjlgYJKoZIhvcNAQcCoIIjhzCCI4MCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCpfA/pR7OjIFT3 -# AofDlc6nOYGKjwNIAy3Eyvb16wpECqCCDYEwggX/MIID56ADAgECAhMzAAABh3IX -# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA+isugNMwZSGLd +# kfBd0C2Ud//U2Nbj31s1jg3Yf9gh4KCCDYUwggYDMIID66ADAgECAhMzAAABiK9S +# 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p -# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw +# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB -# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH -# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d -# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ -# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV -# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE -# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw -# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 -# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu -# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu -# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w -# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 -# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx -# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy -# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K -# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV -# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr -# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx -# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe -# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g -# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf -# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI -# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5 -# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea -# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS -# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 -# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla -# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS -# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT -# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB -# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG -# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S -# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz -# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 -# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u -# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 -# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl -# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP -# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB -# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF -# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM -# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ -# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud -# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO -# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 -# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p -# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB -# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw -# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA -# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY -# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj -# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd -# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ -# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf -# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ -# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j -# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B -# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 -# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 -# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I -# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG -# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx -# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z -# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN -# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor -# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgzc+/PrE5 -# 4VVWEHbuPUTelP1qUjpv3t9Tr6VG3NE7g5EwQgYKKwYBBAGCNwIBDDE0MDKgFIAS -# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN -# BgkqhkiG9w0BAQEFAASCAQBxJUwvSKHR/18WcqMt7X5KZ2EEH2C3C6OZcJ10VwIQ -# uwqgtGg1ZzaTtbBjdU58wK0URWPdiWv+DI2pLLy/qO6VsAieHd9h1Feqe1JExlh+ -# s43iGMFqiMpZp9qJ29MZe/I/4uC0ZTGatO97ld93tPGiRpbyXxZHbqkxPr1IMjns -# hVxoq0QH8ia699zw/6K6uCfdRlu49ZbyVoZbRRh9Cx3xEVVwiGO2/i2vMBOO0Xwc -# j0SBp/G2vUcmddBnf+DxcBbBmeDpNzJdggF+YtBVubLv1In2MHYOmfB8CXPAi0IS -# 57nptq/BQ1edeN9ytizPOc+gi8pRuwWt4rLTmr2JTEt/oYIS8TCCEu0GCisGAQQB -# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME -# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB -# MDEwDQYJYIZIAWUDBAIBBQAEIPZ740SKNZOpkM9U1riD6qS3XQ9TMXQJTBjlSj6W -# 6sYjAgZfFz3Hj4kYEzIwMjAwNzI5MTIyMDEwLjkyNVowBIACAfSggdSkgdEwgc4x +# AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0 +# t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs +# 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd +# vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv +# V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W +# MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE +# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w +# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh +# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW +# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v +# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw +# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov +# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx +# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB +# ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q +# qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X +# 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P +# mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM +# i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT +# GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz +# LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM +# SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa +# 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV +# Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+ +# r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK +# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV +# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv +# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm +# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw +# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE +# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD +# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG +# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la +# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc +# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D +# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ +# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk +# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 +# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd +# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL +# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd +# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 +# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS +# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI +# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL +# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD +# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv +# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf +# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 +# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf +# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF +# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h +# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA +# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn +# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 +# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b +# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ +# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy +# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp +# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi +# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb +# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS +# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL +# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX +# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFWcwghVjAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt -# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p -# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg -# VFNTIEVTTjozMkJELUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt -# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABLqjSGQeT9GvoAAAA -# AAEuMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo -# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y -# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw -# MB4XDTE5MTIxOTAxMTUwNVoXDTIxMDMxNzAxMTUwNVowgc4xCzAJBgNVBAYTAlVT -# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK -# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy -# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozMkJE -# LUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj -# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK7TTKJRU196LFIjMQ9q -# /UjpPhz43m5RnHgHAVp2YGni74+ltsYoO1nZ58rTbJhCQ8GYHy8B4devgbqqYPQN -# U3i+drpEtEcNLbsMr4MEq3SM+vO3a6QMFd1lDRy7IQLPJNLKvcM69Nt7ku1YyM5N -# nPNDcRJsnUb/8Yx/zcW5cWjnoj8s9fQ93BPf/J74qM1ql2CdzQV74PBisMP/tppA -# nSuNwo8I7+uWr6vfpBynSWDvJeMDrcsa62Xsm7DbB1NnSsPGAGt3RzlBV9KViciz -# e4U3fo4chdoB2+QLu17PaEmj07qq700CG5XJkpEYOjedNFiByApF7YRvQrOZQ07Q -# YiMCAwEAAaOCARswggEXMB0GA1UdDgQWBBSGmokmTguJN7uqSTQ1UhLwt1RObDAf -# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH -# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU -# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF -# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0 -# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG -# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCN4ARqpzCuutNqY2nWJDDXj35iaidl -# gtJ/bspYsAX8atJl19IfUKIzTuuSVU3caXZ6/YvMMYMcbsNa/4J28us23K6PWZAl -# jIj0G8QtwDMlQHjrKnrcr4FBAz6ZqvB6SrN3/Wbb0QSK/OlxsU0mfD7z87R2JM4g -# wKJvH6EILuAEtjwUGSB1NKm3Twrm51fCD0jxvWxzaUS2etvMPrh8DNrrHLJBR3UH -# vg/NXS2IzdQn20xjjsW0BUAiTf+NCRpxUvu/j80Nb1++vnejibfpQJ2IlXiJdIi+ -# Hb+OL3XOr8MaDDSYOaRFAIfcoq3VPi4BkvSC8QGrvhjAZafkE7R6L5FJMIIGcTCC -# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC -# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV -# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv -# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN -# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv -# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 -# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw -# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0 -# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw -# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe -# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx -# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G -# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA -# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7 -# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC -# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX -# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v -# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI -# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j -# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g -# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93 -# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB -# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA -# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh -# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS -# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK -# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon -# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi -# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/ -# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII -# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0 -# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a -# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ -# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ -# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT -# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD -# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP -# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoz -# MkJELUUzRDUtM0IxRDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy -# dmljZaIjCgEBMAcGBSsOAwIaAxUA+1/CN6BILeU1yDGo+b6WkpLoQpuggYMwgYCk -# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH -# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD -# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF -# AOLLn2AwIhgPMjAyMDA3MjkxMTEwMjRaGA8yMDIwMDczMDExMTAyNFowdzA9Bgor -# BgEEAYRZCgQBMS8wLTAKAgUA4sufYAIBADAKAgEAAgImPAIB/zAHAgEAAgIRsTAK -# AgUA4szw4AIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB -# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAI1W0gMAkyYoYB5t -# BiO10MENTa3AeN9Mc5rqjrw99nmLr9S7zq9oTprZ59SQ2eomQpBaIO+33xWK2met -# +NR+bq2vNh+m6W2dacmZ2Ki8jlMfUajJaWX0xbHWaGMpL+9nlgZnNUcH7whVwXxp -# FlMaPFf0CKv8Uo11B4rlZat8fFE4MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC -# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV -# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp -# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEuqNIZB5P0a+gAAAAAAS4wDQYJYIZIAWUD -# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B -# CQQxIgQgJ7FDhQl++eQw1izf/wfOB3EPDCi12dQQe1YoFx8iEckwgfoGCyqGSIb3 -# DQEJEAIvMYHqMIHnMIHkMIG9BCDa/s3O8YhWiqpVN0kTeK+x2m0RAh17JpR6DiFo -# TILJKTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u -# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp -# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB -# LqjSGQeT9GvoAAAAAAEuMCIEIIpoWQMeh7Zef+LuYZIDjkaUw/O5NUX/s7oGyVZj -# W4khMA0GCSqGSIb3DQEBCwUABIIBAJ1oCV3CVki6wRkqMYV9scx3xjFJoNah1g4X -# FPXqezxKaG534hToWOZUmXfvnJwCt136zoR8Z0JSEE6PnWVU7e7EhTrqIkMSHSSL -# GGg5HIHS/nbmsu6VCigAHZn0okwXXy8ftOKRFhMVaOSLqa7qQgrRMHBBduHvq6xw -# bv/aqK6i2TE5Sv/QrhsgVDKWNL3oyhVlp+tJzCAuHILSVkClGXHNjrRDrEHkeWP7 -# xrbtgxDGHATlqra2bW2bAp2B46X9qjZkBmHopXS/VOfSQltQWef9VyuB4wzrNuxG -# gF4uSZOf20eeD1svHaZvTVNwIdLq6WvPVhUB4s6GWFofm1dcDpQ= +# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p +# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA +# AYgwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw +# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIK4I +# CDH7/r/eeMqTtDETJ67ogfneVRo0/P6ogV2vy4tXMEIGCisGAQQBgjcCAQwxNDAy +# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j +# b20wDQYJKoZIhvcNAQEBBQAEggEAOnmVmILEjI6ZiuuSOvvTvijidkBez61Vz97A +# jV3AOsfmUvLpVaTVa1Mt2iPDuq1QLqRPaT7BD8PAUwr91pYllVgEd8NqivCIaCZg +# QyIRiTmHQxbozWsLcjxMvX2VxSmNKDw7IOHzUbXtmiEGhygyZpdh/uiCj7ziSxp3 +# lQBR8mUE1NL9dxaxKWLhGeORqAepw6nId9oO+mHRh4JRK7uqZOFAES7/21M9vPZi +# XYilJLgIoyMkvqYSdoouzn6+m74kgzkNkyK9GYz2mmO2BCMnai9Njze2d0+kY+37 +# kt10BmJDw3FHaZ+/fH/TMTgo0ZcAOicP9ccdIh/CzzpU52o+Q6GCEvEwghLtBgor +# BgEEAYI3AwMBMYIS3TCCEtkGCSqGSIb3DQEHAqCCEsowghLGAgEDMQ8wDQYJYIZI +# AWUDBAIBBQAwggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYKKwYBBAGE +# WQoDATAxMA0GCWCGSAFlAwQCAQUABCBSbhMJwNER+BICn3iLUnPrP8dptyUphcFC +# A/NsIgnPLwIGX4hEzP6WGBMyMDIwMTEwOTE0NDY1Mi4yMzNaMASAAgH0oIHUpIHR +# MIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH +# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL +# EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhh +# bGVzIFRTUyBFU046MEE1Ni1FMzI5LTRENEQxJTAjBgNVBAMTHE1pY3Jvc29mdCBU +# aW1lLVN0YW1wIFNlcnZpY2Wggg5EMIIE9TCCA92gAwIBAgITMwAAAScvbqPvkagZ +# qAAAAAABJzANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK +# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 +# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg +# MjAxMDAeFw0xOTEyMTkwMTE0NTlaFw0yMTAzMTcwMTE0NTlaMIHOMQswCQYDVQQG +# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG +# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQg +# T3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046 +# MEE1Ni1FMzI5LTRENEQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl +# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD4Ad5xEZ5On0uN +# L71ng9xwoDPRKeMUyEIj5yVxPRPh5GVbU7D3pqDsoXzQMhfeRP61L1zlU1HCRS+1 +# 29eo0yj1zjbAlmPAwosUgyIonesWt9E4hFlXCGUcIg5XMdvQ+Ouzk2r+awNRuk8A +# BGOa0I4VBy6zqCYHyX2pGauiB43frJSNP6pcrO0CBmpBZNjgepof5Z/50vBuJDUS +# ug6OIMQ7ZwUhSzX4bEmZUUjAycBb62dhQpGqHsXe6ypVDTgAEnGONdSBKkHiNT8H +# 0Zt2lm0vCLwHyTwtgIdi67T/LCp+X2mlPHqXsY3u72X3GYn/3G8YFCkrSc6m3b0w +# TXPd5/2fAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQU5fSWVYBfOTEkW2JTiV24WNNt +# lfIwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBL +# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv +# TWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr +# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNU +# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAK +# BggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEACsqNfNFVxwalZ42cEMuzZc12 +# 6Nvluanx8UewDVeUQZEZHRmppMFHAzS/g6RzmxTyR2tKE3mChNGW5dTL730vEbRh +# nYRmBgiX/gT3f4AQrOPnZGXY7zszcrlbgzxpakOX+x0u4rkP3Ashh3B2CdJ11XsB +# di5PiZa1spB6U5S8D15gqTUfoIniLT4v1DBdkWExsKI1vsiFcDcjGJ4xRlMRF+fw +# 7SY0WZoOzwRzKxDTdg4DusAXpaeKbch9iithLFk/vIxQrqCr/niW8tEA+eSzeX/E +# q1D0ZyvOn4e2lTnwoJUKH6OQAWSBogyK4OCbFeJOqdKAUiBTgHKkQIYh/tbKQjCC +# BnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNV +# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w +# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m +# dCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1 +# NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp +# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw +# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw +# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/ +# aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxh +# MFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhH +# hjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tk +# iVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox +# 8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJN +# AgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIox +# kPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P +# BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9 +# lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQu +# Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3Js +# MFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3Nv +# ZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAG +# A1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRw +# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAG +# CCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEA +# dABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXED +# PZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgr +# UYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c +# 8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFw +# nzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFt +# w5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk +# 7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9d +# dJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zG +# y9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3 +# yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7c +# RDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wkn +# HNWzfjUeCLraNtvTX4/edIhJEqGCAtIwggI7AgEBMIH8oYHUpIHRMIHOMQswCQYD +# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe +# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3Nv +# ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBF +# U046MEE1Ni1FMzI5LTRENEQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w +# IFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVALOVuE5sgxzETO4s+poBqI6r1x8zoIGD +# MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV +# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG +# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEF +# BQACBQDjU7byMCIYDzIwMjAxMTA5MTYzOTE0WhgPMjAyMDExMTAxNjM5MTRaMHcw +# PQYKKwYBBAGEWQoEATEvMC0wCgIFAONTtvICAQAwCgIBAAICIt0CAf8wBwIBAAIC +# EcQwCgIFAONVCHICAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAK +# MAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQAQhyIIAC/A +# P+VJdbhL9IQgm8WTa1DmPPE+BQSuRbBy2MmzC1KostixdEkr2OaNSjcYuZBNIJgv +# vE8CWhVDD+sbBpVcOdoSfoBwHXKfvqSTiWvovoexkF0X5aon7yr3PkJ/kEqoLyUM +# xRvdWKJdHOL1sT0/aWHn048c6aGin/zc8DGCAw0wggMJAgEBMIGTMHwxCzAJBgNV +# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w +# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m +# dCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABJy9uo++RqBmoAAAAAAEnMA0GCWCG +# SAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZI +# hvcNAQkEMSIEIJZkrbvF4R8oqYYpN6ZPGOj+QEZTQriEi/Yw9gW6zMqRMIH6Bgsq +# hkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgG5LoSxKGHWoW/wVMlbMztlQ4upAdzEmq +# H//vLu0jPiIwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu +# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv +# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAIT +# MwAAAScvbqPvkagZqAAAAAABJzAiBCDwhEViCRvqKwQV3MxociF2iGYrDP4p1BK+ +# s4tStO4vSDANBgkqhkiG9w0BAQsFAASCAQAkgmDo8lVmar0ZIqTG1it3skG8PZC9 +# iqEEC1vxcz8OSfsjl2QSkQ5T2+3xWpxWA4uy2+Byv0bi8EsfQEnnn4vtdthS6/kb +# vB/LLQiqoMhJ0rasf3/y/4KnQZEtztpg1+cCaNwFUgI6o+E8YEFt1frhLwFs/0WH +# 5pyBFx9ECEs0M22SLIpW13gexv9fgk6ZboIfSreAI28DLveeJpkgwggxHRpuVOVD +# 4D7QQJAvJ0VU6p+yJlbvQXR9iltwb1REhlsJ5mADJ/FkzPVX/swMSUIoyE2inlxK +# LEiPkkZYwiFYCifFYUTnQjWU1Ls0EV+ysosL+jhzCxO8S6oRdp5TAi4F # SIG # End signature block From 76da019f6d25d9d09c3b2fceb9fa3e59ac1dd29b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 16:44:27 +0100 Subject: [PATCH 050/157] Update Window CI config to install .NET Core 2.1 + 3.1 runtimes --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 46a9d5656..7b120873c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,6 +17,8 @@ install: - eclint check -w "**/*.{cs,tt,cmd,sh,md,txt,yml,json,sln,csproj,shfbproj}" - git reset --hard - ps: tools\dotnet-install.ps1 -JSonFile global.json +- ps: tools\dotnet-install.ps1 -Runtime dotnet -Version 3.1.10 -SkipNonVersionedFiles +- ps: tools\dotnet-install.ps1 -Runtime dotnet -Version 2.1.23 -SkipNonVersionedFiles before_build: - dotnet --info build_script: From 01edeef0cc4a5eeb31fd806fb9b0347395617e6a Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 16:51:40 +0100 Subject: [PATCH 051/157] Update dotnet-t4 to latest version (2.2.0) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8ec2b9a94..0758d4e1c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-t4": { - "version": "2.0.5", + "version": "2.2.0", "commands": [ "t4" ] From 9342bb9d6c6b2f623ea2a4f84a5c9ca62aa8f1a0 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 17:42:34 +0100 Subject: [PATCH 052/157] Add .NET 5.0 as a test target (#777) --- .travis.yml | 2 +- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- appveyor.yml | 2 +- test.cmd | 2 ++ test.sh | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae20362cb..deb6e3fda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,4 +55,4 @@ script: - # curl -s https://codecov.io/bash > codecov - curl -sSL https://raw.githubusercontent.com/codecov/codecov-bash/14662d32a4862918c31efafe4b450de1305a38e1/codecov > codecov - chmod +x codecov - - ./codecov -f ./MoreLinq.Test/coverage.netcoreapp3.1.opencover.xml + - ./codecov -f ./MoreLinq.Test/coverage.net5.0.opencover.xml diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index e990760a7..7e5f2afd9 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - netcoreapp3.1;netcoreapp2.1;net451 + net5.0;netcoreapp3.1;netcoreapp2.1;net451 true portable MoreLinq.Test diff --git a/appveyor.yml b/appveyor.yml index 7b120873c..3f39d25cc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -48,7 +48,7 @@ build_script: } test_script: - cmd: test.cmd -- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.netcoreapp3.1.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) +- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.net5.0.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) - ps: Get-ChildItem tmp/cover | Compress-Archive -DestinationPath coverage-report.zip - cmd: | cd tmp\cover diff --git a/test.cmd b/test.cmd index 9d66b4a31..03ed51dc9 100644 --- a/test.cmd +++ b/test.cmd @@ -7,6 +7,8 @@ goto :EOF :main setlocal call build ^ + && call :test net5.0 Debug ^ + && call :test net5.0 Debug ^ && call :test netcoreapp2.1 Debug ^ && call :test netcoreapp2.1 Release ^ && call :test netcoreapp3.1 Debug ^ diff --git a/test.sh b/test.sh index b26ecb839..9108be273 100755 --- a/test.sh +++ b/test.sh @@ -7,7 +7,7 @@ if [[ -z "$1" ]]; then else configs="$1" fi -for v in 2.1 3.1; do +for f in netcoreapp2.1 netcoreapp3.1 net5.0; do for c in $configs; do if [[ "$c" == "Debug" ]]; then coverage_args="-p:CollectCoverage=true @@ -16,7 +16,7 @@ for v in 2.1 3.1; do else unset coverage_args fi - dotnet test --no-build -c $c -f netcoreapp$v MoreLinq.Test $coverage_args + dotnet test --no-build -c $c -f $f MoreLinq.Test $coverage_args done done if [[ -z `which mono 2>/dev/null` ]]; then From 6fe6c22ce3b3872d1b9311055c588e4b9113e310 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 26 Dec 2020 18:56:24 +0100 Subject: [PATCH 053/157] Fix param arg passed to "ArgumentException" --- MoreLinq.Test/TestExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/TestExtensions.cs b/MoreLinq.Test/TestExtensions.cs index dfd05f458..5331447dd 100644 --- a/MoreLinq.Test/TestExtensions.cs +++ b/MoreLinq.Test/TestExtensions.cs @@ -89,7 +89,7 @@ internal static IEnumerable ToSourceKind(this IEnumerable input, Source case SourceKind.BreakingReadOnlyCollection: return new BreakingReadOnlyCollection(input.ToList()); default: - throw new ArgumentException(nameof(sourceKind)); + throw new ArgumentException(null, nameof(sourceKind)); } } } From 4f3bc787fdc28b319bbeeda2610b913bf1274e12 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 01:48:45 +0100 Subject: [PATCH 054/157] Use "KeyValuePair" helper in "RunLengthEncode" tests --- MoreLinq.Test/RunLengthEncodeTest.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/MoreLinq.Test/RunLengthEncodeTest.cs b/MoreLinq.Test/RunLengthEncodeTest.cs index 9ef44e32c..d36a462d4 100644 --- a/MoreLinq.Test/RunLengthEncodeTest.cs +++ b/MoreLinq.Test/RunLengthEncodeTest.cs @@ -46,7 +46,7 @@ public void TestRunLengthEncodeEmptySequence() var sequence = Enumerable.Empty(); var result = sequence.RunLengthEncode(); - Assert.That(result, Is.EqualTo(sequence.Select(x => new KeyValuePair(x, x)))); + Assert.That(result, Is.EqualTo(sequence.Select(x => KeyValuePair.Create(x, x)))); } /// @@ -56,10 +56,14 @@ public void TestRunLengthEncodeEmptySequence() public void TestRunLengthEncodeCustomComparer() { var sequence = new[] { "a", "A", "a", "b", "b", "B", "B" }; + var result = sequence.RunLengthEncode(StringComparer.CurrentCultureIgnoreCase) - .Select(kvp => new KeyValuePair(kvp.Key.ToLower(), kvp.Value)); - var expectedResult = new[] {new KeyValuePair("a", 3), - new KeyValuePair("b", 4)}; + .Select(kvp => KeyValuePair.Create(kvp.Key.ToLower(), kvp.Value)); + var expectedResult = new[] + { + KeyValuePair.Create("a", 3), + KeyValuePair.Create("b", 4) + }; Assert.That(result, Is.EqualTo(expectedResult)); } @@ -71,7 +75,7 @@ public void TestRunLengthEncodeCustomComparer() public void TestRunLengthEncodeResults() { var sequence = new[] { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 }; - var expectedResult = Enumerable.Range(1, 6).Select(x => new KeyValuePair(x, x)); + var expectedResult = Enumerable.Range(1, 6).Select(x => KeyValuePair.Create(x, x)); var result = sequence.RunLengthEncode(); Assert.That(result, Is.EqualTo(expectedResult)); @@ -85,7 +89,7 @@ public void TestRunLengthEncodeNoRuns() { var sequence = Enumerable.Range(1, 10); var result = sequence.RunLengthEncode(); - var expectedResult = sequence.Select(x => new KeyValuePair(x, 1)); + var expectedResult = sequence.Select(x => KeyValuePair.Create(x, 1)); Assert.That(result, Is.EqualTo(expectedResult)); } @@ -101,7 +105,7 @@ public void TestRunLengthEncodeOneRun() const int repeatCount = 10; var sequence = Enumerable.Repeat(value, repeatCount); var result = sequence.RunLengthEncode(); - var expectedResult = new[] { new KeyValuePair(value, repeatCount) }; + var expectedResult = new[] { KeyValuePair.Create(value, repeatCount) }; Assert.That(result, Is.EqualTo(expectedResult)); } From a455be113eb6e927b604d829cacad7e78f91cd4e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 01:50:09 +0100 Subject: [PATCH 055/157] Simplify "RunLengthEncode" test for empty case --- MoreLinq.Test/RunLengthEncodeTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/RunLengthEncodeTest.cs b/MoreLinq.Test/RunLengthEncodeTest.cs index d36a462d4..dba396b72 100644 --- a/MoreLinq.Test/RunLengthEncodeTest.cs +++ b/MoreLinq.Test/RunLengthEncodeTest.cs @@ -46,7 +46,7 @@ public void TestRunLengthEncodeEmptySequence() var sequence = Enumerable.Empty(); var result = sequence.RunLengthEncode(); - Assert.That(result, Is.EqualTo(sequence.Select(x => KeyValuePair.Create(x, x)))); + Assert.That(result, Is.Empty); } /// From 2fa9214ee5324c8a1563923ea1d640571ffaa8a2 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 01:51:29 +0100 Subject: [PATCH 056/157] Use invariant culture in "RunLengthEncode" test case --- MoreLinq.Test/RunLengthEncodeTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/RunLengthEncodeTest.cs b/MoreLinq.Test/RunLengthEncodeTest.cs index dba396b72..707d61d55 100644 --- a/MoreLinq.Test/RunLengthEncodeTest.cs +++ b/MoreLinq.Test/RunLengthEncodeTest.cs @@ -57,8 +57,8 @@ public void TestRunLengthEncodeCustomComparer() { var sequence = new[] { "a", "A", "a", "b", "b", "B", "B" }; - var result = sequence.RunLengthEncode(StringComparer.CurrentCultureIgnoreCase) - .Select(kvp => KeyValuePair.Create(kvp.Key.ToLower(), kvp.Value)); + var result = sequence.RunLengthEncode(StringComparer.InvariantCultureIgnoreCase) + .Select(kvp => KeyValuePair.Create(kvp.Key.ToLowerInvariant(), kvp.Value)); var expectedResult = new[] { KeyValuePair.Create("a", 3), From a6adbdeddbe1673d9ef2d97612c5a3c08a92fdf9 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 02:33:56 +0100 Subject: [PATCH 057/157] Use invariant culture in "SortedMerge" test case --- MoreLinq.Test/SortedMergeTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index bc21204be..ca588cd1a 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -167,7 +167,7 @@ public void TestSortedMergeCustomComparer() var sequenceB = new[] { "b", "E", "k", "q", "r", "u", "V", "x", "Y" }; var sequenceC = new[] { "C", "F", "l", "m", "N", "P", "s", "w" }; var expectedResult = sequenceA.Concat(sequenceB).Concat(sequenceC) - .OrderBy(a => a, StringComparer.CurrentCultureIgnoreCase); + .OrderBy(a => a, StringComparer.InvariantCultureIgnoreCase); var result = sequenceA.SortedMerge(OrderByDirection.Ascending, sequenceB, sequenceC); Assert.That(result, Is.EqualTo(expectedResult)); From 348197a7ae05fce860f288e3dab77b0b04e57982 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 14:21:22 +0100 Subject: [PATCH 058/157] Fix "SortedMerge" test case (was missing comparer arg) --- MoreLinq.Test/SortedMergeTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index ca588cd1a..9dc684671 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -166,9 +166,10 @@ public void TestSortedMergeCustomComparer() var sequenceA = new[] { "a", "D", "G", "h", "i", "J", "O", "t", "z" }; var sequenceB = new[] { "b", "E", "k", "q", "r", "u", "V", "x", "Y" }; var sequenceC = new[] { "C", "F", "l", "m", "N", "P", "s", "w" }; + var comparer = StringComparer.InvariantCultureIgnoreCase; var expectedResult = sequenceA.Concat(sequenceB).Concat(sequenceC) - .OrderBy(a => a, StringComparer.InvariantCultureIgnoreCase); - var result = sequenceA.SortedMerge(OrderByDirection.Ascending, sequenceB, sequenceC); + .OrderBy(a => a, comparer); + var result = sequenceA.SortedMerge(OrderByDirection.Ascending, comparer, sequenceB, sequenceC); Assert.That(result, Is.EqualTo(expectedResult)); } From 4cf03594cb88c39e7f6c523e5c1c711a5aa4764e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 13:49:37 +0100 Subject: [PATCH 059/157] Fix "dotnet t4 -h" redir to be x-plat --- MoreLinq/MoreLinq.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index b214a896c..acaa93874 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -267,7 +267,10 @@ See also: https://github.com/dotnet/msbuild/issues/2781 --> - + + + + From 2abc6a91ca16dea63bade649b313e0f59305975c Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 27 Dec 2020 15:38:06 +0100 Subject: [PATCH 060/157] Disable parallel building due to T4 for simplicity --- MoreLinq/MoreLinq.csproj | 7 +++---- build.cmd | 1 - build.sh | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index acaa93874..b43179b00 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -123,6 +123,9 @@ 9 enable true + + false portable true MoreLinq @@ -263,10 +266,6 @@ - - diff --git a/build.cmd b/build.cmd index 6f47fd666..08e0f43bf 100644 --- a/build.cmd +++ b/build.cmd @@ -13,7 +13,6 @@ if "%1"=="docs" shift & goto :docs dotnet restore && dotnet tool restore ^ && call :codegen MoreLinq\Extensions.g.cs -x "[/\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq ^ && call :codegen MoreLinq\Extensions.ToDataTable.g.cs -i "[/\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq ^ - && dotnet build MoreLinq -t:TransformTextTemplates -p:BuildInParallel=false ^ && for %%i in (debug release) do dotnet build -c %%i --no-restore %* || exit /b 1 goto :EOF diff --git a/build.sh b/build.sh index e96b1eaed..1cedf8ddc 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,6 @@ codegen() { } codegen MoreLinq/Extensions.g.cs -x "[/\\\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq codegen MoreLinq/Extensions.ToDataTable.g.cs -i "[/\\\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq -dotnet build MoreLinq -t:TransformTextTemplates -p:BuildInParallel=false if [[ -z "$1" ]]; then configs="Debug Release" else From 55744f29b2e9c200b0bc65dbc6bf5a5a0fac33bc Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 31 Dec 2020 09:16:16 +0100 Subject: [PATCH 061/157] Unify to single CI/CD platform (#778) Closes #602. --- .travis.yml | 58 -- appveyor.yml | 86 ++- tools/dotnet-install.sh | 1108 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1165 insertions(+), 87 deletions(-) delete mode 100644 .travis.yml create mode 100755 tools/dotnet-install.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index deb6e3fda..000000000 --- a/.travis.yml +++ /dev/null @@ -1,58 +0,0 @@ -if: NOT branch = deploy -language: csharp -os: - - linux - - osx -osx_image: xcode9.4 -solution: MoreLinq.sln -mono: 5.0.1 -dist: xenial -sudo: required -dotnet: 5.0.100 -env: - - CONFIGURATION=Debug - - CONFIGURATION=Release -addons: - apt: - sources: - - sourceline: 'deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main' - key_url: 'https://packages.microsoft.com/keys/microsoft.asc' - packages: - - dotnet-runtime-2.1 - - dotnet-runtime-3.1 - -before_install: - - | - if [ "$TRAVIS_OS_NAME" == "osx" ] || [ `uname` == "Darwin" ]; then - # Handle too many files on OS X - ulimit -n 4096 - # Install dotnet core 2.1 runtime - wget --retry-connrefused --waitretry=1 -O /tmp/dn21.pkg 'https://download.visualstudio.microsoft.com/download/pr/9314da31-774c-4d2b-8743-998f2a21f5ab/bc918ca05ab6b650f2991b205c04f623/dotnet-runtime-2.1.13-osx-x64.pkg' - sudo installer -pkg /tmp/dn21.pkg -target / - # Install dotnet core 3.1 runtime - wget --retry-connrefused --waitretry=1 -O /tmp/dn31.pkg 'https://download.visualstudio.microsoft.com/download/pr/cf89f4c1-b56a-4250-a927-461f17a828ac/3522d5d7cf26727e416d781533d07b65/dotnet-runtime-3.1.10-osx-x64.pkg' - sudo installer -pkg /tmp/dn31.pkg -target / - fi - - dotnet --info - -install: - - npm install -g eclint - -before_script: - - git rm .editorconfig - - eclint check -n "**/*.{cs,tt,cmd,sh,md,txt,yml}" - - eclint check -w "**/*.{cs,tt,cmd,sh,md,txt,yml,json,sln,csproj,shfbproj}" - - git reset --hard - -script: - - | - if grep --extended-regexp '^[[:space:]]*using[[:space:]]+System\.Linq;' $(ls MoreLinq.Test/*Test.cs); then - echo "System.Linq import found, failing the build!" >&2 - exit 1 - fi - - ./test.sh $CONFIGURATION - - # revert to following post merge of PR codecov/codecov-bash#138 - - # curl -s https://codecov.io/bash > codecov - - curl -sSL https://raw.githubusercontent.com/codecov/codecov-bash/14662d32a4862918c31efafe4b450de1305a38e1/codecov > codecov - - chmod +x codecov - - ./codecov -f ./MoreLinq.Test/coverage.net5.0.opencover.xml diff --git a/appveyor.yml b/appveyor.yml index 3f39d25cc..82a906dde 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,24 +1,64 @@ version: '{build}' -image: Visual Studio 2019 +image: +- Visual Studio 2019 +- Ubuntu1604 +- macos +environment: + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true +for: +- + matrix: + only: + - image: Visual Studio 2019 + environment: + IMAGE_NAME: win + deploy: + - provider: NuGet + server: https://www.myget.org/F/morelinq/api/v2/package + api_key: + secure: fhGwXyO35FSshRzs5GWmF1LJTrd1sIqmS/jNCSfO2LfOciuYAKiXuFMYZFGiTAl+ + symbol_server: https://www.myget.org/F/morelinq/symbols/api/v2/package + on: + branch: deploy + notifications: + - provider: Email + to: + - morelinq-roll@googlegroups.com + on_build_success: true + on_build_failure: true + on_build_status_changed: false +- + matrix: + only: + - image: Ubuntu1604 + environment: + IMAGE_NAME: ubuntu-16.04 +- + matrix: + only: + - image: macos + environment: + IMAGE_NAME: macos skip_commits: files: - '*.md' - '*.txt' - '.editorconfig' - lic/* -environment: - DOTNET_CLI_TELEMETRY_OPTOUT: true - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true install: -- ps: Install-Product node 6 - npm install -g eclint - git rm .editorconfig - eclint check -n "**/*.{cs,tt,cmd,sh,md,txt,yml}" - eclint check -w "**/*.{cs,tt,cmd,sh,md,txt,yml,json,sln,csproj,shfbproj}" - git reset --hard -- ps: tools\dotnet-install.ps1 -JSonFile global.json -- ps: tools\dotnet-install.ps1 -Runtime dotnet -Version 3.1.10 -SkipNonVersionedFiles -- ps: tools\dotnet-install.ps1 -Runtime dotnet -Version 2.1.23 -SkipNonVersionedFiles +- ps: if ($isWindows) { tools\dotnet-install.ps1 -JSonFile global.json } +- ps: if ($isWindows) { tools\dotnet-install.ps1 -Runtime dotnet -Version 3.1.10 -SkipNonVersionedFiles } +- ps: if ($isWindows) { tools\dotnet-install.ps1 -Runtime dotnet -Version 2.1.23 -SkipNonVersionedFiles } +- sh: ./tools/dotnet-install.sh --jsonfile global.json +- sh: ./tools/dotnet-install.sh --runtime dotnet --version 3.1.10 --skip-non-versioned-files +- sh: ./tools/dotnet-install.sh --runtime dotnet --version 2.1.23 --skip-non-versioned-files +- sh: export PATH="~/.dotnet:$PATH" before_build: - dotnet --info build_script: @@ -33,7 +73,7 @@ build_script: $id = $id.Substring(0, 13) - cmd /c call pack.cmd ci-$id + if ($isWindows) { cmd /c call pack.cmd ci-$id } else { ./pack.sh ci-$id } if ($LASTEXITCODE -ne 0) { throw "Building/Packing failed with an exit code of $LASTEXITCODE." @@ -48,26 +88,14 @@ build_script: } test_script: - cmd: test.cmd +- sh: ./test.sh - ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.net5.0.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) -- ps: Get-ChildItem tmp/cover | Compress-Archive -DestinationPath coverage-report.zip -- cmd: | - cd tmp\cover - tar -cvz -f ../../coverage-report.tar.gz * +- ps: | + cd tmp/cover + tar -cz -f "../../coverage-report-${IMAGE_NAME}.tar.gz" * +- sh: curl -sSL https://codecov.io/bash > codecov +- sh: chmod +x codecov +- sh: if [ "$CI_LINUX" = "true" ]; then ./codecov -f ./MoreLinq.Test/coverage.net5.0.opencover.xml; fi artifacts: - path: dist\*.nupkg -- path: coverage-report.* -deploy: -- provider: NuGet - server: https://www.myget.org/F/morelinq/api/v2/package - api_key: - secure: fhGwXyO35FSshRzs5GWmF1LJTrd1sIqmS/jNCSfO2LfOciuYAKiXuFMYZFGiTAl+ - symbol_server: https://www.myget.org/F/morelinq/symbols/api/v2/package - on: - branch: deploy -notifications: -- provider: Email - to: - - morelinq-roll@googlegroups.com - on_build_success: true - on_build_failure: true - on_build_status_changed: false +- path: coverage-report-* diff --git a/tools/dotnet-install.sh b/tools/dotnet-install.sh new file mode 100755 index 000000000..6525339be --- /dev/null +++ b/tools/dotnet-install.sh @@ -0,0 +1,1108 @@ +#!/usr/bin/env bash +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +# Stop script on NZEC +set -e +# Stop script if unbound variable found (use ${var:-} if intentional) +set -u +# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success +# This is causing it to fail +set -o pipefail + +# Use in the the functions: eval $invocation +invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"' + +# standard output may be used as a return value in the functions +# we need a way to write text on the screen in the functions so that +# it won't interfere with the return value. +# Exposing stream 3 as a pipe to standard output of the script itself +exec 3>&1 + +# Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors. +# See if stdout is a terminal +if [ -t 1 ] && command -v tput > /dev/null; then + # see if it supports colors + ncolors=$(tput colors) + if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then + bold="$(tput bold || echo)" + normal="$(tput sgr0 || echo)" + black="$(tput setaf 0 || echo)" + red="$(tput setaf 1 || echo)" + green="$(tput setaf 2 || echo)" + yellow="$(tput setaf 3 || echo)" + blue="$(tput setaf 4 || echo)" + magenta="$(tput setaf 5 || echo)" + cyan="$(tput setaf 6 || echo)" + white="$(tput setaf 7 || echo)" + fi +fi + +say_warning() { + printf "%b\n" "${yellow:-}dotnet_install: Warning: $1${normal:-}" +} + +say_err() { + printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2 +} + +say() { + # using stream 3 (defined in the beginning) to not interfere with stdout of functions + # which may be used as return value + printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3 +} + +say_verbose() { + if [ "$verbose" = true ]; then + say "$1" + fi +} + +# This platform list is finite - if the SDK/Runtime has supported Linux distribution-specific assets, +# then and only then should the Linux distribution appear in this list. +# Adding a Linux distribution to this list does not imply distribution-specific support. +get_legacy_os_name_from_platform() { + eval $invocation + + platform="$1" + case "$platform" in + "centos.7") + echo "centos" + return 0 + ;; + "debian.8") + echo "debian" + return 0 + ;; + "debian.9") + echo "debian.9" + return 0 + ;; + "fedora.23") + echo "fedora.23" + return 0 + ;; + "fedora.24") + echo "fedora.24" + return 0 + ;; + "fedora.27") + echo "fedora.27" + return 0 + ;; + "fedora.28") + echo "fedora.28" + return 0 + ;; + "opensuse.13.2") + echo "opensuse.13.2" + return 0 + ;; + "opensuse.42.1") + echo "opensuse.42.1" + return 0 + ;; + "opensuse.42.3") + echo "opensuse.42.3" + return 0 + ;; + "rhel.7"*) + echo "rhel" + return 0 + ;; + "ubuntu.14.04") + echo "ubuntu" + return 0 + ;; + "ubuntu.16.04") + echo "ubuntu.16.04" + return 0 + ;; + "ubuntu.16.10") + echo "ubuntu.16.10" + return 0 + ;; + "ubuntu.18.04") + echo "ubuntu.18.04" + return 0 + ;; + "alpine.3.4.3") + echo "alpine" + return 0 + ;; + esac + return 1 +} + +get_linux_platform_name() { + eval $invocation + + if [ -n "$runtime_id" ]; then + echo "${runtime_id%-*}" + return 0 + else + if [ -e /etc/os-release ]; then + . /etc/os-release + echo "$ID${VERSION_ID:+.${VERSION_ID}}" + return 0 + elif [ -e /etc/redhat-release ]; then + local redhatRelease=$(&1 || true) | grep -q musl +} + +get_current_os_name() { + eval $invocation + + local uname=$(uname) + if [ "$uname" = "Darwin" ]; then + echo "osx" + return 0 + elif [ "$uname" = "FreeBSD" ]; then + echo "freebsd" + return 0 + elif [ "$uname" = "Linux" ]; then + local linux_platform_name + linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; } + + if [ "$linux_platform_name" = "rhel.6" ]; then + echo $linux_platform_name + return 0 + elif is_musl_based_distro; then + echo "linux-musl" + return 0 + else + echo "linux" + return 0 + fi + fi + + say_err "OS name could not be detected: UName = $uname" + return 1 +} + +get_legacy_os_name() { + eval $invocation + + local uname=$(uname) + if [ "$uname" = "Darwin" ]; then + echo "osx" + return 0 + elif [ -n "$runtime_id" ]; then + echo $(get_legacy_os_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}") + return 0 + else + if [ -e /etc/os-release ]; then + . /etc/os-release + os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "") + if [ -n "$os" ]; then + echo "$os" + return 0 + fi + fi + fi + + say_verbose "Distribution specific OS name and version could not be detected: UName = $uname" + return 1 +} + +machine_has() { + eval $invocation + + hash "$1" > /dev/null 2>&1 + return $? +} + + +check_min_reqs() { + local hasMinimum=false + if machine_has "curl"; then + hasMinimum=true + elif machine_has "wget"; then + hasMinimum=true + fi + + if [ "$hasMinimum" = "false" ]; then + say_err "curl (recommended) or wget are required to download dotnet. Install missing prerequisite to proceed." + return 1 + fi + return 0 +} + +# args: +# input - $1 +to_lowercase() { + #eval $invocation + + echo "$1" | tr '[:upper:]' '[:lower:]' + return 0 +} + +# args: +# input - $1 +remove_trailing_slash() { + #eval $invocation + + local input="${1:-}" + echo "${input%/}" + return 0 +} + +# args: +# input - $1 +remove_beginning_slash() { + #eval $invocation + + local input="${1:-}" + echo "${input#/}" + return 0 +} + +# args: +# root_path - $1 +# child_path - $2 - this parameter can be empty +combine_paths() { + eval $invocation + + # TODO: Consider making it work with any number of paths. For now: + if [ ! -z "${3:-}" ]; then + say_err "combine_paths: Function takes two parameters." + return 1 + fi + + local root_path="$(remove_trailing_slash "$1")" + local child_path="$(remove_beginning_slash "${2:-}")" + say_verbose "combine_paths: root_path=$root_path" + say_verbose "combine_paths: child_path=$child_path" + echo "$root_path/$child_path" + return 0 +} + +get_machine_architecture() { + eval $invocation + + if command -v uname > /dev/null; then + CPUName=$(uname -m) + case $CPUName in + armv7l) + echo "arm" + return 0 + ;; + aarch64) + echo "arm64" + return 0 + ;; + esac + fi + + # Always default to 'x64' + echo "x64" + return 0 +} + +# args: +# architecture - $1 +get_normalized_architecture_from_architecture() { + eval $invocation + + local architecture="$(to_lowercase "$1")" + case "$architecture" in + \) + echo "$(get_normalized_architecture_from_architecture "$(get_machine_architecture)")" + return 0 + ;; + amd64|x64) + echo "x64" + return 0 + ;; + arm) + echo "arm" + return 0 + ;; + arm64) + echo "arm64" + return 0 + ;; + esac + + say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues" + return 1 +} + +# The version text returned from the feeds is a 1-line or 2-line string: +# For the SDK and the dotnet runtime (2 lines): +# Line 1: # commit_hash +# Line 2: # 4-part version +# For the aspnetcore runtime (1 line): +# Line 1: # 4-part version + +# args: +# version_text - stdin +get_version_from_version_info() { + eval $invocation + + cat | tail -n 1 | sed 's/\r$//' + return 0 +} + +# args: +# install_root - $1 +# relative_path_to_package - $2 +# specific_version - $3 +is_dotnet_package_installed() { + eval $invocation + + local install_root="$1" + local relative_path_to_package="$2" + local specific_version="${3//[$'\t\r\n']}" + + local dotnet_package_path="$(combine_paths "$(combine_paths "$install_root" "$relative_path_to_package")" "$specific_version")" + say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path" + + if [ -d "$dotnet_package_path" ]; then + return 0 + else + return 1 + fi +} + +# args: +# azure_feed - $1 +# channel - $2 +# normalized_architecture - $3 +# coherent - $4 +get_latest_version_info() { + eval $invocation + + local azure_feed="$1" + local channel="$2" + local normalized_architecture="$3" + local coherent="$4" + + local version_file_url=null + if [[ "$runtime" == "dotnet" ]]; then + version_file_url="$uncached_feed/Runtime/$channel/latest.version" + elif [[ "$runtime" == "aspnetcore" ]]; then + version_file_url="$uncached_feed/aspnetcore/Runtime/$channel/latest.version" + elif [ -z "$runtime" ]; then + if [ "$coherent" = true ]; then + version_file_url="$uncached_feed/Sdk/$channel/latest.coherent.version" + else + version_file_url="$uncached_feed/Sdk/$channel/latest.version" + fi + else + say_err "Invalid value for \$runtime" + return 1 + fi + say_verbose "get_latest_version_info: latest url: $version_file_url" + + download "$version_file_url" + return $? +} + +# args: +# json_file - $1 +parse_jsonfile_for_version() { + eval $invocation + + local json_file="$1" + if [ ! -f "$json_file" ]; then + say_err "Unable to find \`$json_file\`" + return 1 + fi + + sdk_section=$(cat $json_file | awk '/"sdk"/,/}/') + if [ -z "$sdk_section" ]; then + say_err "Unable to parse the SDK node in \`$json_file\`" + return 1 + fi + + sdk_list=$(echo $sdk_section | awk -F"[{}]" '{print $2}') + sdk_list=${sdk_list//[\" ]/} + sdk_list=${sdk_list//,/$'\n'} + + local version_info="" + while read -r line; do + IFS=: + while read -r key value; do + if [[ "$key" == "version" ]]; then + version_info=$value + fi + done <<< "$line" + done <<< "$sdk_list" + if [ -z "$version_info" ]; then + say_err "Unable to find the SDK:version node in \`$json_file\`" + return 1 + fi + + unset IFS; + echo "$version_info" + return 0 +} + +# args: +# azure_feed - $1 +# channel - $2 +# normalized_architecture - $3 +# version - $4 +# json_file - $5 +get_specific_version_from_version() { + eval $invocation + + local azure_feed="$1" + local channel="$2" + local normalized_architecture="$3" + local version="$(to_lowercase "$4")" + local json_file="$5" + + if [ -z "$json_file" ]; then + case "$version" in + latest) + local version_info + version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1 + say_verbose "get_specific_version_from_version: version_info=$version_info" + echo "$version_info" | get_version_from_version_info + return 0 + ;; + coherent) + local version_info + version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" true)" || return 1 + say_verbose "get_specific_version_from_version: version_info=$version_info" + echo "$version_info" | get_version_from_version_info + return 0 + ;; + *) + echo "$version" + return 0 + ;; + esac + else + local version_info + version_info="$(parse_jsonfile_for_version "$json_file")" || return 1 + echo "$version_info" + return 0 + fi +} + +# args: +# azure_feed - $1 +# channel - $2 +# normalized_architecture - $3 +# specific_version - $4 +construct_download_link() { + eval $invocation + + local azure_feed="$1" + local channel="$2" + local normalized_architecture="$3" + local specific_version="${4//[$'\t\r\n']}" + local specific_product_version="$(get_specific_product_version "$1" "$4")" + + local osname + osname="$(get_current_os_name)" || return 1 + + local download_link=null + if [[ "$runtime" == "dotnet" ]]; then + download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz" + elif [[ "$runtime" == "aspnetcore" ]]; then + download_link="$azure_feed/aspnetcore/Runtime/$specific_version/aspnetcore-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz" + elif [ -z "$runtime" ]; then + download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_product_version-$osname-$normalized_architecture.tar.gz" + else + return 1 + fi + + echo "$download_link" + return 0 +} + +# args: +# azure_feed - $1 +# specific_version - $2 +get_specific_product_version() { + # If we find a 'productVersion.txt' at the root of any folder, we'll use its contents + # to resolve the version of what's in the folder, superseding the specified version. + eval $invocation + + local azure_feed="$1" + local specific_version="${2//[$'\t\r\n']}" + local specific_product_version=$specific_version + + local download_link=null + if [[ "$runtime" == "dotnet" ]]; then + download_link="$azure_feed/Runtime/$specific_version/productVersion.txt${feed_credential}" + elif [[ "$runtime" == "aspnetcore" ]]; then + download_link="$azure_feed/aspnetcore/Runtime/$specific_version/productVersion.txt${feed_credential}" + elif [ -z "$runtime" ]; then + download_link="$azure_feed/Sdk/$specific_version/productVersion.txt${feed_credential}" + else + return 1 + fi + + if machine_has "curl" + then + specific_product_version=$(curl -s --fail "$download_link") + if [ $? -ne 0 ] + then + specific_product_version=$specific_version + fi + elif machine_has "wget" + then + specific_product_version=$(wget -qO- "$download_link") + if [ $? -ne 0 ] + then + specific_product_version=$specific_version + fi + fi + specific_product_version="${specific_product_version//[$'\t\r\n']}" + + echo "$specific_product_version" + return 0 +} + +# args: +# azure_feed - $1 +# channel - $2 +# normalized_architecture - $3 +# specific_version - $4 +construct_legacy_download_link() { + eval $invocation + + local azure_feed="$1" + local channel="$2" + local normalized_architecture="$3" + local specific_version="${4//[$'\t\r\n']}" + + local distro_specific_osname + distro_specific_osname="$(get_legacy_os_name)" || return 1 + + local legacy_download_link=null + if [[ "$runtime" == "dotnet" ]]; then + legacy_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" + elif [ -z "$runtime" ]; then + legacy_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" + else + return 1 + fi + + echo "$legacy_download_link" + return 0 +} + +get_user_install_path() { + eval $invocation + + if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then + echo "$DOTNET_INSTALL_DIR" + else + echo "$HOME/.dotnet" + fi + return 0 +} + +# args: +# install_dir - $1 +resolve_installation_path() { + eval $invocation + + local install_dir=$1 + if [ "$install_dir" = "" ]; then + local user_install_path="$(get_user_install_path)" + say_verbose "resolve_installation_path: user_install_path=$user_install_path" + echo "$user_install_path" + return 0 + fi + + echo "$install_dir" + return 0 +} + +# args: +# relative_or_absolute_path - $1 +get_absolute_path() { + eval $invocation + + local relative_or_absolute_path=$1 + echo "$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")" + return 0 +} + +# args: +# input_files - stdin +# root_path - $1 +# out_path - $2 +# override - $3 +copy_files_or_dirs_from_list() { + eval $invocation + + local root_path="$(remove_trailing_slash "$1")" + local out_path="$(remove_trailing_slash "$2")" + local override="$3" + local osname="$(get_current_os_name)" + local override_switch=$( + if [ "$override" = false ]; then + if [ "$osname" = "linux-musl" ]; then + printf -- "-u"; + else + printf -- "-n"; + fi + fi) + + cat | uniq | while read -r file_path; do + local path="$(remove_beginning_slash "${file_path#$root_path}")" + local target="$out_path/$path" + if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then + mkdir -p "$out_path/$(dirname "$path")" + if [ -d "$target" ]; then + rm -rf "$target" + fi + cp -R $override_switch "$root_path/$path" "$target" + fi + done +} + +# args: +# zip_path - $1 +# out_path - $2 +extract_dotnet_package() { + eval $invocation + + local zip_path="$1" + local out_path="$2" + + local temp_out_path="$(mktemp -d "$temporary_file_template")" + + local failed=false + tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true + + local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/' + find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false + find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files" + + rm -rf "$temp_out_path" + + if [ "$failed" = true ]; then + say_err "Extraction failed" + return 1 + fi +} + +# args: +# remote_path - $1 +# [out_path] - $2 - stdout if not provided +download() { + eval $invocation + + local remote_path="$1" + local out_path="${2:-}" + + if [[ "$remote_path" != "http"* ]]; then + cp "$remote_path" "$out_path" + return $? + fi + + local failed=false + if machine_has "curl"; then + downloadcurl "$remote_path" "$out_path" || failed=true + elif machine_has "wget"; then + downloadwget "$remote_path" "$out_path" || failed=true + else + failed=true + fi + if [ "$failed" = true ]; then + say_verbose "Download failed: $remote_path" + return 1 + fi + return 0 +} + +downloadcurl() { + eval $invocation + local remote_path="$1" + local out_path="${2:-}" + + # Append feed_credential as late as possible before calling curl to avoid logging feed_credential + remote_path="${remote_path}${feed_credential}" + + local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs " + local failed=false + if [ -z "$out_path" ]; then + curl $curl_options "$remote_path" || failed=true + else + curl $curl_options -o "$out_path" "$remote_path" || failed=true + fi + if [ "$failed" = true ]; then + say_verbose "Curl download failed" + return 1 + fi + return 0 +} + +downloadwget() { + eval $invocation + local remote_path="$1" + local out_path="${2:-}" + + # Append feed_credential as late as possible before calling wget to avoid logging feed_credential + remote_path="${remote_path}${feed_credential}" + local wget_options="--tries 20 --waitretry 2 --connect-timeout 15 " + local failed=false + if [ -z "$out_path" ]; then + wget -q $wget_options -O - "$remote_path" || failed=true + else + wget $wget_options -O "$out_path" "$remote_path" || failed=true + fi + if [ "$failed" = true ]; then + say_verbose "Wget download failed" + return 1 + fi + return 0 +} + +calculate_vars() { + eval $invocation + valid_legacy_download_link=true + + normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")" + say_verbose "normalized_architecture=$normalized_architecture" + + specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")" + specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version")" + say_verbose "specific_version=$specific_version" + if [ -z "$specific_version" ]; then + say_err "Could not resolve version information." + return 1 + fi + + download_link="$(construct_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" + say_verbose "Constructed primary named payload URL: $download_link" + + legacy_download_link="$(construct_legacy_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" || valid_legacy_download_link=false + + if [ "$valid_legacy_download_link" = true ]; then + say_verbose "Constructed legacy named payload URL: $legacy_download_link" + else + say_verbose "Cound not construct a legacy_download_link; omitting..." + fi + + install_root="$(resolve_installation_path "$install_dir")" + say_verbose "InstallRoot: $install_root" +} + +install_dotnet() { + eval $invocation + local download_failed=false + local asset_name='' + local asset_relative_path='' + + if [[ "$runtime" == "dotnet" ]]; then + asset_relative_path="shared/Microsoft.NETCore.App" + asset_name=".NET Core Runtime" + elif [[ "$runtime" == "aspnetcore" ]]; then + asset_relative_path="shared/Microsoft.AspNetCore.App" + asset_name="ASP.NET Core Runtime" + elif [ -z "$runtime" ]; then + asset_relative_path="sdk" + asset_name=".NET Core SDK" + else + say_err "Invalid value for \$runtime" + return 1 + fi + + # Check if the SDK version is already installed. + if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then + say "$asset_name version $specific_version is already installed." + return 0 + fi + + mkdir -p "$install_root" + zip_path="$(mktemp "$temporary_file_template")" + say_verbose "Zip path: $zip_path" + + say "Downloading link: $download_link" + + # Failures are normal in the non-legacy case for ultimately legacy downloads. + # Do not output to stderr, since output to stderr is considered an error. + download "$download_link" "$zip_path" 2>&1 || download_failed=true + + # if the download fails, download the legacy_download_link + if [ "$download_failed" = true ]; then + say "Cannot download: $download_link" + + if [ "$valid_legacy_download_link" = true ]; then + download_failed=false + download_link="$legacy_download_link" + zip_path="$(mktemp "$temporary_file_template")" + say_verbose "Legacy zip path: $zip_path" + say "Downloading legacy link: $download_link" + download "$download_link" "$zip_path" 2>&1 || download_failed=true + + if [ "$download_failed" = true ]; then + say "Cannot download: $download_link" + fi + fi + fi + + if [ "$download_failed" = true ]; then + say_err "Could not find/download: \`$asset_name\` with version = $specific_version" + say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support" + return 1 + fi + + say "Extracting zip from $download_link" + extract_dotnet_package "$zip_path" "$install_root" + + # Check if the SDK version is installed; if not, fail the installation. + # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed. + if [[ $specific_version == *"rtm"* || $specific_version == *"servicing"* ]]; then + IFS='-' + read -ra verArr <<< "$specific_version" + release_version="${verArr[0]}" + unset IFS; + say_verbose "Checking installation: version = $release_version" + if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$release_version"; then + return 0 + fi + fi + + # Check if the standard SDK version is installed. + say_verbose "Checking installation: version = $specific_product_version" + if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_product_version"; then + return 0 + fi + + say_err "\`$asset_name\` with version = $specific_product_version failed to install with an unknown error." + return 1 +} + +args=("$@") + +local_version_file_relative_path="/.version" +bin_folder_relative_path="" +temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX" + +channel="LTS" +version="Latest" +json_file="" +install_dir="" +architecture="" +dry_run=false +no_path=false +no_cdn=false +azure_feed="https://dotnetcli.azureedge.net/dotnet" +uncached_feed="https://dotnetcli.blob.core.windows.net/dotnet" +feed_credential="" +verbose=false +runtime="" +runtime_id="" +override_non_versioned_files=true +non_dynamic_parameters="" + +while [ $# -ne 0 ] +do + name="$1" + case "$name" in + -c|--channel|-[Cc]hannel) + shift + channel="$1" + ;; + -v|--version|-[Vv]ersion) + shift + version="$1" + ;; + -i|--install-dir|-[Ii]nstall[Dd]ir) + shift + install_dir="$1" + ;; + --arch|--architecture|-[Aa]rch|-[Aa]rchitecture) + shift + architecture="$1" + ;; + --shared-runtime|-[Ss]hared[Rr]untime) + say_warning "The --shared-runtime flag is obsolete and may be removed in a future version of this script. The recommended usage is to specify '--runtime dotnet'." + if [ -z "$runtime" ]; then + runtime="dotnet" + fi + ;; + --runtime|-[Rr]untime) + shift + runtime="$1" + if [[ "$runtime" != "dotnet" ]] && [[ "$runtime" != "aspnetcore" ]]; then + say_err "Unsupported value for --runtime: '$1'. Valid values are 'dotnet' and 'aspnetcore'." + if [[ "$runtime" == "windowsdesktop" ]]; then + say_err "WindowsDesktop archives are manufactured for Windows platforms only." + fi + exit 1 + fi + ;; + --dry-run|-[Dd]ry[Rr]un) + dry_run=true + ;; + --no-path|-[Nn]o[Pp]ath) + no_path=true + non_dynamic_parameters+=" $name" + ;; + --verbose|-[Vv]erbose) + verbose=true + non_dynamic_parameters+=" $name" + ;; + --no-cdn|-[Nn]o[Cc]dn) + no_cdn=true + non_dynamic_parameters+=" $name" + ;; + --azure-feed|-[Aa]zure[Ff]eed) + shift + azure_feed="$1" + non_dynamic_parameters+=" $name "\""$1"\""" + ;; + --uncached-feed|-[Uu]ncached[Ff]eed) + shift + uncached_feed="$1" + non_dynamic_parameters+=" $name "\""$1"\""" + ;; + --feed-credential|-[Ff]eed[Cc]redential) + shift + feed_credential="$1" + non_dynamic_parameters+=" $name "\""$1"\""" + ;; + --runtime-id|-[Rr]untime[Ii]d) + shift + runtime_id="$1" + non_dynamic_parameters+=" $name "\""$1"\""" + ;; + --jsonfile|-[Jj][Ss]on[Ff]ile) + shift + json_file="$1" + ;; + --skip-non-versioned-files|-[Ss]kip[Nn]on[Vv]ersioned[Ff]iles) + override_non_versioned_files=false + non_dynamic_parameters+=" $name" + ;; + -?|--?|-h|--help|-[Hh]elp) + script_name="$(basename "$0")" + echo ".NET Tools Installer" + echo "Usage: $script_name [-c|--channel ] [-v|--version ] [-p|--prefix ]" + echo " $script_name -h|-?|--help" + echo "" + echo "$script_name is a simple command line interface for obtaining dotnet cli." + echo "" + echo "Options:" + echo " -c,--channel Download from the channel specified, Defaults to \`$channel\`." + echo " -Channel" + echo " Possible values:" + echo " - Current - most current release" + echo " - LTS - most current supported release" + echo " - 2-part version in a format A.B - represents a specific release" + echo " examples: 2.0; 1.0" + echo " - Branch name" + echo " examples: release/2.0.0; Master" + echo " Note: The version parameter overrides the channel parameter." + echo " -v,--version Use specific VERSION, Defaults to \`$version\`." + echo " -Version" + echo " Possible values:" + echo " - latest - most latest build on specific channel" + echo " - coherent - most latest coherent build on specific channel" + echo " coherent applies only to SDK downloads" + echo " - 3-part version in a format A.B.C - represents specific version of build" + echo " examples: 2.0.0-preview2-006120; 1.1.0" + echo " -i,--install-dir Install under specified location (see Install Location below)" + echo " -InstallDir" + echo " --architecture Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`." + echo " --arch,-Architecture,-Arch" + echo " Possible values: x64, arm, and arm64" + echo " --runtime Installs a shared runtime only, without the SDK." + echo " -Runtime" + echo " Possible values:" + echo " - dotnet - the Microsoft.NETCore.App shared runtime" + echo " - aspnetcore - the Microsoft.AspNetCore.App shared runtime" + echo " --dry-run,-DryRun Do not perform installation. Display download link." + echo " --no-path, -NoPath Do not set PATH for the current process." + echo " --verbose,-Verbose Display diagnostics information." + echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user." + echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user." + echo " --feed-credential,-FeedCredential Azure feed shared access token. This parameter typically is not specified." + echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable." + echo " -SkipNonVersionedFiles" + echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly." + echo " --jsonfile Determines the SDK version from a user specified global.json file." + echo " Note: global.json must have a value for 'SDK:Version'" + echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)." + echo " -RuntimeId" + echo " -?,--?,-h,--help,-Help Shows this help message" + echo "" + echo "Obsolete parameters:" + echo " --shared-runtime The recommended alternative is '--runtime dotnet'." + echo " This parameter is obsolete and may be removed in a future version of this script." + echo " Installs just the shared runtime bits, not the entire SDK." + echo "" + echo "Install Location:" + echo " Location is chosen in following order:" + echo " - --install-dir option" + echo " - Environmental variable DOTNET_INSTALL_DIR" + echo " - $HOME/.dotnet" + exit 0 + ;; + *) + say_err "Unknown argument \`$name\`" + exit 1 + ;; + esac + + shift +done + +if [ "$no_cdn" = true ]; then + azure_feed="$uncached_feed" +fi + +say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" +say "- The SDK needs to be installed without user interaction and without admin rights." +say "- The SDK installation doesn't need to persist across multiple CI runs." +say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n" + +check_min_reqs +calculate_vars +script_name=$(basename "$0") + +if [ "$dry_run" = true ]; then + say "Payload URLs:" + say "Primary named payload URL: $download_link" + if [ "$valid_legacy_download_link" = true ]; then + say "Legacy named payload URL: $legacy_download_link" + fi + repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\""" + if [[ "$runtime" == "dotnet" ]]; then + repeatable_command+=" --runtime "\""dotnet"\""" + elif [[ "$runtime" == "aspnetcore" ]]; then + repeatable_command+=" --runtime "\""aspnetcore"\""" + fi + repeatable_command+="$non_dynamic_parameters" + say "Repeatable invocation: $repeatable_command" + exit 0 +fi + +install_dotnet + +bin_path="$(get_absolute_path "$(combine_paths "$install_root" "$bin_folder_relative_path")")" +if [ "$no_path" = false ]; then + say "Adding to current process PATH: \`$bin_path\`. Note: This change will be visible only when sourcing script." + export PATH="$bin_path":"$PATH" +else + say "Binaries of dotnet can be found in $bin_path" +fi + +say "Note that the script does not resolve dependencies during installation." +say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section." +say "Installation finished successfully." From 4f05254095f9cdf390630c7f720b0eeba1d81bb1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 31 Dec 2020 17:33:42 +0100 Subject: [PATCH 062/157] Enable use of C# 9 across all projects --- .editorconfig | 3 +++ Directory.Build.props | 5 +++++ MoreLinq.Test/MoreLinq.Test.csproj | 5 ++++- MoreLinq.sln | 1 + MoreLinq/MoreLinq.csproj | 1 - bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj | 1 - 6 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Directory.Build.props diff --git a/.editorconfig b/.editorconfig index 8078f30fa..46779ac64 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,9 @@ trim_trailing_whitespace = true [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 +[*.Build.{props,targets}] +indent_size = 2 + [*.{sln}] indent_style = tab diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..fc25ecef2 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + 9 + + diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 7e5f2afd9..0145db46d 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -13,7 +13,6 @@ library MoreLinq.Test.Program - 8 618 @@ -27,6 +26,10 @@ all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers all diff --git a/MoreLinq.sln b/MoreLinq.sln index 7d4944e58..bab71edee 100644 --- a/MoreLinq.sln +++ b/MoreLinq.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution build.sh = build.sh builddocs.cmd = builddocs.cmd COPYING.txt = COPYING.txt + Directory.Build.props = Directory.Build.props global.json = global.json msbuild.cmd = msbuild.cmd pack.cmd = pack.cmd diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index b43179b00..aa993da4a 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -120,7 +120,6 @@ 3.3.2 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 - 9 enable true false From 2f7cd9789ee5096a5f4839536e4811f5a9d95c86 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 6 Jan 2021 22:01:51 +0100 Subject: [PATCH 065/157] Fix XML encoding of ">" in project file --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 2296e137a..f958f8280 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -264,7 +264,7 @@ - + From ab9e5617139345936eab383828babafe00d4eb30 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 6 Jan 2021 22:24:23 +0100 Subject: [PATCH 066/157] Rewrite some conditions as object patterns --- MoreLinq/Batch.cs | 4 ++-- MoreLinq/ToDataTable.cs | 3 +-- bld/ExtensionsGenerator/Program.cs | 8 ++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index b55797335..12f20030d 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -87,7 +87,7 @@ public static IEnumerable Batch(this IEnumerable collection when collection.Count == 0: + case ICollection { Count: 0 }: { return Enumerable.Empty(); } @@ -100,7 +100,7 @@ public static IEnumerable Batch(this IEnumerable collection when collection.Count == 0: + case IReadOnlyCollection { Count: 0 }: { return Enumerable.Empty(); } diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index fb69640e4..dfbf0eca2 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -140,8 +140,7 @@ static IEnumerable PrepareMemberInfos(ICollection public virtual int CompareTo(TypeKey other) => ReferenceEquals(this, other) ? 0 : other == null ? 1 - : Parameters.Count.CompareTo(other.Parameters.Count) is int lc && lc != 0 ? lc - : string.Compare(Name, other.Name, StringComparison.Ordinal) is int nc && nc != 0 ? nc + : Parameters.Count.CompareTo(other.Parameters.Count) is {} lc and not 0 ? lc + : string.Compare(Name, other.Name, StringComparison.Ordinal) is {} nc and not 0 ? nc : CompareParameters(other); protected virtual int CompareParameters(TypeKey other) => @@ -463,10 +463,10 @@ protected override int CompareParameters(TypeKey other) { if (other is ArrayTypeKey a) { - if (Ranks.Count.CompareTo(a.Ranks.Count) is int rlc && rlc != 0) + if (Ranks.Count.CompareTo(a.Ranks.Count) is {} rlc and not 0) return rlc; if (Ranks.Zip(a.Ranks, (us, them) => (Us: us, Them: them)) - .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is int rc && rc != 0) + .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is {} rc and not 0) return rc; } From b5749f2710d9a271686f8d1b6c8e254c26695714 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 6 Jan 2021 22:26:35 +0100 Subject: [PATCH 067/157] Remove redundant discards --- MoreLinq.Test/FlattenTest.cs | 6 +++--- MoreLinq/Experimental/Memoize.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index 9c28bad9b..6bf60394d 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -331,7 +331,7 @@ public void FlattenSelector() { switch (obj) { - case string _: + case string: return null; case IEnumerable inner: return inner; @@ -374,7 +374,7 @@ public void FlattenSelectorFilteringOnlyIntegers() { switch (obj) { - case int _: + case int: return null; case IEnumerable inner: return inner; @@ -412,7 +412,7 @@ public void FlattenSelectorWithTree() { switch (obj) { - case int _: + case int: return null; case Tree tree: return new object[] { tree.Left, tree.Value, tree.Right }; diff --git a/MoreLinq/Experimental/Memoize.cs b/MoreLinq/Experimental/Memoize.cs index 2ac99aa41..0526cec4f 100644 --- a/MoreLinq/Experimental/Memoize.cs +++ b/MoreLinq/Experimental/Memoize.cs @@ -52,9 +52,9 @@ public static IEnumerable Memoize(this IEnumerable source) switch (source) { case null: throw new ArgumentNullException(nameof(source)); - case ICollection _: // ... - case IReadOnlyCollection _: // ... - case MemoizedEnumerable _: return source; + case ICollection : // ... + case IReadOnlyCollection: // ... + case MemoizedEnumerable : return source; default: return new MemoizedEnumerable(source); } } From 8651b5160a2e5b2de7d2f287d7708841ba2bca7d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 6 Jan 2021 22:36:05 +0100 Subject: [PATCH 068/157] Remove unused imports --- MoreLinq.Test/MaxByTest.cs | 1 - MoreLinq.Test/MinByTest.cs | 1 - MoreLinq.Test/SampleData.cs | 1 - MoreLinq/Collections/Dictionary.cs | 1 - 4 files changed, 4 deletions(-) diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 3e9a7fc0e..737d10e93 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -18,7 +18,6 @@ namespace MoreLinq.Test { using System; - using System.Collections.Generic; using NUnit.Framework; [TestFixture] diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index acd90eb01..934e49dd5 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -18,7 +18,6 @@ namespace MoreLinq.Test { using System; - using System.Collections.Generic; using NUnit.Framework; [TestFixture] diff --git a/MoreLinq.Test/SampleData.cs b/MoreLinq.Test/SampleData.cs index b49e36e9d..df74bed9b 100644 --- a/MoreLinq.Test/SampleData.cs +++ b/MoreLinq.Test/SampleData.cs @@ -18,7 +18,6 @@ namespace MoreLinq.Test { using System; - using System.Collections.Generic; using System.Collections.ObjectModel; /// diff --git a/MoreLinq/Collections/Dictionary.cs b/MoreLinq/Collections/Dictionary.cs index fad4e1a03..796fee0b5 100644 --- a/MoreLinq/Collections/Dictionary.cs +++ b/MoreLinq/Collections/Dictionary.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Collections { - using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; From 1b76cdb5c20c4c9e42fc0328044dd8bd764fb38e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 6 Jan 2021 23:26:52 +0100 Subject: [PATCH 069/157] Replace using statement with declaration in Move --- MoreLinq/Move.cs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/MoreLinq/Move.cs b/MoreLinq/Move.cs index f95f022d4..ea6aaa808 100644 --- a/MoreLinq/Move.cs +++ b/MoreLinq/Move.cs @@ -66,26 +66,25 @@ IEnumerable _(int bufferStartIndex, int bufferSize, int bufferYieldIndex) bool hasMore = true; bool MoveNext(IEnumerator e) => hasMore && (hasMore = e.MoveNext()); - using (var e = source.GetEnumerator()) - { - for (var i = 0; i < bufferStartIndex && MoveNext(e); i++) - yield return e.Current; + using var e = source.GetEnumerator(); - var buffer = new T[bufferSize]; - var length = 0; + for (var i = 0; i < bufferStartIndex && MoveNext(e); i++) + yield return e.Current; - for (; length < bufferSize && MoveNext(e); length++) - buffer[length] = e.Current; + var buffer = new T[bufferSize]; + var length = 0; - for (var i = 0; i < bufferYieldIndex && MoveNext(e); i++) - yield return e.Current; + for (; length < bufferSize && MoveNext(e); length++) + buffer[length] = e.Current; - for (var i = 0; i < length; i++) - yield return buffer[i]; + for (var i = 0; i < bufferYieldIndex && MoveNext(e); i++) + yield return e.Current; - while (MoveNext(e)) - yield return e.Current; - } + for (var i = 0; i < length; i++) + yield return buffer[i]; + + while (MoveNext(e)) + yield return e.Current; } } } From 1fd5cd616b49d7aa11deebe4db43ff5cc8f5ceb6 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 7 Jan 2021 00:12:04 +0100 Subject: [PATCH 070/157] Simpler extensions generator with namespace & program class (#787) --- bld/ExtensionsGenerator/Program.cs | 745 ++++++++++++++--------------- 1 file changed, 368 insertions(+), 377 deletions(-) diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index abf16d709..280b2a4ec 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -14,277 +14,284 @@ // #endregion -namespace MoreLinq.ExtensionsGenerator +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +try { - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Reflection; - using System.Text.RegularExpressions; - using Microsoft.CodeAnalysis; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CodeAnalysis.CSharp.Syntax; - using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - - static class Program - { - static void Run(IEnumerable args) - { - var dir = Directory.GetCurrentDirectory(); + Run(args); + return 0; +} +catch (Exception e) +{ + Console.Error.WriteLine(e.ToString()); + return 0xbad; +} + +static void Run(IEnumerable args) +{ + var dir = Directory.GetCurrentDirectory(); - string includePattern = null; - string excludePattern = null; - var debug = false; - var usings = new List(); - var noClassLead = false; + string includePattern = null; + string excludePattern = null; + var debug = false; + var usings = new List(); + var noClassLead = false; - using (var arg = args.GetEnumerator()) + using (var arg = args.GetEnumerator()) + { + while (arg.MoveNext()) + { + switch (arg.Current) { - while (arg.MoveNext()) - { - switch (arg.Current) - { - case "-i": - case "--include": - includePattern = Read(arg, MissingArgValue); - break; - case "-x": - case "--exclude": - excludePattern = Read(arg, MissingArgValue); - break; - case "-u": - case "--using": - usings.Add(Read(arg, MissingArgValue)); - break; - case "--no-class-lead": - noClassLead = true; - break; - case "-d": - case "--debug": - debug = true; - break; - case "": - continue; - default: - dir = arg.Current[0] != '-' - ? arg.Current - : throw new Exception("Invalid argument: " + arg.Current); - break; - } - } - - static Exception MissingArgValue() => - new InvalidOperationException("Missing argument value."); + case "-i": + case "--include": + includePattern = Read(arg, MissingArgValue); + break; + case "-x": + case "--exclude": + excludePattern = Read(arg, MissingArgValue); + break; + case "-u": + case "--using": + usings.Add(Read(arg, MissingArgValue)); + break; + case "--no-class-lead": + noClassLead = true; + break; + case "-d": + case "--debug": + debug = true; + break; + case "": + continue; + default: + dir = arg.Current[0] != '-' + ? arg.Current + : throw new Exception("Invalid argument: " + arg.Current); + break; } + } - static Func - PredicateFromPattern(string pattern, bool @default) => - string.IsNullOrEmpty(pattern) - ? delegate { return @default; } - : new Func(new Regex(pattern).IsMatch); + static Exception MissingArgValue() => + new InvalidOperationException("Missing argument value."); + } - var includePredicate = PredicateFromPattern(includePattern, true); - var excludePredicate = PredicateFromPattern(excludePattern, false); + static Func + PredicateFromPattern(string pattern, bool @default) => + string.IsNullOrEmpty(pattern) + ? delegate { return @default; } + : new Func(new Regex(pattern).IsMatch); - var thisAssemblyName = typeof(Program).GetTypeInfo().Assembly.GetName(); + var includePredicate = PredicateFromPattern(includePattern, true); + var excludePredicate = PredicateFromPattern(excludePattern, false); - // - // Type abbreviations are used to abbreviate all generic type - // parameters into a letter from the alphabet. So for example, a - // method with generic type parameters `TSource` and `TResult` will - // become `a` and `b`. This is used later for sorting and stabilizes - // the sort irrespective of how the type parameters are named or - // renamed in the source. - // + var thisAssemblyName = typeof(TypeKey).GetTypeInfo().Assembly.GetName(); - var abbreviatedTypeNodes = Enumerable - .Range(0, 26) - .Select(a => (char) ('a' + a)) - .Select(ch => new SimpleTypeKey(ch.ToString())) - .ToArray(); + // + // Type abbreviations are used to abbreviate all generic type + // parameters into a letter from the alphabet. So for example, a + // method with generic type parameters `TSource` and `TResult` will + // become `a` and `b`. This is used later for sorting and stabilizes + // the sort irrespective of how the type parameters are named or + // renamed in the source. + // - var q = + var abbreviatedTypeNodes = Enumerable + .Range(0, 26) + .Select(a => (char) ('a' + a)) + .Select(ch => new SimpleTypeKey(ch.ToString())) + .ToArray(); - from ms in new[] - { - from fp in Directory.EnumerateFiles(dir, "*.cs") - where !excludePredicate(fp) && includePredicate(fp) - orderby fp - // - // Find all class declarations where class name is - // `MoreEnumerable`. Note that this is irrespective of - // namespace, which is out of sheer laziness. - // - from cd in - CSharpSyntaxTree - .ParseText(File.ReadAllText(fp), CSharpParseOptions.Default.WithPreprocessorSymbols("MORELINQ")) - .GetRoot() - .SyntaxTree - .GetCompilationUnitRoot() - .DescendantNodes().OfType() - where (string) cd.Identifier.Value == "MoreEnumerable" - // - // Get all method declarations where method: - // - // - has at least one parameter - // - extends a type (first parameter uses the `this` modifier) - // - is public - // - isn't marked as being obsolete - // - from md in cd.DescendantNodes().OfType() - let mn = (string) md.Identifier.Value - where md.ParameterList.Parameters.Count > 0 - && md.ParameterList.Parameters.First().Modifiers.Any(m => (string)m.Value == "this") - && md.Modifiers.Any(m => (string)m.Value == "public") - && md.AttributeLists.SelectMany(al => al.Attributes).All(a => a.Name.ToString() != "Obsolete") - // - // Build a dictionary of type abbreviations (e.g. TSource -> a, - // TResult -> b, etc.) for the method's type parameters. If the - // method is non-generic, then this will be null! - // - let typeParameterAbbreviationByName = - md.TypeParameterList - ?.Parameters - .Select((e, i) => (Original: e.Identifier.ValueText, Alias: abbreviatedTypeNodes[i])) - .ToDictionary(e => e.Original, e => e.Alias) - // - // Put everything together. While we mostly care about the - // method declaration, the rest of the information is captured - // for the purpose of stabilizing the code generation order and - // debugging (--debug). - // - select new - { - Syntax = md, - Name = md.Identifier.ToString(), - TypeParameterCount = md.TypeParameterList?.Parameters.Count ?? 0, - TypeParameterAbbreviationByName = typeParameterAbbreviationByName, - ParameterCount = md.ParameterList.Parameters.Count, - SortableParameterTypes = - from p in md.ParameterList.Parameters - select CreateTypeKey(p.Type, - n => typeParameterAbbreviationByName != null - && typeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), - } - } - from e in ms.Select((m, i) => (SourceOrder: i + 1, Method: m)) - orderby - e.Method.Name, - e.Method.TypeParameterCount, - e.Method.ParameterCount, - new TupleTypeKey(ImmutableList.CreateRange(e.Method.SortableParameterTypes)) + var q = + + from ms in new[] + { + from fp in Directory.EnumerateFiles(dir, "*.cs") + where !excludePredicate(fp) && includePredicate(fp) + orderby fp + // + // Find all class declarations where class name is + // `MoreEnumerable`. Note that this is irrespective of + // namespace, which is out of sheer laziness. + // + from cd in + CSharpSyntaxTree + .ParseText(File.ReadAllText(fp), CSharpParseOptions.Default.WithPreprocessorSymbols("MORELINQ")) + .GetRoot() + .SyntaxTree + .GetCompilationUnitRoot() + .DescendantNodes().OfType() + where (string) cd.Identifier.Value == "MoreEnumerable" + // + // Get all method declarations where method: + // + // - has at least one parameter + // - extends a type (first parameter uses the `this` modifier) + // - is public + // - isn't marked as being obsolete + // + from md in cd.DescendantNodes().OfType() + let mn = (string) md.Identifier.Value + where md.ParameterList.Parameters.Count > 0 + && md.ParameterList.Parameters.First().Modifiers.Any(m => (string)m.Value == "this") + && md.Modifiers.Any(m => (string)m.Value == "public") + && md.AttributeLists.SelectMany(al => al.Attributes).All(a => a.Name.ToString() != "Obsolete") + // + // Build a dictionary of type abbreviations (e.g. TSource -> a, + // TResult -> b, etc.) for the method's type parameters. If the + // method is non-generic, then this will be null! + // + let typeParameterAbbreviationByName = + md.TypeParameterList + ?.Parameters + .Select((e, i) => (Original: e.Identifier.ValueText, Alias: abbreviatedTypeNodes[i])) + .ToDictionary(e => e.Original, e => e.Alias) + // + // Put everything together. While we mostly care about the + // method declaration, the rest of the information is captured + // for the purpose of stabilizing the code generation order and + // debugging (--debug). + // select new { - e.Method, - e.SourceOrder, - }; + Syntax = md, + Name = md.Identifier.ToString(), + TypeParameterCount = md.TypeParameterList?.Parameters.Count ?? 0, + TypeParameterAbbreviationByName = typeParameterAbbreviationByName, + ParameterCount = md.ParameterList.Parameters.Count, + SortableParameterTypes = + from p in md.ParameterList.Parameters + select CreateTypeKey(p.Type, + n => typeParameterAbbreviationByName != null + && typeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), + } + } + from e in ms.Select((m, i) => (SourceOrder: i + 1, Method: m)) + orderby + e.Method.Name, + e.Method.TypeParameterCount, + e.Method.ParameterCount, + new TupleTypeKey(ImmutableList.CreateRange(e.Method.SortableParameterTypes)) + select new + { + e.Method, + e.SourceOrder, + }; - q = q.ToArray(); + q = q.ToArray(); - if (debug) + if (debug) + { + var ms = + // + // Example of what this is designed to produce: + // + // 083: Lag(IEnumerable, int, Func) where a = TSource, b = TResult + // 084: Lag(IEnumerable, int, a, Func) where a = TSource, b = TResult + // 085: Lead(IEnumerable, int, Func) where a = TSource, b = TResult + // 086: Lead(IEnumerable, int, a, Func) where a = TSource, b = TResult + // + from e in q + let m = e.Method + select new { - var ms = - // - // Example of what this is designed to produce: - // - // 083: Lag(IEnumerable, int, Func) where a = TSource, b = TResult - // 084: Lag(IEnumerable, int, a, Func) where a = TSource, b = TResult - // 085: Lead(IEnumerable, int, Func) where a = TSource, b = TResult - // 086: Lead(IEnumerable, int, a, Func) where a = TSource, b = TResult - // - from e in q - let m = e.Method - select new - { - m.Name, - - SourceOrder = e.SourceOrder.ToString("000", CultureInfo.InvariantCulture), - - TypeParameters = - m.TypeParameterCount == 0 - ? string.Empty - : "<" + string.Join(", ", from a in m.TypeParameterAbbreviationByName - select a.Value) + ">", - Parameters = - "(" + string.Join(", ", m.SortableParameterTypes) + ")", - - Abbreviations = - m.TypeParameterCount == 0 - ? string.Empty - : " where " + string.Join(", ", from a in m.TypeParameterAbbreviationByName - select a.Value + " = " + a.Key), - } - into e - select e.SourceOrder + ": " - + e.Name + e.TypeParameters + e.Parameters + e.Abbreviations; - - foreach (var m in ms) - Console.Error.WriteLine(m); + m.Name, + + SourceOrder = e.SourceOrder.ToString("000", CultureInfo.InvariantCulture), + + TypeParameters = + m.TypeParameterCount == 0 + ? string.Empty + : "<" + string.Join(", ", from a in m.TypeParameterAbbreviationByName + select a.Value) + ">", + Parameters = + "(" + string.Join(", ", m.SortableParameterTypes) + ")", + + Abbreviations = + m.TypeParameterCount == 0 + ? string.Empty + : " where " + string.Join(", ", from a in m.TypeParameterAbbreviationByName + select a.Value + " = " + a.Key), } + into e + select e.SourceOrder + ": " + + e.Name + e.TypeParameters + e.Parameters + e.Abbreviations; - var indent = new string(' ', 4); - var indent2 = indent + indent; - var indent3 = indent2 + indent; + foreach (var m in ms) + Console.Error.WriteLine(m); + } - var baseImports = new [] - { - "System", - "System.CodeDom.Compiler", - "System.Collections.Generic", - "System.Diagnostics.CodeAnalysis", - }; - - var imports = - from ns in baseImports.Concat(usings) - select indent + $"using {ns};"; - - var classes = - from md in q - select md.Method.Syntax into md - group md by (string) md.Identifier.Value into g - select new - { - Name = g.Key, - Overloads = - from md in g - select - MethodDeclaration(md.ReturnType, md.Identifier) - .WithAttributeLists(md.AttributeLists) - .WithModifiers(md.Modifiers) - .WithTypeParameterList(md.TypeParameterList) - .WithConstraintClauses(md.ConstraintClauses) - .WithParameterList(md.ParameterList) - .WithExpressionBody( - ArrowExpressionClause( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("MoreEnumerable"), - IdentifierName(md.Identifier)), - ArgumentList( - SeparatedList( - from p in md.ParameterList.Parameters - select Argument(IdentifierName(p.Identifier)), - Enumerable.Repeat(ParseToken(",").WithTrailingTrivia(Space), - md.ParameterList.Parameters.Count - 1)))) - .WithLeadingTrivia(Space)) - .WithLeadingTrivia(Whitespace(indent3))) - .WithSemicolonToken(ParseToken(";").WithTrailingTrivia(LineFeed)) - } - into m - select (!noClassLead ? $@" - /// {m.Name} extension. - - [GeneratedCode(""{thisAssemblyName.Name}"", ""{thisAssemblyName.Version}"")]" : null) + $@" - public static partial class {m.Name}Extension - {{ + var indent = new string(' ', 4); + var indent2 = indent + indent; + var indent3 = indent2 + indent; + + var baseImports = new [] + { + "System", + "System.CodeDom.Compiler", + "System.Collections.Generic", + "System.Diagnostics.CodeAnalysis", + }; + + var imports = + from ns in baseImports.Concat(usings) + select indent + $"using {ns};"; + + var classes = + from md in q + select md.Method.Syntax into md + group md by (string) md.Identifier.Value into g + select new + { + Name = g.Key, + Overloads = + from md in g + select + MethodDeclaration(md.ReturnType, md.Identifier) + .WithAttributeLists(md.AttributeLists) + .WithModifiers(md.Modifiers) + .WithTypeParameterList(md.TypeParameterList) + .WithConstraintClauses(md.ConstraintClauses) + .WithParameterList(md.ParameterList) + .WithExpressionBody( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("MoreEnumerable"), + IdentifierName(md.Identifier)), + ArgumentList( + SeparatedList( + from p in md.ParameterList.Parameters + select Argument(IdentifierName(p.Identifier)), + Enumerable.Repeat(ParseToken(",").WithTrailingTrivia(Space), + md.ParameterList.Parameters.Count - 1)))) + .WithLeadingTrivia(Space)) + .WithLeadingTrivia(Whitespace(indent3))) + .WithSemicolonToken(ParseToken(";").WithTrailingTrivia(LineFeed)) + } + into m + select (!noClassLead ? $@" +/// {m.Name} extension. + +[GeneratedCode(""{thisAssemblyName.Name}"", ""{thisAssemblyName.Version}"")]" : null) + $@" +public static partial class {m.Name}Extension +{{ {string.Join(null, from mo in m.Overloads select mo.ToFullString())} - }}"; +}}"; - var template = $@" + var template = $@" #region License and Terms // MoreLINQ - Extensions to LINQ to Objects // @@ -319,158 +326,142 @@ namespace MoreLinq.Extensions {string.Join("\n", imports)} {string.Join("\n", classes)} }} - "; - - Console.WriteLine(template.Trim() - // normalize line endings - .Replace("\r", string.Empty) - .Replace("\n", Environment.NewLine)); - } + "; - public static TypeKey CreateTypeKey(TypeSyntax root, - Func abbreviator = null) - { - return Walk(root ?? throw new ArgumentNullException(nameof(root))); + Console.WriteLine(template.Trim() + // normalize line endings + .Replace("\r", string.Empty) + .Replace("\n", Environment.NewLine)); +} - TypeKey Walk(TypeSyntax ts) => ts switch - { - PredefinedTypeSyntax pts => new SimpleTypeKey(pts.ToString()), - NullableTypeSyntax nts => new NullableTypeKey(Walk(nts.ElementType)), - IdentifierNameSyntax ins => abbreviator?.Invoke(ins.Identifier.ValueText) - ?? new SimpleTypeKey(ins.ToString()), - GenericNameSyntax gns => - new GenericTypeKey(gns.Identifier.ToString(), - ImmutableList.CreateRange(gns.TypeArgumentList.Arguments.Select(Walk))), - ArrayTypeSyntax ats => - new ArrayTypeKey(Walk(ats.ElementType), - ImmutableList.CreateRange(from rs in ats.RankSpecifiers - select rs.Rank)), - TupleTypeSyntax tts => - new TupleTypeKey(ImmutableList.CreateRange(from te in tts.Elements - select Walk(te.Type))), - _ => throw new NotSupportedException("Unhandled type: " + ts) - }; - } +static TypeKey CreateTypeKey(TypeSyntax root, + Func abbreviator = null) +{ + return Walk(root ?? throw new ArgumentNullException(nameof(root))); - static T Read(IEnumerator e, Func errorFactory = null) - { - if (!e.MoveNext()) - throw errorFactory?.Invoke() ?? new InvalidOperationException(); - return e.Current; - } + TypeKey Walk(TypeSyntax ts) => ts switch + { + PredefinedTypeSyntax pts => new SimpleTypeKey(pts.ToString()), + NullableTypeSyntax nts => new NullableTypeKey(Walk(nts.ElementType)), + IdentifierNameSyntax ins => abbreviator?.Invoke(ins.Identifier.ValueText) + ?? new SimpleTypeKey(ins.ToString()), + GenericNameSyntax gns => + new GenericTypeKey(gns.Identifier.ToString(), + ImmutableList.CreateRange(gns.TypeArgumentList.Arguments.Select(Walk))), + ArrayTypeSyntax ats => + new ArrayTypeKey(Walk(ats.ElementType), + ImmutableList.CreateRange(from rs in ats.RankSpecifiers + select rs.Rank)), + TupleTypeSyntax tts => + new TupleTypeKey(ImmutableList.CreateRange(from te in tts.Elements + select Walk(te.Type))), + _ => throw new NotSupportedException("Unhandled type: " + ts) + }; +} - static int Main(string[] args) - { - try - { - Run(args); - return 0; - } - catch (Exception e) - { - Console.Error.WriteLine(e.ToString()); - return 0xbad; - } - } - } +static T Read(IEnumerator e, Func errorFactory = null) +{ + if (!e.MoveNext()) + throw errorFactory?.Invoke() ?? new InvalidOperationException(); + return e.Current; +} - // - // Logical type nodes designed to be structurally sortable based on: - // - // - Type parameter count - // - Name - // - Array rank, if an array - // - Each type parameter (recursively) - // +// +// Logical type nodes designed to be structurally sortable based on: +// +// - Type parameter count +// - Name +// - Array rank, if an array +// - Each type parameter (recursively) +// - abstract class TypeKey : IComparable - { - protected TypeKey(string name) => Name = name; +abstract class TypeKey : IComparable +{ + protected TypeKey(string name) => Name = name; - public string Name { get; } - public abstract ImmutableList Parameters { get; } + public string Name { get; } + public abstract ImmutableList Parameters { get; } - public virtual int CompareTo(TypeKey other) - => ReferenceEquals(this, other) ? 0 - : other == null ? 1 - : Parameters.Count.CompareTo(other.Parameters.Count) is {} lc and not 0 ? lc - : string.Compare(Name, other.Name, StringComparison.Ordinal) is {} nc and not 0 ? nc - : CompareParameters(other); + public virtual int CompareTo(TypeKey other) + => ReferenceEquals(this, other) ? 0 + : other == null ? 1 + : Parameters.Count.CompareTo(other.Parameters.Count) is {} lc and not 0 ? lc + : string.Compare(Name, other.Name, StringComparison.Ordinal) is {} nc and not 0 ? nc + : CompareParameters(other); - protected virtual int CompareParameters(TypeKey other) => - Compare(Parameters, other.Parameters); + protected virtual int CompareParameters(TypeKey other) => + Compare(Parameters, other.Parameters); - protected static int Compare(IEnumerable a, IEnumerable b) => - a.Zip(b, (us, them) => (Us: us, Them: them)) - .Select(e => e.Us.CompareTo(e.Them)) - .FirstOrDefault(e => e != 0); - } + protected static int Compare(IEnumerable a, IEnumerable b) => + a.Zip(b, (us, them) => (Us: us, Them: them)) + .Select(e => e.Us.CompareTo(e.Them)) + .FirstOrDefault(e => e != 0); +} - sealed class SimpleTypeKey : TypeKey - { - public SimpleTypeKey(string name) : base(name) {} - public override string ToString() => Name; - public override ImmutableList Parameters => ImmutableList.Empty; - } +sealed class SimpleTypeKey : TypeKey +{ + public SimpleTypeKey(string name) : base(name) {} + public override string ToString() => Name; + public override ImmutableList Parameters => ImmutableList.Empty; +} - abstract class ParameterizedTypeKey : TypeKey - { - protected ParameterizedTypeKey(string name, TypeKey parameter) : - this(name, ImmutableList.Create(parameter)) {} +abstract class ParameterizedTypeKey : TypeKey +{ + protected ParameterizedTypeKey(string name, TypeKey parameter) : + this(name, ImmutableList.Create(parameter)) {} - protected ParameterizedTypeKey(string name, ImmutableList parameters) : - base(name) => Parameters = parameters; + protected ParameterizedTypeKey(string name, ImmutableList parameters) : + base(name) => Parameters = parameters; - public override ImmutableList Parameters { get; } - } + public override ImmutableList Parameters { get; } +} - sealed class GenericTypeKey : ParameterizedTypeKey - { - public GenericTypeKey(string name, ImmutableList parameters) : - base(name, parameters) {} +sealed class GenericTypeKey : ParameterizedTypeKey +{ + public GenericTypeKey(string name, ImmutableList parameters) : + base(name, parameters) {} - public override string ToString() => - Name + "<" + string.Join(", ", Parameters) + ">"; - } + public override string ToString() => + Name + "<" + string.Join(", ", Parameters) + ">"; +} - sealed class NullableTypeKey : ParameterizedTypeKey - { - public NullableTypeKey(TypeKey underlying) : base("?", underlying) {} - public override string ToString() => Parameters.Single() + "?"; - } +sealed class NullableTypeKey : ParameterizedTypeKey +{ + public NullableTypeKey(TypeKey underlying) : base("?", underlying) {} + public override string ToString() => Parameters.Single() + "?"; +} - sealed class TupleTypeKey : ParameterizedTypeKey - { - public TupleTypeKey(ImmutableList parameters) : - base("()", parameters) {} +sealed class TupleTypeKey : ParameterizedTypeKey +{ + public TupleTypeKey(ImmutableList parameters) : + base("()", parameters) {} - public override string ToString() => - "(" + string.Join(", ", Parameters) + ")"; - } + public override string ToString() => + "(" + string.Join(", ", Parameters) + ")"; +} - sealed class ArrayTypeKey : ParameterizedTypeKey - { - public ArrayTypeKey(TypeKey element, IEnumerable ranks) : - base("[]", element) => Ranks = ImmutableList.CreateRange(ranks); +sealed class ArrayTypeKey : ParameterizedTypeKey +{ + public ArrayTypeKey(TypeKey element, IEnumerable ranks) : + base("[]", element) => Ranks = ImmutableList.CreateRange(ranks); - public ImmutableList Ranks { get; } + public ImmutableList Ranks { get; } - public override string ToString() => - Parameters.Single() + string.Concat(from r in Ranks - select "[" + string.Concat(Enumerable.Repeat(",", r - 1)) + "]"); + public override string ToString() => + Parameters.Single() + string.Concat(from r in Ranks + select "[" + string.Concat(Enumerable.Repeat(",", r - 1)) + "]"); - protected override int CompareParameters(TypeKey other) + protected override int CompareParameters(TypeKey other) + { + if (other is ArrayTypeKey a) { - if (other is ArrayTypeKey a) - { - if (Ranks.Count.CompareTo(a.Ranks.Count) is {} rlc and not 0) - return rlc; - if (Ranks.Zip(a.Ranks, (us, them) => (Us: us, Them: them)) - .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is {} rc and not 0) - return rc; - } - - return base.CompareParameters(other); + if (Ranks.Count.CompareTo(a.Ranks.Count) is {} rlc and not 0) + return rlc; + if (Ranks.Zip(a.Ranks, (us, them) => (Us: us, Them: them)) + .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is {} rc and not 0) + return rc; } + + return base.CompareParameters(other); } } From a12006e0453b78df05372601c3bc8b0073b5a569 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 8 Jan 2021 00:52:38 +0100 Subject: [PATCH 071/157] Replace using statements with declarations in tests (#786) --- MoreLinq.Test/AssertCountTest.cs | 2 +- MoreLinq.Test/BacksertTest.cs | 20 +++---- MoreLinq.Test/BatchTest.cs | 37 ++++++------- MoreLinq.Test/CartesianTest.cs | 80 +++++++++++++-------------- MoreLinq.Test/ChooseTest.cs | 25 ++++----- MoreLinq.Test/CompareCountTest.cs | 54 ++++++++---------- MoreLinq.Test/CountDownTest.cs | 8 +-- MoreLinq.Test/EndsWithTest.cs | 9 ++- MoreLinq.Test/EquiZipTest.cs | 54 +++++++++--------- MoreLinq.Test/FlattenTest.cs | 34 ++++++------ MoreLinq.Test/GroupAdjacentTest.cs | 88 +++++++++++++----------------- MoreLinq.Test/InsertTest.cs | 40 +++++++------- MoreLinq.Test/InterleaveTest.cs | 15 +++-- MoreLinq.Test/MemoizeTest.cs | 51 ++++++++--------- MoreLinq.Test/MoveTest.cs | 24 ++++---- MoreLinq.Test/PartitionTest.cs | 74 +++++++++++-------------- MoreLinq.Test/RepeatTest.cs | 10 ++-- MoreLinq.Test/SortedMergeTest.cs | 27 +++++---- MoreLinq.Test/SplitTest.cs | 24 ++++---- MoreLinq.Test/StartsWithTest.cs | 9 ++- MoreLinq.Test/TakeLastTest.cs | 6 +- MoreLinq.Test/TransposeTest.cs | 62 ++++++++++----------- MoreLinq.Test/WindowLeftTest.cs | 50 ++++++++--------- MoreLinq.Test/WindowRightTest.cs | 48 ++++++++-------- MoreLinq.Test/WindowTest.cs | 17 +++--- MoreLinq.Test/ZipLongestTest.cs | 15 +++-- MoreLinq.Test/ZipShortestTest.cs | 69 +++++++++++------------ 27 files changed, 442 insertions(+), 510 deletions(-) diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index ded8d4f25..fd1c21167 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -120,7 +120,7 @@ public void AssertCountWithMatchingCollectionCount() public void AssertCountWithMismatchingCollectionCount(int sourceCount, int count, string message) { var xs = new int[sourceCount]; - var enumerator = xs.AssertCount(count).GetEnumerator(); + using var enumerator = xs.AssertCount(count).GetEnumerator(); var e = Assert.Throws(() => enumerator.MoveNext()); Assert.AreEqual(e.Message, message); } diff --git a/MoreLinq.Test/BacksertTest.cs b/MoreLinq.Test/BacksertTest.cs index a3c4a3dd6..511a2f88f 100644 --- a/MoreLinq.Test/BacksertTest.cs +++ b/MoreLinq.Test/BacksertTest.cs @@ -40,13 +40,12 @@ public void BacksertWithNegativeIndex() [TestCase(new[] { 1, 2, 3 }, 4, new[] { 9 })] public void BacksertWithIndexGreaterThanSourceLength(int[] seq1, int index, int[] seq2) { - using (var test1 = seq1.AsTestingSequence()) - using (var test2 = seq2.AsTestingSequence()) - { - var result = test1.Backsert(test2, index); + using var test1 = seq1.AsTestingSequence(); + using var test2 = seq2.AsTestingSequence(); - Assert.Throws(() => result.ElementAt(0)); - } + var result = test1.Backsert(test2, index); + + Assert.Throws(() => result.ElementAt(0)); } [TestCase(new[] { 1, 2, 3 }, 0, new[] { 8, 9 }, ExpectedResult = new[] { 1, 2, 3, 8, 9 })] @@ -55,11 +54,10 @@ public void BacksertWithIndexGreaterThanSourceLength(int[] seq1, int index, int[ [TestCase(new[] { 1, 2, 3 }, 3, new[] { 8, 9 }, ExpectedResult = new[] { 8, 9, 1, 2, 3 })] public IEnumerable Backsert(int[] seq1, int index, int[] seq2) { - using (var test1 = seq1.AsTestingSequence()) - using (var test2 = seq2.AsTestingSequence()) - { - return test1.Backsert(test2, index).ToArray(); - } + using var test1 = seq1.AsTestingSequence(); + using var test2 = seq2.AsTestingSequence(); + + return test1.Backsert(test2, index).ToArray(); } } } diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index 08fa3d34d..bc4bec843 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -41,26 +41,24 @@ public void BatchNegativeSize() public void BatchEvenlyDivisibleSequence() { var result = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.Batch(3); - using (var reader = result.Read()) - { - reader.Read().AssertSequenceEqual(1, 2, 3); - reader.Read().AssertSequenceEqual(4, 5, 6); - reader.Read().AssertSequenceEqual(7, 8, 9); - reader.ReadEnd(); - } + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3); + reader.Read().AssertSequenceEqual(4, 5, 6); + reader.Read().AssertSequenceEqual(7, 8, 9); + reader.ReadEnd(); } [Test] public void BatchUnevenlyDivisibleSequence() { var result = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.Batch(4); - using (var reader = result.Read()) - { - reader.Read().AssertSequenceEqual(1, 2, 3, 4); - reader.Read().AssertSequenceEqual(5, 6, 7, 8); - reader.Read().AssertSequenceEqual(9); - reader.ReadEnd(); - } + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3, 4); + reader.Read().AssertSequenceEqual(5, 6, 7, 8); + reader.Read().AssertSequenceEqual(9); + reader.ReadEnd(); } [Test] @@ -74,12 +72,11 @@ public void BatchSequenceTransformingResult() public void BatchSequenceYieldsListsOfBatches() { var result = new[] { 1, 2, 3 }.Batch(2); - using (var reader = result.Read()) - { - Assert.That(reader.Read(), Is.InstanceOf(typeof(IList))); - Assert.That(reader.Read(), Is.InstanceOf(typeof(IList))); - reader.ReadEnd(); - } + + using var reader = result.Read(); + Assert.That(reader.Read(), Is.InstanceOf(typeof(IList))); + Assert.That(reader.Read(), Is.InstanceOf(typeof(IList))); + reader.ReadEnd(); } [Test] diff --git a/MoreLinq.Test/CartesianTest.cs b/MoreLinq.Test/CartesianTest.cs index a7eb0e5c8..36dcaebcb 100644 --- a/MoreLinq.Test/CartesianTest.cs +++ b/MoreLinq.Test/CartesianTest.cs @@ -42,12 +42,12 @@ public void TestCartesianIsLazy() [Test] public void TestCartesianOfEmptySequences() { - using (var sequenceA = Enumerable.Empty().AsTestingSequence()) - using (var sequenceB = Enumerable.Empty().AsTestingSequence()) - { - var result = sequenceA.Cartesian(sequenceB, (a, b) => a + b); - Assert.That(result, Is.Empty); - } + using var sequenceA = Enumerable.Empty().AsTestingSequence(); + using var sequenceB = Enumerable.Empty().AsTestingSequence(); + + var result = sequenceA.Cartesian(sequenceB, (a, b) => a + b); + + Assert.That(result, Is.Empty); } /// @@ -83,12 +83,12 @@ public void TestCartesianProductCount() const int countA = 100; const int countB = 75; const int expectedCount = countA*countB; - using (var sequenceA = Enumerable.Range(1, countA).AsTestingSequence()) - using (var sequenceB = Enumerable.Range(1, countB).AsTestingSequence()) - { - var result = sequenceA.Cartesian(sequenceB, (a, b) => a + b); - Assert.AreEqual( expectedCount, result.Count() ); - } + using var sequenceA = Enumerable.Range(1, countA).AsTestingSequence(); + using var sequenceB = Enumerable.Range(1, countB).AsTestingSequence(); + + var result = sequenceA.Cartesian(sequenceB, (a, b) => a + b); + + Assert.AreEqual(expectedCount, result.Count() ); } /// @@ -103,15 +103,15 @@ public void TestCartesianProductCount_Multidimensional() const int countC = 8; const int countD = 7; + using var sequenceA = Enumerable.Range(1, countA).AsTestingSequence(); + using var sequenceB = Enumerable.Range(1, countB).AsTestingSequence(); + using var sequenceC = Enumerable.Range(1, countC).AsTestingSequence(); + using var sequenceD = Enumerable.Range(1, countD).AsTestingSequence(); + + var result = sequenceA.Cartesian(sequenceB, sequenceC, sequenceD, (a, b, c, d) => a + b + c + d); + const int expectedCount = countA * countB * countC * countD; - using (var sequenceA = Enumerable.Range(1, countA).AsTestingSequence()) - using (var sequenceB = Enumerable.Range(1, countB).AsTestingSequence()) - using (var sequenceC = Enumerable.Range(1, countC).AsTestingSequence()) - using (var sequenceD = Enumerable.Range(1, countD).AsTestingSequence()) - { - var result = sequenceA.Cartesian(sequenceB, sequenceC, sequenceD, (a, b, c, d) => a + b + c + d); - Assert.AreEqual(expectedCount, result.Count()); - } + Assert.AreEqual(expectedCount, result.Count()); } /// @@ -132,20 +132,19 @@ public void TestCartesianProductCombinations() Enumerable.Repeat(false, 5).ToArray() }; - using (var tsA = sequenceA.AsTestingSequence()) - using (var tsB = sequenceB.AsTestingSequence()) - { - var result = tsA.Cartesian(tsB, (a, b) => new { A = a, B = b }) - .ToArray(); + using var tsA = sequenceA.AsTestingSequence(); + using var tsB = sequenceB.AsTestingSequence(); - // verify that the expected number of results is correct - Assert.AreEqual(sequenceA.Count() * sequenceB.Count(), result.Count()); + var result = tsA.Cartesian(tsB, (a, b) => new { A = a, B = b }) + .ToArray(); - // ensure that all "cells" were visited by the cartesian product - foreach (var coord in result) - expectedSet[coord.A][coord.B] = true; - Assert.IsTrue(expectedSet.SelectMany(x => x).All(z => z)); - } + // verify that the expected number of results is correct + Assert.AreEqual(sequenceA.Count() * sequenceB.Count(), result.Count()); + + // ensure that all "cells" were visited by the cartesian product + foreach (var coord in result) + expectedSet[coord.A][coord.B] = true; + Assert.IsTrue(expectedSet.SelectMany(x => x).All(z => z)); } /// @@ -155,16 +154,15 @@ public void TestCartesianProductCombinations() [Test] public void TestEmptyCartesianEvaluation() { - using (var sequence = Enumerable.Range(0, 5).AsTestingSequence()) - { - var resultA = sequence.Cartesian(Enumerable.Empty(), (a, b) => new { A = a, B = b }); - var resultB = Enumerable.Empty().Cartesian(sequence, (a, b) => new { A = a, B = b }); - var resultC = Enumerable.Empty().Cartesian(Enumerable.Empty(), (a, b) => new { A = a, B = b }); + using var sequence = Enumerable.Range(0, 5).AsTestingSequence(); - Assert.AreEqual(0, resultA.Count()); - Assert.AreEqual(0, resultB.Count()); - Assert.AreEqual(0, resultC.Count()); - } + var resultA = sequence.Cartesian(Enumerable.Empty(), (a, b) => new { A = a, B = b }); + var resultB = Enumerable.Empty().Cartesian(sequence, (a, b) => new { A = a, B = b }); + var resultC = Enumerable.Empty().Cartesian(Enumerable.Empty(), (a, b) => new { A = a, B = b }); + + Assert.AreEqual(0, resultA.Count()); + Assert.AreEqual(0, resultB.Count()); + Assert.AreEqual(0, resultC.Count()); } } } diff --git a/MoreLinq.Test/ChooseTest.cs b/MoreLinq.Test/ChooseTest.cs index e1c335945..acc4dd112 100644 --- a/MoreLinq.Test/ChooseTest.cs +++ b/MoreLinq.Test/ChooseTest.cs @@ -33,30 +33,29 @@ public void IsLazy() [Test] public void WithEmptySource() { - using (var xs = Enumerable.Empty().AsTestingSequence()) - Assert.That(xs.Choose(BreakingFunc.Of()), Is.Empty); + using var xs = Enumerable.Empty().AsTestingSequence(); + Assert.That(xs.Choose(BreakingFunc.Of()), Is.Empty); } [Test] public void None() { - using (var xs = Enumerable.Range(1, 10).AsTestingSequence()) - Assert.That(xs.Choose(_ => (false, 0)), Is.Empty); + using var xs = Enumerable.Range(1, 10).AsTestingSequence(); + Assert.That(xs.Choose(_ => (false, 0)), Is.Empty); } [Test] public void ThoseParsable() { - using (var xs = + using var xs = "O,l,2,3,4,S,6,7,B,9" - .Split(',') - .Choose(s => (int.TryParse(s, NumberStyles.Integer, - CultureInfo.InvariantCulture, - out var n), n)) - .AsTestingSequence()) - { - xs.AssertSequenceEqual(2, 3, 4, 6, 7, 9); - } + .Split(',') + .Choose(s => (int.TryParse(s, NumberStyles.Integer, + CultureInfo.InvariantCulture, + out var n), n)) + .AsTestingSequence(); + + xs.AssertSequenceEqual(2, 3, 4, 6, 7, 9); } // A cheap trick to masquerade a tuple as an option diff --git a/MoreLinq.Test/CompareCountTest.cs b/MoreLinq.Test/CompareCountTest.cs index 6deba897c..96e2151ab 100644 --- a/MoreLinq.Test/CompareCountTest.cs +++ b/MoreLinq.Test/CompareCountTest.cs @@ -55,11 +55,10 @@ public void CompareCountWithCollectionAndSequence(int collectionCount, { var collection = new BreakingCollection(collectionCount); - using (var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence()) - { - Assert.AreEqual(expectedCompareCount, collection.CompareCount(seq)); - Assert.AreEqual(expectedMoveNextCallCount, seq.MoveNextCallCount); - } + using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); + + Assert.AreEqual(expectedCompareCount, collection.CompareCount(seq)); + Assert.AreEqual(expectedMoveNextCallCount, seq.MoveNextCallCount); } [TestCase(0, 0, 0, 1)] @@ -73,11 +72,10 @@ public void CompareCountWithSequenceAndCollection(int sequenceCount, { var collection = new BreakingCollection(collectionCount); - using (var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence()) - { - Assert.AreEqual(expectedCompareCount, seq.CompareCount(collection)); - Assert.AreEqual(expectedMoveNextCallCount, seq.MoveNextCallCount); - } + using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); + + Assert.AreEqual(expectedCompareCount, seq.CompareCount(collection)); + Assert.AreEqual(expectedMoveNextCallCount, seq.MoveNextCallCount); } [TestCase(0, 0, 0, 1)] @@ -89,23 +87,21 @@ public void CompareCountWithSequenceAndSequence(int sequenceCount1, int expectedCompareCount, int expectedMoveNextCallCount) { - using (var seq1 = Enumerable.Range(0, sequenceCount1).AsTestingSequence()) - using (var seq2 = Enumerable.Range(0, sequenceCount2).AsTestingSequence()) - { - Assert.AreEqual(expectedCompareCount, seq1.CompareCount(seq2)); - Assert.AreEqual(expectedMoveNextCallCount, seq1.MoveNextCallCount); - Assert.AreEqual(expectedMoveNextCallCount, seq2.MoveNextCallCount); - } + using var seq1 = Enumerable.Range(0, sequenceCount1).AsTestingSequence(); + using var seq2 = Enumerable.Range(0, sequenceCount2).AsTestingSequence(); + + Assert.AreEqual(expectedCompareCount, seq1.CompareCount(seq2)); + Assert.AreEqual(expectedMoveNextCallCount, seq1.MoveNextCallCount); + Assert.AreEqual(expectedMoveNextCallCount, seq2.MoveNextCallCount); } [Test] public void CompareCountDisposesSequenceEnumerators() { - using (var seq1 = TestingSequence.Of()) - using (var seq2 = TestingSequence.Of()) - { - Assert.AreEqual(0, seq1.CompareCount(seq2)); - } + using var seq1 = TestingSequence.Of(); + using var seq2 = TestingSequence.Of(); + + Assert.AreEqual(0, seq1.CompareCount(seq2)); } [Test] @@ -113,10 +109,9 @@ public void CompareCountDisposesFirstEnumerator() { var collection = new BreakingCollection(0); - using (var seq = TestingSequence.Of()) - { - Assert.AreEqual(0, seq.CompareCount(collection)); - } + using var seq = TestingSequence.Of(); + + Assert.AreEqual(0, seq.CompareCount(collection)); } [Test] @@ -124,10 +119,9 @@ public void CompareCountDisposesSecondEnumerator() { var collection = new BreakingCollection(0); - using (var seq = TestingSequence.Of()) - { - Assert.AreEqual(0, collection.CompareCount(seq)); - } + using var seq = TestingSequence.Of(); + + Assert.AreEqual(0, collection.CompareCount(seq)); } [Test] diff --git a/MoreLinq.Test/CountDownTest.cs b/MoreLinq.Test/CountDownTest.cs index 09bb9d940..1e42f40ea 100644 --- a/MoreLinq.Test/CountDownTest.cs +++ b/MoreLinq.Test/CountDownTest.cs @@ -67,11 +67,9 @@ static IEnumerable GetData(Func selector) [TestCaseSource(nameof(SequenceData))] public IEnumerable<(int, int?)> WithSequence(int[] xs, int count) { - using (var ts = xs.Select(x => x).AsTestingSequence()) - { - foreach (var e in ts.CountDown(count, ValueTuple.Create)) - yield return e; - } + using var ts = xs.Select(x => x).AsTestingSequence(); + foreach (var e in ts.CountDown(count, ValueTuple.Create)) + yield return e; } static readonly IEnumerable ListData = diff --git a/MoreLinq.Test/EndsWithTest.cs b/MoreLinq.Test/EndsWithTest.cs index 81dece5bf..7d4c1cb14 100644 --- a/MoreLinq.Test/EndsWithTest.cs +++ b/MoreLinq.Test/EndsWithTest.cs @@ -72,11 +72,10 @@ public bool EndsWithReturnsTrueIfSecondIsEmpty(string first, string second) [Test] public void EndsWithDisposesBothSequenceEnumerators() { - using (var first = TestingSequence.Of(1,2,3)) - using (var second = TestingSequence.Of(1)) - { - first.EndsWith(second); - } + using var first = TestingSequence.Of(1,2,3); + using var second = TestingSequence.Of(1); + + first.EndsWith(second); } [Test] diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index b7f9dde57..f118f21eb 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -27,25 +27,23 @@ public class EquiZipTest [Test] public void BothSequencesDisposedWithUnequalLengthsAndLongerFirst() { - using (var longer = TestingSequence.Of(1, 2, 3)) - using (var shorter = TestingSequence.Of(1, 2)) - { - // Yes, this will throw... but then we should still have disposed both sequences - Assert.Throws(() => - longer.EquiZip(shorter, (x, y) => x + y).Consume()); - } + using var longer = TestingSequence.Of(1, 2, 3); + using var shorter = TestingSequence.Of(1, 2); + + // Yes, this will throw... but then we should still have disposed both sequences + Assert.Throws(() => + longer.EquiZip(shorter, (x, y) => x + y).Consume()); } [Test] public void BothSequencesDisposedWithUnequalLengthsAndShorterFirst() { - using (var longer = TestingSequence.Of(1, 2, 3)) - using (var shorter = TestingSequence.Of(1, 2)) - { - // Yes, this will throw... but then we should still have disposed both sequences - Assert.Throws(() => - shorter.EquiZip(longer, (x, y) => x + y).Consume()); - } + using var longer = TestingSequence.Of(1, 2, 3); + using var shorter = TestingSequence.Of(1, 2); + + // Yes, this will throw... but then we should still have disposed both sequences + Assert.Throws(() => + shorter.EquiZip(longer, (x, y) => x + y).Consume()); } [Test] @@ -84,26 +82,24 @@ public void ZipIsLazy() [Test] public void MoveNextIsNotCalledUnnecessarily() { - using (var s1 = TestingSequence.Of(1, 2)) - using (var s2 = TestingSequence.Of(1, 2, 3)) - using (var s3 = MoreEnumerable.From(() => 1, - () => 2, - () => throw new TestException()) - .AsTestingSequence()) - { - Assert.Throws(() => - s1.EquiZip(s2, s3, (x, y, z) => x + y + z).Consume()); - } + using var s1 = TestingSequence.Of(1, 2); + using var s2 = TestingSequence.Of(1, 2, 3); + using var s3 = MoreEnumerable.From(() => 1, + () => 2, + () => throw new TestException()) + .AsTestingSequence(); + + Assert.Throws(() => + s1.EquiZip(s2, s3, (x, y, z) => x + y + z).Consume()); } [Test] public void ZipDisposesInnerSequencesCaseGetEnumeratorThrows() { - using (var s1 = TestingSequence.Of(1, 2)) - { - Assert.Throws(() => - s1.EquiZip(new BreakingSequence(), Tuple.Create).Consume()); - } + using var s1 = TestingSequence.Of(1, 2); + + Assert.Throws(() => + s1.EquiZip(new BreakingSequence(), Tuple.Create).Consume()); } } } diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index 6bf60394d..a416df347 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -236,29 +236,27 @@ public void FlattenFullIteratedDisposesInnerSequences() 7, }; - using (var inner1 = TestingSequence.Of(4, 5)) - using (var inner2 = TestingSequence.Of(true, false)) - using (var inner3 = TestingSequence.Of(6, inner2, 7)) - using (var source = TestingSequence.Of(inner1, inner3)) - { - Assert.That(source.Flatten(), Is.EqualTo(expectations)); - } + using var inner1 = TestingSequence.Of(4, 5); + using var inner2 = TestingSequence.Of(true, false); + using var inner3 = TestingSequence.Of(6, inner2, 7); + using var source = TestingSequence.Of(inner1, inner3); + + Assert.That(source.Flatten(), Is.EqualTo(expectations)); } [Test] public void FlattenInterruptedIterationDisposesInnerSequences() { - using (var inner1 = TestingSequence.Of(4, 5)) - using (var inner2 = MoreEnumerable.From(() => true, - () => false, - () => throw new TestException()) - .AsTestingSequence()) - using (var inner3 = TestingSequence.Of(6, inner2, 7)) - using (var source = TestingSequence.Of(inner1, inner3)) - { - Assert.Throws(() => - source.Flatten().Consume()); - } + using var inner1 = TestingSequence.Of(4, 5); + using var inner2 = MoreEnumerable.From(() => true, + () => false, + () => throw new TestException()) + .AsTestingSequence(); + using var inner3 = TestingSequence.Of(6, inner2, 7); + using var source = TestingSequence.Of(inner1, inner3); + + Assert.Throws(() => + source.Flatten().Consume()); } [Test] diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index 1c4edb323..f8bb13ea9 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -55,33 +55,31 @@ public void GroupAdjacentSourceSequence() const string ten = "ten"; var source = new[] { one, two, three, four, five, six, seven, eight, nine, ten }; + var groupings = source.GroupAdjacent(s => s.Length); - using (var reader = groupings.Read()) - { - AssertGrouping(reader, 3, one, two); - AssertGrouping(reader, 5, three); - AssertGrouping(reader, 4, four, five); - AssertGrouping(reader, 3, six); - AssertGrouping(reader, 5, seven, eight); - AssertGrouping(reader, 4, nine); - AssertGrouping(reader, 3, ten); - reader.ReadEnd(); - } + using var reader = groupings.Read(); + AssertGrouping(reader, 3, one, two); + AssertGrouping(reader, 5, three); + AssertGrouping(reader, 4, four, five); + AssertGrouping(reader, 3, six); + AssertGrouping(reader, 5, seven, eight); + AssertGrouping(reader, 4, nine); + AssertGrouping(reader, 3, ten); + reader.ReadEnd(); } [Test] public void GroupAdjacentSourceSequenceComparer() { var source = new[] { "foo", "FOO", "Foo", "bar", "BAR", "Bar" }; + var groupings = source.GroupAdjacent(s => s, StringComparer.OrdinalIgnoreCase); - using (var reader = groupings.Read()) - { - AssertGrouping(reader, "foo", "foo", "FOO", "Foo"); - AssertGrouping(reader, "bar", "bar", "BAR", "Bar"); - reader.ReadEnd(); - } + using var reader = groupings.Read(); + AssertGrouping(reader, "foo", "foo", "FOO", "Foo"); + AssertGrouping(reader, "bar", "bar", "BAR", "Bar"); + reader.ReadEnd(); } [Test] @@ -105,14 +103,12 @@ public void GroupAdjacentSourceSequenceElementSelector() var groupings = source.GroupAdjacent(e => e.Month, e => e.Value * 2); - using (var reader = groupings.Read()) - { - AssertGrouping(reader, 1, 123 * 2, 456 * 2, 789 * 2); - AssertGrouping(reader, 2, 987 * 2, 654 * 2, 321 * 2); - AssertGrouping(reader, 3, 789 * 2, 456 * 2, 123 * 2); - AssertGrouping(reader, 1, 123 * 2, 456 * 2, 781 * 2); - reader.ReadEnd(); - } + using var reader = groupings.Read(); + AssertGrouping(reader, 1, 123 * 2, 456 * 2, 789 * 2); + AssertGrouping(reader, 2, 987 * 2, 654 * 2, 321 * 2); + AssertGrouping(reader, 3, 789 * 2, 456 * 2, 123 * 2); + AssertGrouping(reader, 1, 123 * 2, 456 * 2, 781 * 2); + reader.ReadEnd(); } [Test] @@ -136,14 +132,12 @@ public void GroupAdjacentSourceSequenceElementSelectorComparer() var groupings = source.GroupAdjacent(e => e.Month, e => e.Value * 2, StringComparer.OrdinalIgnoreCase); - using (var reader = groupings.Read()) - { - AssertGrouping(reader, "jan", 123 * 2, 456 * 2, 789 * 2); - AssertGrouping(reader, "feb", 987 * 2, 654 * 2, 321 * 2); - AssertGrouping(reader, "mar", 789 * 2, 456 * 2, 123 * 2); - AssertGrouping(reader, "jan", 123 * 2, 456 * 2, 781 * 2); - reader.ReadEnd(); - } + using var reader = groupings.Read(); + AssertGrouping(reader, "jan", 123 * 2, 456 * 2, 789 * 2); + AssertGrouping(reader, "feb", 987 * 2, 654 * 2, 321 * 2); + AssertGrouping(reader, "mar", 789 * 2, 456 * 2, 123 * 2); + AssertGrouping(reader, "jan", 123 * 2, 456 * 2, 781 * 2); + reader.ReadEnd(); } [Test] @@ -167,14 +161,12 @@ public void GroupAdjacentSourceSequenceResultSelector() var groupings = source.GroupAdjacent(e => e.Month, (key, group) => group.Sum(v => v.Value)); - using (var reader = groupings.Read()) - { - AssertResult(reader, 123 + 456 + 789); - AssertResult(reader, 987 + 654 + 321); - AssertResult(reader, 789 + 456 + 123); - AssertResult(reader, 123 + 456 + 781); - reader.ReadEnd(); - } + using var reader = groupings.Read(); + AssertResult(reader, 123 + 456 + 789); + AssertResult(reader, 987 + 654 + 321); + AssertResult(reader, 789 + 456 + 123); + AssertResult(reader, 123 + 456 + 781); + reader.ReadEnd(); } [Test] @@ -198,14 +190,12 @@ public void GroupAdjacentSourceSequenceResultSelectorComparer() var groupings = source.GroupAdjacent(e => e.Month, (key, group) => group.Sum(v => v.Value), StringComparer.OrdinalIgnoreCase); - using (var reader = groupings.Read()) - { - AssertResult(reader, 123 + 456 + 789); - AssertResult(reader, 987 + 654 + 321); - AssertResult(reader, 789 + 456 + 123); - AssertResult(reader, 123 + 456 + 781); - reader.ReadEnd(); - } + using var reader = groupings.Read(); + AssertResult(reader, 123 + 456 + 789); + AssertResult(reader, 987 + 654 + 321); + AssertResult(reader, 789 + 456 + 123); + AssertResult(reader, 123 + 456 + 781); + reader.ReadEnd(); } static void AssertGrouping(SequenceReader> reader, diff --git a/MoreLinq.Test/InsertTest.cs b/MoreLinq.Test/InsertTest.cs index 664f2fbc4..b8d51f514 100644 --- a/MoreLinq.Test/InsertTest.cs +++ b/MoreLinq.Test/InsertTest.cs @@ -37,15 +37,14 @@ public void InsertWithIndexGreaterThanSourceLengthMaterialized(int count) var seq1 = Enumerable.Range(0, count).ToList(); var seq2 = new[] { 97, 98, 99 }; - using (var test1 = seq1.AsTestingSequence()) - using (var test2 = seq2.AsTestingSequence()) - { - var result = test1.Insert(test2, count + 1); + using var test1 = seq1.AsTestingSequence(); + using var test2 = seq2.AsTestingSequence(); - AssertThrowsArgument.OutOfRangeException("index", () => - result.ForEach((e, index) => - Assert.That(e, Is.EqualTo(seq1[index])))); - } + var result = test1.Insert(test2, count + 1); + + AssertThrowsArgument.OutOfRangeException("index", () => + result.ForEach((e, index) => + Assert.That(e, Is.EqualTo(seq1[index])))); } [TestCase(7)] @@ -56,13 +55,12 @@ public void InsertWithIndexGreaterThanSourceLengthLazy(int count) var seq1 = Enumerable.Range(0, count); var seq2 = new[] { 97, 98, 99 }; - using (var test1 = seq1.AsTestingSequence()) - using (var test2 = seq2.AsTestingSequence()) - { - var result = test1.Insert(test2, count + 1).Take(count); + using var test1 = seq1.AsTestingSequence(); + using var test2 = seq2.AsTestingSequence(); + + var result = test1.Insert(test2, count + 1).Take(count); - Assert.That(seq1, Is.EqualTo(result)); - } + Assert.That(seq1, Is.EqualTo(result)); } [TestCase(3, 0)] @@ -74,13 +72,13 @@ public void Insert(int count, int index) var seq1 = Enumerable.Range(1, count); var seq2 = new[] { 97, 98, 99 }; - using (var test1 = seq1.AsTestingSequence()) - using (var test2 = seq2.AsTestingSequence()) - { - var result = test1.Insert(test2, index); - var expectations = seq1.Take(index).Concat(seq2).Concat(seq1.Skip(index)); - Assert.That(result, Is.EqualTo(expectations)); - } + using var test1 = seq1.AsTestingSequence(); + using var test2 = seq2.AsTestingSequence(); + + var result = test1.Insert(test2, index); + + var expectations = seq1.Take(index).Concat(seq2).Concat(seq1.Skip(index)); + Assert.That(result, Is.EqualTo(expectations)); } [Test] diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 6c1498262..b201a776e 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -174,14 +174,13 @@ public void TestInterleaveDisposesAllIterators() { const int count = 10; - using (var sequenceA = Enumerable.Range(1, count).AsTestingSequence()) - using (var sequenceB = Enumerable.Range(1, count - 1).AsTestingSequence()) - using (var sequenceC = Enumerable.Range(1, count - 5).AsTestingSequence()) - using (var sequenceD = Enumerable.Range(1, 0).AsTestingSequence()) - { - sequenceA.Interleave(sequenceB, sequenceC, sequenceD) - .Consume(); - } + using var sequenceA = Enumerable.Range(1, count).AsTestingSequence(); + using var sequenceB = Enumerable.Range(1, count - 1).AsTestingSequence(); + using var sequenceC = Enumerable.Range(1, count - 5).AsTestingSequence(); + using var sequenceD = Enumerable.Range(1, 0).AsTestingSequence(); + + sequenceA.Interleave(sequenceB, sequenceC, sequenceD) + .Consume(); } } } diff --git a/MoreLinq.Test/MemoizeTest.cs b/MoreLinq.Test/MemoizeTest.cs index 4d93b2159..f2658fac2 100644 --- a/MoreLinq.Test/MemoizeTest.cs +++ b/MoreLinq.Test/MemoizeTest.cs @@ -113,14 +113,14 @@ public void MemoizeWithInMemoryCollection(SourceKind sourceKind) public void MemoizeEnumeratesOnlyOnce() { const int count = 10; - using (var ts = Enumerable.Range(1, count).AsTestingSequence()) + using var ts = Enumerable.Range(1, count).AsTestingSequence(); + + var memoized = ts.Memoize(); + + using ((IDisposable)memoized) { - var memoized = ts.Memoize(); - using ((IDisposable) memoized) - { - Assert.That(memoized.ToList().Count, Is.EqualTo(count)); - Assert.That(memoized.ToList().Count, Is.EqualTo(count)); - } + Assert.That(memoized.ToList().Count, Is.EqualTo(count)); + Assert.That(memoized.ToList().Count, Is.EqualTo(count)); } } @@ -129,30 +129,29 @@ public void MemoizeDoesNotDisposeOnEarlyExitByDefault() { Assert.Throws(() => { - using (var xs = new[] { 1, 2 }.AsTestingSequence()) - { - xs.Memoize().Take(1).Consume(); - xs.Memoize().Take(1).Consume(); - } + using var xs = new[] { 1, 2 }.AsTestingSequence(); + + xs.Memoize().Take(1).Consume(); + xs.Memoize().Take(1).Consume(); }); } [Test] public void MemoizeWithDisposeOnEarlyExitTrue() { - using (var xs = new[] { 1, 2 }.AsTestingSequence()) - { - var memoized = xs.Memoize(); - using ((IDisposable) memoized) - memoized.Take(1).Consume(); - } + using var xs = new[] { 1, 2 }.AsTestingSequence(); + + var memoized = xs.Memoize(); + + using ((IDisposable) memoized) + memoized.Take(1).Consume(); } [Test] public void MemoizeDisposesAfterSourceIsIteratedEntirely() { - using (var xs = new[] { 1, 2 }.AsTestingSequence()) - xs.Memoize().Consume(); + using var xs = new[] { 1, 2 }.AsTestingSequence(); + xs.Memoize().Consume(); } [Test, Explicit] @@ -216,15 +215,13 @@ public static void MemoizeIteratorThrowsWhenCacheDisposedDuringIteration() var memoized = sequence.Memoize(); var disposable = (IDisposable) memoized; - using (var reader = memoized.Read()) - { - Assert.That(reader.Read(), Is.EqualTo(1)); + using var reader = memoized.Read(); + Assert.That(reader.Read(), Is.EqualTo(1)); - disposable.Dispose(); + disposable.Dispose(); - var e = Assert.Throws(() => reader.Read()); - Assert.That(e.ObjectName, Is.EqualTo("MemoizedEnumerable")); - } + var e = Assert.Throws(() => reader.Read()); + Assert.That(e.ObjectName, Is.EqualTo("MemoizedEnumerable")); } [Test] diff --git a/MoreLinq.Test/MoveTest.cs b/MoreLinq.Test/MoveTest.cs index 150f958ec..f001a01b7 100644 --- a/MoreLinq.Test/MoveTest.cs +++ b/MoreLinq.Test/MoveTest.cs @@ -56,15 +56,14 @@ public void Move(int length, int fromIndex, int count, int toIndex) { var source = Enumerable.Range(0, length); - var exclude = source.Exclude(fromIndex, count); + using var test = source.AsTestingSequence(); + + var result = test.Move(fromIndex, count, toIndex); + var slice = source.Slice(fromIndex, count); + var exclude = source.Exclude(fromIndex, count); var expectations = exclude.Take(toIndex).Concat(slice).Concat(exclude.Skip(toIndex)); - - using (var test = source.AsTestingSequence()) - { - var result = test.Move(fromIndex, count, toIndex); - Assert.That(result, Is.EqualTo(expectations)); - } + Assert.That(result, Is.EqualTo(expectations)); } public static IEnumerable MoveSource() @@ -85,13 +84,12 @@ public void MoveWithSequenceShorterThanToIndex(int length, int fromIndex, int co { var source = Enumerable.Range(0, length); - var expectations = source.Exclude(fromIndex, count).Concat(source.Slice(fromIndex, count)); + using var test = source.AsTestingSequence(); - using (var test = source.AsTestingSequence()) - { - var result = test.Move(fromIndex, count, toIndex); - Assert.That(result, Is.EqualTo(expectations)); - } + var result = test.Move(fromIndex, count, toIndex); + + var expectations = source.Exclude(fromIndex, count).Concat(source.Slice(fromIndex, count)); + Assert.That(result, Is.EqualTo(expectations)); } public static IEnumerable MoveWithSequenceShorterThanToIndexSource() diff --git a/MoreLinq.Test/PartitionTest.cs b/MoreLinq.Test/PartitionTest.cs index 7617f90ab..cdc4ac19c 100644 --- a/MoreLinq.Test/PartitionTest.cs +++ b/MoreLinq.Test/PartitionTest.cs @@ -93,18 +93,16 @@ public void PartitionBooleanGroupingWithSingleKey() Assert.That(m3, Is.EqualTo(new[] { 0, 3, 6, 9 })); - using (var r = etc.Read()) - { - var r1 = r.Read(); - Assert.That(r1.Key, Is.EqualTo(1)); - Assert.That(r1, Is.EqualTo(new[] { 1, 4, 7 })); - - var r2 = r.Read(); - Assert.That(r2.Key, Is.EqualTo(2)); - Assert.That(r2, Is.EqualTo(new[] { 2, 5, 8 })); - - r.ReadEnd(); - } + using var r = etc.Read(); + var r1 = r.Read(); + Assert.That(r1.Key, Is.EqualTo(1)); + Assert.That(r1, Is.EqualTo(new[] { 1, 4, 7 })); + + var r2 = r.Read(); + Assert.That(r2.Key, Is.EqualTo(2)); + Assert.That(r2, Is.EqualTo(new[] { 2, 5, 8 })); + + r.ReadEnd(); } [Test] @@ -118,13 +116,11 @@ public void PartitionBooleanGroupingWitTwoKeys() Assert.That(ms, Is.EqualTo(new[] { 0, 3, 6, 9 })); Assert.That(r1, Is.EqualTo(new[] { 1, 4, 7 })); - using (var r = etc.Read()) - { - var r2 = r.Read(); - Assert.That(r2.Key, Is.EqualTo(2)); - Assert.That(r2, Is.EqualTo(new[] { 2, 5, 8 })); - r.ReadEnd(); - } + using var r = etc.Read(); + var r2 = r.Read(); + Assert.That(r2.Key, Is.EqualTo(2)); + Assert.That(r2, Is.EqualTo(new[] { 2, 5, 8 })); + r.ReadEnd(); } [Test] @@ -153,12 +149,10 @@ public void PartitionBooleanGroupingWithSingleKeyWithComparer() Assert.That(foo, Is.EqualTo(new[] { "foo", "FOO" })); - using (var r = etc.Read()) - { - var bar = r.Read(); - Assert.That(bar, Is.EqualTo(new[] { "bar", "Bar" })); - r.ReadEnd(); - } + using var r = etc.Read(); + var bar = r.Read(); + Assert.That(bar, Is.EqualTo(new[] { "bar", "Bar" })); + r.ReadEnd(); } [Test] @@ -174,18 +168,16 @@ public void PartitionBooleanGroupingWithTwoKeysWithComparer() Assert.That(foos, Is.EqualTo(new[] { "foo", "FOO" })); Assert.That(bar, Is.EqualTo(new[] { "bar", "Bar" })); - using (var r = etc.Read()) - { - var baz = r.Read(); - Assert.That(baz.Key, Is.EqualTo("baz")); - Assert.That(baz, Is.EqualTo(new[] { "baz", "bAz" })); + using var r = etc.Read(); + var baz = r.Read(); + Assert.That(baz.Key, Is.EqualTo("baz")); + Assert.That(baz, Is.EqualTo(new[] { "baz", "bAz" })); - var qux = r.Read(); - Assert.That(qux.Key, Is.EqualTo("QUx")); - Assert.That(qux, Is.EqualTo(new[] { "QUx", "QuX" })); + var qux = r.Read(); + Assert.That(qux.Key, Is.EqualTo("QUx")); + Assert.That(qux, Is.EqualTo(new[] { "QUx", "QuX" })); - r.ReadEnd(); - } + r.ReadEnd(); } [Test] @@ -202,13 +194,11 @@ public void PartitionBooleanGroupingWithThreeKeysWithComparer() Assert.That(bar, Is.EqualTo(new[] { "bar", "Bar" })); Assert.That(baz, Is.EqualTo(new[] { "baz", "bAz" })); - using (var r = etc.Read()) - { - var qux = r.Read(); - Assert.That(qux.Key, Is.EqualTo("QUx")); - Assert.That(qux, Is.EqualTo(new[] { "QUx", "QuX" })); - r.ReadEnd(); - } + using var r = etc.Read(); + var qux = r.Read(); + Assert.That(qux.Key, Is.EqualTo("QUx")); + Assert.That(qux, Is.EqualTo(new[] { "QUx", "QuX" })); + r.ReadEnd(); } } } diff --git a/MoreLinq.Test/RepeatTest.cs b/MoreLinq.Test/RepeatTest.cs index 46a810e6b..20399644d 100644 --- a/MoreLinq.Test/RepeatTest.cs +++ b/MoreLinq.Test/RepeatTest.cs @@ -73,11 +73,11 @@ public void TestNegativeRepeatCount() public void TestRepeatForeverBehaviorSingleElementList() { const int value = 3; - using (var sequence = new[] { value }.AsTestingSequence()) - { - var result = sequence.Repeat(); - Assert.IsTrue(result.Take(100).All(x => x == value)); - } + using var sequence = new[] { value }.AsTestingSequence(); + + var result = sequence.Repeat(); + + Assert.IsTrue(result.Take(100).All(x => x == value)); } /// diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index 9dc684671..7c7cae4d3 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -46,12 +46,11 @@ public void TestSortedMergeIsLazy() [Test] public void TestSortedMergeDisposesOnError() { - using (var sequenceA = TestingSequence.Of()) - { - // Expected and thrown by BreakingSequence - Assert.Throws(() => - sequenceA.SortedMerge(OrderByDirection.Ascending, new BreakingSequence()).Consume()); - } + using var sequenceA = TestingSequence.Of(); + + // Expected and thrown by BreakingSequence + Assert.Throws(() => + sequenceA.SortedMerge(OrderByDirection.Ascending, new BreakingSequence()).Consume()); } /// @@ -181,14 +180,14 @@ public void TestSortedMergeCustomComparer() public void TestSortedMergeAllSequencesDisposed() { const int count = 10; - using (var sequenceA = Enumerable.Range(1, count).AsTestingSequence()) - using (var sequenceB = Enumerable.Range(1, count - 1).AsTestingSequence()) - using (var sequenceC = Enumerable.Range(1, count - 5).AsTestingSequence()) - using (var sequenceD = Enumerable.Range(1, 0).AsTestingSequence()) - { - sequenceA.SortedMerge(OrderByDirection.Ascending, sequenceB, sequenceC, sequenceD) - .Consume(); // ensures the sequences are actually merged and iterators are obtained - } + + using var sequenceA = Enumerable.Range(1, count).AsTestingSequence(); + using var sequenceB = Enumerable.Range(1, count - 1).AsTestingSequence(); + using var sequenceC = Enumerable.Range(1, count - 5).AsTestingSequence(); + using var sequenceD = Enumerable.Range(1, 0).AsTestingSequence(); + + sequenceA.SortedMerge(OrderByDirection.Ascending, sequenceB, sequenceC, sequenceD) + .Consume(); // ensures the sequences are actually merged and iterators are obtained } } } diff --git a/MoreLinq.Test/SplitTest.cs b/MoreLinq.Test/SplitTest.cs index 9026cd93c..b7f6044de 100644 --- a/MoreLinq.Test/SplitTest.cs +++ b/MoreLinq.Test/SplitTest.cs @@ -40,25 +40,23 @@ public void SplitUptoMaxCount() public void SplitWithSeparatorSelector() { var result = new int?[] { 1, 2, null, 3, null, 4, 5, 6 }.Split(n => n == null); - using (var reader = result.Read()) - { - reader.Read().AssertSequenceEqual(1, 2); - reader.Read().AssertSequenceEqual(3); - reader.Read().AssertSequenceEqual(4, 5, 6); - reader.ReadEnd(); - } + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2); + reader.Read().AssertSequenceEqual(3); + reader.Read().AssertSequenceEqual(4, 5, 6); + reader.ReadEnd(); } [Test] public void SplitWithSeparatorSelectorUptoMaxCount() { var result = new int?[] { 1, 2, null, 3, null, 4, 5, 6 }.Split(n => n == null, 1); - using (var reader = result.Read()) - { - reader.Read().AssertSequenceEqual(1, 2); - reader.Read().AssertSequenceEqual(3, null, 4, 5, 6); - reader.ReadEnd(); - } + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2); + reader.Read().AssertSequenceEqual(3, null, 4, 5, 6); + reader.ReadEnd(); } } } diff --git a/MoreLinq.Test/StartsWithTest.cs b/MoreLinq.Test/StartsWithTest.cs index c4df100b5..3aeb83e43 100644 --- a/MoreLinq.Test/StartsWithTest.cs +++ b/MoreLinq.Test/StartsWithTest.cs @@ -72,11 +72,10 @@ public bool StartsWithReturnsTrueIfSecondIsEmpty(string first, string second) [Test] public void StartsWithDisposesBothSequenceEnumerators() { - using (var first = TestingSequence.Of(1,2,3)) - using (var second = TestingSequence.Of(1)) - { - first.StartsWith(second); - } + using var first = TestingSequence.Of(1,2,3); + using var second = TestingSequence.Of(1); + + first.StartsWith(second); } [Test] diff --git a/MoreLinq.Test/TakeLastTest.cs b/MoreLinq.Test/TakeLastTest.cs index 16c23e633..31548d411 100644 --- a/MoreLinq.Test/TakeLastTest.cs +++ b/MoreLinq.Test/TakeLastTest.cs @@ -57,10 +57,8 @@ public void TakeLastIsLazy() [Test] public void TakeLastDisposesSequenceEnumerator() { - using (var seq = TestingSequence.Of(1,2,3)) - { - seq.TakeLast(1).Consume(); - } + using var seq = TestingSequence.Of(1,2,3); + seq.TakeLast(1).Consume(); } [TestCase(SourceKind.BreakingList)] diff --git a/MoreLinq.Test/TransposeTest.cs b/MoreLinq.Test/TransposeTest.cs index d0436aead..097734165 100644 --- a/MoreLinq.Test/TransposeTest.cs +++ b/MoreLinq.Test/TransposeTest.cs @@ -33,14 +33,13 @@ public void TransposeIsLazy() [Test] public void TransposeWithOneNullRow() { - using (var seq1 = TestingSequence.Of(10, 11)) - using (var seq2 = TestingSequence.Of()) - using (var seq3 = TestingSequence.Of(30, 31, 32)) - using (var matrix = TestingSequence.Of(seq1, seq2, seq3, null)) - { - Assert.Throws(() => - matrix.Transpose().FirstOrDefault()); - } + using var seq1 = TestingSequence.Of(10, 11); + using var seq2 = TestingSequence.Of(); + using var seq3 = TestingSequence.Of(30, 31, 32); + using var matrix = TestingSequence.Of(seq1, seq2, seq3, null); + + Assert.Throws(() => + matrix.Transpose().FirstOrDefault()); } [Test] @@ -54,13 +53,12 @@ public void TransposeWithRowsOfSameLength() new [] { 13, 23, 33 }, }; - using (var row1 = TestingSequence.Of(10, 11, 12, 13)) - using (var row2 = TestingSequence.Of(20, 21, 22, 23)) - using (var row3 = TestingSequence.Of(30, 31, 32, 33)) - using (var matrix = TestingSequence.Of(row1, row2, row3)) - { - AssertMatrix(expectations, matrix.Transpose()); - } + using var row1 = TestingSequence.Of(10, 11, 12, 13); + using var row2 = TestingSequence.Of(20, 21, 22, 23); + using var row3 = TestingSequence.Of(30, 31, 32, 33); + using var matrix = TestingSequence.Of(row1, row2, row3); + + AssertMatrix(expectations, matrix.Transpose()); } [Test] @@ -73,14 +71,13 @@ public void TransposeWithRowsOfDifferentLengths() new[] { 32 } }; - using (var row1 = TestingSequence.Of(10, 11)) - using (var row2 = TestingSequence.Of(20)) - using (var row3 = TestingSequence.Of()) - using (var row4 = TestingSequence.Of(30, 31, 32)) - using (var matrix = TestingSequence.Of(row1, row2, row3, row4)) - { - AssertMatrix(expectations, matrix.Transpose()); - } + using var row1 = TestingSequence.Of(10, 11); + using var row2 = TestingSequence.Of(20); + using var row3 = TestingSequence.Of(); + using var row4 = TestingSequence.Of(30, 31, 32); + using var matrix = TestingSequence.Of(row1, row2, row3, row4); + + AssertMatrix(expectations, matrix.Transpose()); } [Test] @@ -184,16 +181,15 @@ public void TransposeConsumesRowsLazily() [Test] public void TransposeWithErroneousRowDisposesRowIterators() { - using (var row1 = TestingSequence.Of(10, 11)) - using (var row2 = MoreEnumerable.From(() => 20, - () => throw new TestException()) - .AsTestingSequence()) - using (var row3 = TestingSequence.Of(30, 32)) - using (var matrix = TestingSequence.Of(row1, row2, row3)) - { - Assert.Throws(() => - matrix.Transpose().Consume()); - } + using var row1 = TestingSequence.Of(10, 11); + using var row2 = MoreEnumerable.From(() => 20, + () => throw new TestException()) + .AsTestingSequence(); + using var row3 = TestingSequence.Of(30, 32); + using var matrix = TestingSequence.Of(row1, row2, row3); + + Assert.Throws(() => + matrix.Transpose().Consume()); } static bool IsPrime(int number) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index c63dc071c..10a49cfe1 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -84,11 +84,11 @@ public void WindowLeftWithNegativeWindowSize() [Test] public void WindowLeftWithEmptySequence() { - using (var xs = Enumerable.Empty().AsTestingSequence()) - { - var result = xs.WindowLeft(5); - Assert.That(result, Is.Empty); - } + using var xs = Enumerable.Empty().AsTestingSequence(); + + var result = xs.WindowLeft(5); + + Assert.That(result, Is.Empty); } [Test] @@ -112,31 +112,31 @@ public void WindowLeftWithSingleElement() [Test] public void WindowLeftWithWindowSizeLargerThanSequence() { - using (var sequence = Enumerable.Range(1, 5).AsTestingSequence()) - using (var reader = sequence.WindowLeft(10).Read()) - { - reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); - reader.Read().AssertSequenceEqual(2, 3, 4, 5); - reader.Read().AssertSequenceEqual(3, 4, 5); - reader.Read().AssertSequenceEqual(4, 5); - reader.Read().AssertSequenceEqual(5); - reader.ReadEnd(); - } + using var sequence = Enumerable.Range(1, 5).AsTestingSequence(); + + using var reader = sequence.WindowLeft(10).Read(); + + reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); + reader.Read().AssertSequenceEqual(2, 3, 4, 5); + reader.Read().AssertSequenceEqual(3, 4, 5); + reader.Read().AssertSequenceEqual(4, 5); + reader.Read().AssertSequenceEqual(5); + reader.ReadEnd(); } [Test] public void WindowLeftWithWindowSizeSmallerThanSequence() { - using (var sequence = Enumerable.Range(1, 5).AsTestingSequence()) - using (var reader = sequence.WindowLeft(3).Read()) - { - reader.Read().AssertSequenceEqual(1, 2, 3); - reader.Read().AssertSequenceEqual(2, 3, 4); - reader.Read().AssertSequenceEqual(3, 4, 5); - reader.Read().AssertSequenceEqual(4, 5); - reader.Read().AssertSequenceEqual(5); - reader.ReadEnd(); - } + using var sequence = Enumerable.Range(1, 5).AsTestingSequence(); + + using var reader = sequence.WindowLeft(3).Read(); + + reader.Read().AssertSequenceEqual(1, 2, 3); + reader.Read().AssertSequenceEqual(2, 3, 4); + reader.Read().AssertSequenceEqual(3, 4, 5); + reader.Read().AssertSequenceEqual(4, 5); + reader.Read().AssertSequenceEqual(5); + reader.ReadEnd(); } } } diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index d5296e307..6f2aa27df 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -84,11 +84,11 @@ public void WindowRightWithNegativeWindowSize() [Test] public void WindowRightWithEmptySequence() { - using (var xs = Enumerable.Empty().AsTestingSequence()) - { - var result = xs.WindowRight(5); - Assert.That(result, Is.Empty); - } + using var xs = Enumerable.Empty().AsTestingSequence(); + + var result = xs.WindowRight(5); + + Assert.That(result, Is.Empty); } [Test] @@ -112,31 +112,29 @@ public void WindowRightWithSingleElement() [Test] public void WindowRightWithWindowSizeLargerThanSequence() { - using (var sequence = Enumerable.Range(1, 5).AsTestingSequence()) - using (var reader = sequence.WindowRight(10).Read()) - { - reader.Read().AssertSequenceEqual( 1); - reader.Read().AssertSequenceEqual( 1, 2); - reader.Read().AssertSequenceEqual( 1, 2, 3); - reader.Read().AssertSequenceEqual( 1, 2, 3, 4); - reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); - reader.ReadEnd(); - } + using var sequence = Enumerable.Range(1, 5).AsTestingSequence(); + + using var reader = sequence.WindowRight(10).Read(); + reader.Read().AssertSequenceEqual( 1); + reader.Read().AssertSequenceEqual( 1, 2); + reader.Read().AssertSequenceEqual( 1, 2, 3); + reader.Read().AssertSequenceEqual( 1, 2, 3, 4); + reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); + reader.ReadEnd(); } [Test] public void WindowRightWithWindowSizeSmallerThanSequence() { - using (var sequence = Enumerable.Range(1, 5).AsTestingSequence()) - using (var reader = sequence.WindowRight(3).Read()) - { - reader.Read().AssertSequenceEqual( 1); - reader.Read().AssertSequenceEqual( 1, 2); - reader.Read().AssertSequenceEqual(1, 2, 3); - reader.Read().AssertSequenceEqual(2, 3, 4); - reader.Read().AssertSequenceEqual(3, 4, 5); - reader.ReadEnd(); - } + using var sequence = Enumerable.Range(1, 5).AsTestingSequence(); + + using var reader = sequence.WindowRight(3).Read(); + reader.Read().AssertSequenceEqual( 1); + reader.Read().AssertSequenceEqual( 1, 2); + reader.Read().AssertSequenceEqual(1, 2, 3); + reader.Read().AssertSequenceEqual(2, 3, 4); + reader.Read().AssertSequenceEqual(3, 4, 5); + reader.ReadEnd(); } } } diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 2fc765be9..b9ae4bf18 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -166,15 +166,14 @@ public void TestWindowSmallerThanSequence() [Test] public void TestWindowWindowsImmutability() { - using (var windows = Enumerable.Range(1, 5).Window(2).AsTestingSequence()) - using (var reader = windows.ToArray().Read()) - { - reader.Read().AssertSequenceEqual(1, 2); - reader.Read().AssertSequenceEqual(2, 3); - reader.Read().AssertSequenceEqual(3, 4); - reader.Read().AssertSequenceEqual(4, 5); - reader.ReadEnd(); - } + using var windows = Enumerable.Range(1, 5).Window(2).AsTestingSequence(); + + using var reader = windows.ToArray().Read(); + reader.Read().AssertSequenceEqual(1, 2); + reader.Read().AssertSequenceEqual(2, 3); + reader.Read().AssertSequenceEqual(3, 4); + reader.Read().AssertSequenceEqual(4, 5); + reader.ReadEnd(); } } } diff --git a/MoreLinq.Test/ZipLongestTest.cs b/MoreLinq.Test/ZipLongestTest.cs index 2046c17fd..f61470013 100644 --- a/MoreLinq.Test/ZipLongestTest.cs +++ b/MoreLinq.Test/ZipLongestTest.cs @@ -46,9 +46,9 @@ from e in new[] [Test, TestCaseSource(nameof(TestData))] public IEnumerable<(int, string)> ZipLongest(int[] first, string[] second) { - using (var ts1 = TestingSequence.Of(first)) - using (var ts2 = TestingSequence.Of(second)) - return ts1.ZipLongest(ts2, Tuple.Create).ToArray(); + using var ts1 = TestingSequence.Of(first); + using var ts2 = TestingSequence.Of(second); + return ts1.ZipLongest(ts2, Tuple.Create).ToArray(); } [Test] @@ -76,11 +76,10 @@ public void ZipLongestDisposeSequencesEagerly() [Test] public void ZipLongestDisposesInnerSequencesCaseGetEnumeratorThrows() { - using (var s1 = TestingSequence.Of(1, 2)) - { - Assert.Throws(() => - s1.ZipLongest(new BreakingSequence(), Tuple.Create).Consume()); - } + using var s1 = TestingSequence.Of(1, 2); + + Assert.Throws(() => + s1.ZipLongest(new BreakingSequence(), Tuple.Create).Consume()); } } } diff --git a/MoreLinq.Test/ZipShortestTest.cs b/MoreLinq.Test/ZipShortestTest.cs index 5b29f46c4..3d2d8f80e 100644 --- a/MoreLinq.Test/ZipShortestTest.cs +++ b/MoreLinq.Test/ZipShortestTest.cs @@ -27,21 +27,19 @@ public class ZipShortestTest [Test] public void BothSequencesDisposedWithUnequalLengthsAndLongerFirst() { - using (var longer = TestingSequence.Of(1, 2, 3)) - using (var shorter = TestingSequence.Of(1, 2)) - { - longer.ZipShortest(shorter, (x, y) => x + y).Consume(); - } + using var longer = TestingSequence.Of(1, 2, 3); + using var shorter = TestingSequence.Of(1, 2); + + longer.ZipShortest(shorter, (x, y) => x + y).Consume(); } [Test] public void BothSequencesDisposedWithUnequalLengthsAndShorterFirst() { - using (var longer = TestingSequence.Of(1, 2, 3)) - using (var shorter = TestingSequence.Of(1, 2)) - { - shorter.ZipShortest(longer, (x, y) => x + y).Consume(); - } + using var longer = TestingSequence.Of(1, 2, 3); + using var shorter = TestingSequence.Of(1, 2); + + shorter.ZipShortest(longer, (x, y) => x + y).Consume(); } [Test] @@ -78,42 +76,41 @@ public void ZipShortestIsLazy() [Test] public void MoveNextIsNotCalledUnnecessarilyWhenFirstIsShorter() { - using (var s1 = TestingSequence.Of(1, 2)) - using (var s2 = MoreEnumerable.From(() => 4, - () => 5, - () => throw new TestException()) - .AsTestingSequence()) - { - var zipped = s1.ZipShortest(s2, Tuple.Create); - Assert.That(zipped, Is.Not.Null); - zipped.AssertSequenceEqual((1, 4), (2, 5)); - } + using var s1 = TestingSequence.Of(1, 2); + using var s2 = MoreEnumerable.From(() => 4, + () => 5, + () => throw new TestException()) + .AsTestingSequence(); + + var zipped = s1.ZipShortest(s2, Tuple.Create); + + Assert.That(zipped, Is.Not.Null); + zipped.AssertSequenceEqual((1, 4), (2, 5)); } [Test] public void ZipShortestNotIterateUnnecessaryElements() { - using (var s1 = MoreEnumerable.From(() => 4, - () => 5, - () => 6, - () => throw new TestException()) - .AsTestingSequence()) - using (var s2 = TestingSequence.Of(1, 2)) - { - var zipped = s1.ZipShortest(s2, Tuple.Create); - Assert.That(zipped, Is.Not.Null); - zipped.AssertSequenceEqual((4, 1), (5, 2)); - } + using var s1 = MoreEnumerable.From(() => 4, + () => 5, + () => 6, + () => throw new TestException()) + .AsTestingSequence(); + using var s2 = TestingSequence.Of(1, 2); + + var zipped = s1.ZipShortest(s2, Tuple.Create); + + Assert.That(zipped, Is.Not.Null); + zipped.AssertSequenceEqual((4, 1), (5, 2)); } [Test] public void ZipShortestDisposesInnerSequencesCaseGetEnumeratorThrows() { - using (var s1 = TestingSequence.Of(1, 2)) - { - Assert.Throws(() => - s1.ZipShortest(new BreakingSequence(), Tuple.Create).Consume()); - } + using var s1 = TestingSequence.Of(1, 2); + + Assert.Throws(() => + s1.ZipShortest(new BreakingSequence(), Tuple.Create).Consume()); } } } From 70e4feb4460e673d72af4d0be58f20d56a4f76f5 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 8 Jan 2021 16:53:22 +0100 Subject: [PATCH 072/157] Fix indentation of generated extensions wrappers --- bld/ExtensionsGenerator/Program.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index 280b2a4ec..3b8c983bb 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -283,13 +283,13 @@ select Argument(IdentifierName(p.Identifier)), } into m select (!noClassLead ? $@" -/// {m.Name} extension. + /// {m.Name} extension. -[GeneratedCode(""{thisAssemblyName.Name}"", ""{thisAssemblyName.Version}"")]" : null) + $@" -public static partial class {m.Name}Extension -{{ + [GeneratedCode(""{thisAssemblyName.Name}"", ""{thisAssemblyName.Version}"")]" : null) + $@" + public static partial class {m.Name}Extension + {{ {string.Join(null, from mo in m.Overloads select mo.ToFullString())} -}}"; + }}"; var template = $@" #region License and Terms From 8868feb83331e8941be698e3a3c66703bc08b39d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Jan 2021 22:25:15 +0100 Subject: [PATCH 073/157] Update test project dependencies to latest versions --- MoreLinq.Test/MoreLinq.Test.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index ab381bd5c..3386c9e89 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -20,7 +20,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -33,8 +33,8 @@ runtime; build; native; contentfiles; analyzers all - - + + From 7bdbe7f45f24b1b47d09e1966e4ed321f2530837 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 13 Jan 2021 00:35:18 +0100 Subject: [PATCH 074/157] Fix grep invocation on Windows CI build (#791) The fix is to use PowerShell (PWSH) as opposed to Windows PowerShell (PS), which seems send malformed file paths (assuming a CR in there) to "grep". --- appveyor.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f75d8dd70..7624d9496 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -62,13 +62,14 @@ install: before_build: - dotnet --info build_script: -- ps: >- +- pwsh: | grep --extended-regexp '^[[:space:]]*using[[:space:]]+System\.Linq;' (dir -Recurse -File -Filter *Test.cs MoreLinq.Test) - if ($LASTEXITCODE -eq 0) { throw 'Unit tests should not import System.Linq' + } else { + $LASTEXITCODE = 0 } - +- ps: >- $id = $env:APPVEYOR_REPO_COMMIT_TIMESTAMP -replace '([-:]|\.0+Z)', '' $id = $id.Substring(0, 13) From 1138ed57953cabc430c2a6cfb65240598e7e4413 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 13 Jan 2021 18:42:20 +0100 Subject: [PATCH 075/157] Use consistent line-folding in CI YAML shell sections --- appveyor.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7624d9496..d2b14361a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -69,21 +69,15 @@ build_script: } else { $LASTEXITCODE = 0 } -- ps: >- +- ps: | $id = $env:APPVEYOR_REPO_COMMIT_TIMESTAMP -replace '([-:]|\.0+Z)', '' - $id = $id.Substring(0, 13) - if ($isWindows) { cmd /c call pack.cmd ci-$id } else { ./pack.sh ci-$id } - if ($LASTEXITCODE -ne 0) { throw "Building/Packing failed with an exit code of $LASTEXITCODE." } - $diff = git diff --ignore-all-space --exit-code 2>&1 - $diff | % { if ($_ -is [string]) { $_ } else { [string]$_ } } | echo - if ($LASTEXITCODE -ne 0) { throw "New code was generated during build that's not been committed." } From 0aa24fd4f07c08769a9f52196bcecad0158fa342 Mon Sep 17 00:00:00 2001 From: MarcDirven <31624073+MarcDirven@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:25:55 +0100 Subject: [PATCH 076/157] Fix typo in ScanRight doc example ("+" instead of "/") This is a squashed merge of PR #796 that fixes #795. --- MoreLinq/Extensions.g.cs | 2 +- MoreLinq/ScanRight.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index b40abc470..4f94d77d3 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -4970,7 +4970,7 @@ public static partial class ScanRightExtension /// The scanned sequence. /// /// i.ToString()).ScanRight((a, b) => string.Format("({0}/{1})", a, b)); + /// var result = Enumerable.Range(1, 5).Select(i => i.ToString()).ScanRight((a, b) => $"({a}+{b})"); /// ]]> /// The result variable will contain [ "(1+(2+(3+(4+5))))", "(2+(3+(4+5)))", "(3+(4+5))", "(4+5)", "5" ]. /// diff --git a/MoreLinq/ScanRight.cs b/MoreLinq/ScanRight.cs index 648aaa470..131aced3c 100644 --- a/MoreLinq/ScanRight.cs +++ b/MoreLinq/ScanRight.cs @@ -36,7 +36,7 @@ static partial class MoreEnumerable /// The scanned sequence. /// /// i.ToString()).ScanRight((a, b) => string.Format("({0}/{1})", a, b)); + /// var result = Enumerable.Range(1, 5).Select(i => i.ToString()).ScanRight((a, b) => $"({a}+{b})"); /// ]]> /// The result variable will contain [ "(1+(2+(3+(4+5))))", "(2+(3+(4+5)))", "(3+(4+5))", "(4+5)", "5" ]. /// From 153af163838b79bb0fd4ca954b7a7b738eaac8df Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Mar 2021 21:59:57 +0100 Subject: [PATCH 077/157] Remove phantom "Properties" folder from project --- MoreLinq/MoreLinq.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index f958f8280..5a1689b90 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -244,10 +244,6 @@ - - - - all From c5c569e7046ffde6c16244782f349298619533be Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Mar 2021 21:57:46 +0100 Subject: [PATCH 078/157] Use null-coalescing assignment --- MoreLinq/Batch.cs | 4 +--- MoreLinq/Split.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index 12f20030d..4621f46cf 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -130,9 +130,7 @@ IEnumerable Batch(int size) foreach (var item in source) { - if (bucket == null) - bucket = new TSource[size]; - + bucket ??= new TSource[size]; bucket[count++] = item; // The bucket is fully buffered before it's yielded diff --git a/MoreLinq/Split.cs b/MoreLinq/Split.cs index 86d6e184b..93f52c22e 100644 --- a/MoreLinq/Split.cs +++ b/MoreLinq/Split.cs @@ -287,9 +287,7 @@ public static IEnumerable Split(this IEnumerable(); - + items ??= new List(); items.Add(item); } } From 9b9be9c5ebd68986661da81c56bf070274aec447 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Mar 2021 22:08:45 +0100 Subject: [PATCH 079/157] Remove phantom "Properties" folder from test project [ci skip] --- MoreLinq.Test/MoreLinq.Test.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 3386c9e89..7558169e9 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -86,10 +86,6 @@ - - - - From 9d71daec1b9322035cba67a1d86c1660cd0fa91b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Mar 2021 22:36:10 +0100 Subject: [PATCH 080/157] Fix dictionary bug when null key has null value --- MoreLinq.Test/ScanByTest.cs | 15 +++++++++++++++ MoreLinq/Collections/Dictionary.cs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/MoreLinq.Test/ScanByTest.cs b/MoreLinq.Test/ScanByTest.cs index 7a11a698b..ab5098389 100644 --- a/MoreLinq.Test/ScanByTest.cs +++ b/MoreLinq.Test/ScanByTest.cs @@ -115,6 +115,21 @@ public void ScanByWithSomeNullKeys() KeyValuePair.Create("foo" , 1)); } + [Test] + public void ScanByWithNullSeed() + { + var nil = (object)null; + var source = new[] { "foo", null, "bar", null, "baz" }; + var result = source.ScanBy(c => c, k => nil, (i, k, e) => nil); + + result.AssertSequenceEqual( + KeyValuePair.Create("foo" , nil), + KeyValuePair.Create((string)null, nil), + KeyValuePair.Create("bar" , nil), + KeyValuePair.Create((string)null, nil), + KeyValuePair.Create("baz" , nil)); + } + [Test] public void ScanByDoesNotIterateUnnecessaryElements() { diff --git a/MoreLinq/Collections/Dictionary.cs b/MoreLinq/Collections/Dictionary.cs index 796fee0b5..ef2bf08ab 100644 --- a/MoreLinq/Collections/Dictionary.cs +++ b/MoreLinq/Collections/Dictionary.cs @@ -56,7 +56,7 @@ public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { switch (_null) { - case (true, {} v): + case (true, var v): value = v; return true; case (false, _): From dae16605c05a46eaae32ce59e325a19352279fd8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 18 Mar 2021 18:58:08 +0100 Subject: [PATCH 081/157] Fix "TrySingle" signature with more honest nullable annotations (#801) --- MoreLinq/Experimental/TrySingle.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/MoreLinq/Experimental/TrySingle.cs b/MoreLinq/Experimental/TrySingle.cs index 6be3f8212..e72bfbc82 100644 --- a/MoreLinq/Experimental/TrySingle.cs +++ b/MoreLinq/Experimental/TrySingle.cs @@ -51,7 +51,7 @@ partial class ExperimentalEnumerable /// than two elements from the sequence. /// - public static (TCardinality Cardinality, T Value) + public static (TCardinality Cardinality, T? Value) TrySingle(this IEnumerable source, TCardinality zero, TCardinality one, TCardinality many) => TrySingle(source, zero, one, many, ValueTuple.Create); @@ -96,11 +96,7 @@ public static (TCardinality Cardinality, T Value) public static TResult TrySingle(this IEnumerable source, TCardinality zero, TCardinality one, TCardinality many, - // TODO review second argument of resultSelector - // ...that can be defaulted to null for nullable references - // so the signature is not quite accurate, but can we do - // something about that? - Func resultSelector) + Func resultSelector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); @@ -108,7 +104,7 @@ public static TResult TrySingle(this IEnumerable so switch (source.TryGetCollectionCount()) { case 0: - return resultSelector(zero, default!); + return resultSelector(zero, default); case 1: { var item = source switch @@ -120,15 +116,15 @@ public static TResult TrySingle(this IEnumerable so return resultSelector(one, item); } case {}: - return resultSelector(many, default!); + return resultSelector(many, default); default: { using var e = source.GetEnumerator(); if (!e.MoveNext()) - return resultSelector(zero, default!); + return resultSelector(zero, default); var current = e.Current; return !e.MoveNext() ? resultSelector(one, current) - : resultSelector(many, default!); + : resultSelector(many, default); } } } From 65a917f46649cd96c325c9be440d39700d0bb351 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 19 Mar 2021 16:40:37 +0100 Subject: [PATCH 082/157] Remove redundant use of dammit (!) in dictionary --- MoreLinq/Collections/Dictionary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/Collections/Dictionary.cs b/MoreLinq/Collections/Dictionary.cs index ef2bf08ab..6366554ab 100644 --- a/MoreLinq/Collections/Dictionary.cs +++ b/MoreLinq/Collections/Dictionary.cs @@ -60,7 +60,7 @@ public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) value = v; return true; case (false, _): - value = default!; + value = default; return false; } } From 7458bf13c3e47490abfccdb09f18284654a371dd Mon Sep 17 00:00:00 2001 From: sid-6581 Date: Mon, 22 Mar 2021 14:20:39 -0700 Subject: [PATCH 083/157] Cleanup dammit ("!") usage in "FallbackIfEmpty" (#808) --- MoreLinq/FallbackIfEmpty.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index d872ce192..0d0fbc867 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -46,7 +46,7 @@ static partial class MoreEnumerable public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback) { if (source == null) throw new ArgumentNullException(nameof(source)); - return FallbackIfEmptyImpl(source, 1, fallback, default!, default!, default!, null); + return FallbackIfEmptyImpl(source, 1, fallback, default, default, default, null); } /// @@ -67,7 +67,7 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fa public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2) { if (source == null) throw new ArgumentNullException(nameof(source)); - return FallbackIfEmptyImpl(source, 2, fallback1, fallback2, default!, default!, null); + return FallbackIfEmptyImpl(source, 2, fallback1, fallback2, default, default, null); } /// @@ -90,7 +90,7 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fa public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2, T fallback3) { if (source == null) throw new ArgumentNullException(nameof(source)); - return FallbackIfEmptyImpl(source, 3, fallback1, fallback2, fallback3, default!, null); + return FallbackIfEmptyImpl(source, 3, fallback1, fallback2, fallback3, default, null); } /// @@ -155,11 +155,11 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, IEnu { if (source == null) throw new ArgumentNullException(nameof(source)); if (fallback == null) throw new ArgumentNullException(nameof(fallback)); - return FallbackIfEmptyImpl(source, null, default!, default!, default!, default!, fallback); + return FallbackIfEmptyImpl(source, null, default, default, default, default, fallback); } static IEnumerable FallbackIfEmptyImpl(IEnumerable source, - int? count, T fallback1, T fallback2, T fallback3, T fallback4, + int? count, T? fallback1, T? fallback2, T? fallback3, T? fallback4, IEnumerable? fallback) { return source.TryGetCollectionCount() is {} collectionCount @@ -190,10 +190,10 @@ IEnumerable FallbackOnArgs() { Debug.Assert(count >= 1 && count <= 4); - yield return fallback1; - if (count > 1) yield return fallback2; - if (count > 2) yield return fallback3; - if (count > 3) yield return fallback4; + yield return fallback1!; + if (count > 1) yield return fallback2!; + if (count > 2) yield return fallback3!; + if (count > 3) yield return fallback4!; } } } From 56b1079c455683cb52522a7e713121a7015949cb Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 22 Mar 2021 22:43:06 +0100 Subject: [PATCH 084/157] Fix type arg code formatting inconsistency --- MoreLinq/FullGroupJoin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/FullGroupJoin.cs b/MoreLinq/FullGroupJoin.cs index c1a331057..4a1f3d84d 100644 --- a/MoreLinq/FullGroupJoin.cs +++ b/MoreLinq/FullGroupJoin.cs @@ -151,7 +151,7 @@ public static IEnumerable FullGroupJoin IEnumerable _(IEqualityComparer comparer) { - var alookup = Lookup.CreateForJoin(first, firstKeySelector, comparer); + var alookup = Lookup.CreateForJoin(first, firstKeySelector, comparer); var blookup = Lookup.CreateForJoin(second, secondKeySelector, comparer); foreach (var a in alookup) From f98e0b3869f37b33027c9a678c99798b8542872e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 22 Mar 2021 22:43:50 +0100 Subject: [PATCH 085/157] Add missing "[Test]" on "FullGroupJoin" lazy test --- MoreLinq.Test/FullGroupJoinTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index adae88d99..d718e2638 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -27,6 +27,7 @@ public class FullGroupJoinTest { public enum OverloadCase { CustomResult, TupleResult } + [Test] public void FullGroupIsLazy() { var bs = new BreakingSequence(); From ed0cf7111c1bcc093bcfc0620fab211fd0784aab Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 22 Mar 2021 22:47:44 +0100 Subject: [PATCH 086/157] Fix "Pad*" signatures with more honest nullable annotations This is squashed merge or PR #804 that adds to #803. --- MoreLinq.Test/PadStartTest.cs | 81 +++++++++++++++++++++++-------- MoreLinq.Test/PadTest.cs | 89 +++++++++++++++++++++++++---------- MoreLinq/Extensions.g.cs | 4 +- MoreLinq/Pad.cs | 12 ++--- MoreLinq/PadStart.cs | 12 ++--- 5 files changed, 138 insertions(+), 60 deletions(-) diff --git a/MoreLinq.Test/PadStartTest.cs b/MoreLinq.Test/PadStartTest.cs index a0091a374..a1124712c 100644 --- a/MoreLinq.Test/PadStartTest.cs +++ b/MoreLinq.Test/PadStartTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using NUnit.Framework; @@ -38,13 +40,25 @@ public void PadStartIsLazy() new BreakingSequence().PadStart(0); } - [TestCase(new[] { 123, 456, 789 }, 2, new[] { 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 3, new[] { 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 4, new[] { 0, 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 5, new[] { 0, 0, 123, 456, 789 })] - public void PadStart(ICollection source, int width, IEnumerable expected) + public class PadStartWithDefaultPadding { - AssertEqual(source, x => x.PadStart(width), expected); + [TestCase(new[] { 123, 456, 789 }, 2, new[] { 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 3, new[] { 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 4, new[] { 0, 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 5, new[] { 0, 0, 123, 456, 789 })] + public void ValueTypeElements(ICollection source, int width, IEnumerable expected) + { + AssertEqual(source, x => x.PadStart(width), expected); + } + + [TestCase(new[] { "foo", "bar", "baz" }, 2, new[] { "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 3, new[] { "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 4, new[] { null, "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 5, new[] { null, null, "foo", "bar", "baz" })] + public void ReferenceTypeElements(ICollection source, int width, IEnumerable expected) + { + AssertEqual(source, x => x.PadStart(width), expected); + } } // PadStart(source, width, padding) @@ -61,13 +75,25 @@ public void PadStartWithPaddingIsLazy() new BreakingSequence().PadStart(0, -1); } - [TestCase(new[] { 123, 456, 789 }, 2, new[] { 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 3, new[] { 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 4, new[] { -1, 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 5, new[] { -1, -1, 123, 456, 789 })] - public void PadStartWithPadding(ICollection source, int width, IEnumerable expected) + public class PadStartWithPadding { - AssertEqual(source, x => x.PadStart(width, -1), expected); + [TestCase(new[] { 123, 456, 789 }, 2, new[] { 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 3, new[] { 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 4, new[] { -1, 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 5, new[] { -1, -1, 123, 456, 789 })] + public void ValueTypeElements(ICollection source, int width, IEnumerable expected) + { + AssertEqual(source, x => x.PadStart(width, -1), expected); + } + + [TestCase(new[] { "foo", "bar", "baz" }, 2, new[] { "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 3, new[] { "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 4, new[] { "", "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 5, new[] { "", "", "foo", "bar", "baz" })] + public void ReferenceTypeElements(ICollection source, int width, IEnumerable expected) + { + AssertEqual(source, x => x.PadStart(width, string.Empty), expected); + } } // PadStart(source, width, paddingSelector) @@ -84,17 +110,32 @@ public void PadStartWithSelectorIsLazy() new BreakingSequence().PadStart(0, BreakingFunc.Of()); } - [TestCase(new[] { 123, 456, 789 }, 2, new[] { 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 3, new[] { 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 4, new[] { 0, 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 5, new[] { 0, -1, 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 6, new[] { 0, -1, -4, 123, 456, 789 })] - [TestCase(new[] { 123, 456, 789 }, 7, new[] { 0, -1, -4, -9, 123, 456, 789 })] - public void PadStartWithSelector(ICollection source, int width, IEnumerable expected) + public class PadStartWithSelector { - AssertEqual(source, x => x.PadStart(width, y => y * -y), expected); + [TestCase(new[] { 123, 456, 789 }, 2, new[] { 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 3, new[] { 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 4, new[] { 0, 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 5, new[] { 0, -1, 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 6, new[] { 0, -1, -4, 123, 456, 789 })] + [TestCase(new[] { 123, 456, 789 }, 7, new[] { 0, -1, -4, -9, 123, 456, 789 })] + public void ValueTypeElements(ICollection source, int width, IEnumerable expected) + { + AssertEqual(source, x => x.PadStart(width, y => y * -y), expected); + } + + [TestCase(new[] { "foo", "bar", "baz" }, 2, new[] { "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 3, new[] { "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 4, new[] { "+", "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 5, new[] { "+", "++", "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 6, new[] { "+", "++", "+++", "foo", "bar", "baz" })] + [TestCase(new[] { "foo", "bar", "baz" }, 7, new[] { "+", "++", "+++", "++++", "foo", "bar", "baz" })] + public void ReferenceTypeElements(ICollection source, int width, IEnumerable expected) + { + AssertEqual(source, x => x.PadStart(width, y => new string('+', y + 1)), expected); + } } + static void AssertEqual(ICollection input, Func, IEnumerable> op, IEnumerable expected) { // Test that the behaviour does not change whether a collection diff --git a/MoreLinq.Test/PadTest.cs b/MoreLinq.Test/PadTest.cs index 6a333bf82..a7d2ccc58 100644 --- a/MoreLinq.Test/PadTest.cs +++ b/MoreLinq.Test/PadTest.cs @@ -15,8 +15,11 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { + using System.Collections.Generic; using NUnit.Framework; [TestFixture] @@ -41,39 +44,73 @@ public void PadWithFillerIsLazy() new BreakingSequence().Pad(0, new object()); } - [Test] - public void PadWideSourceSequence() + public class ValueTypeElements { - var result = new[] { 123, 456, 789 }.Pad(2); - result.AssertSequenceEqual(123, 456, 789); - } + [Test] + public void PadWideSourceSequence() + { + var result = new[] { 123, 456, 789 }.Pad(2); + result.AssertSequenceEqual(123, 456, 789); + } - [Test] - public void PadEqualSourceSequence() - { - var result = new[] { 123, 456, 789 }.Pad(3); - result.AssertSequenceEqual(123, 456, 789); - } + [Test] + public void PadEqualSourceSequence() + { + var result = new[] { 123, 456, 789 }.Pad(3); + result.AssertSequenceEqual(123, 456, 789); + } - [Test] - public void PadNarrowSourceSequenceWithDefaultPadding() - { - var result = new[] { 123, 456, 789 }.Pad(5); - result.AssertSequenceEqual(123, 456, 789, 0, 0); - } + [Test] + public void PadNarrowSourceSequenceWithDefaultPadding() + { + var result = new[] { 123, 456, 789 }.Pad(5); + result.AssertSequenceEqual(123, 456, 789, 0, 0); + } - [Test] - public void PadNarrowSourceSequenceWithNonDefaultPadding() - { - var result = new[] { 123, 456, 789 }.Pad(5, -1); - result.AssertSequenceEqual(123, 456, 789, -1, -1); + [Test] + public void PadNarrowSourceSequenceWithNonDefaultPadding() + { + var result = new[] { 123, 456, 789 }.Pad(5, -1); + result.AssertSequenceEqual(123, 456, 789, -1, -1); + } + + [Test] + public void PadNarrowSourceSequenceWithDynamicPadding() + { + var result = "hello".ToCharArray().Pad(15, i => i % 2 == 0 ? '+' : '-'); + result.AssertSequenceEqual("hello-+-+-+-+-+".ToCharArray()); + } } - [Test] - public void PadNarrowSourceSequenceWithDynamicPadding() + public class ReferenceTypeElements { - var result = "hello".ToCharArray().Pad(15, i => i % 2 == 0 ? '+' : '-'); - result.AssertSequenceEqual("hello-+-+-+-+-+".ToCharArray()); + [Test] + public void PadWideSourceSequence() + { + var result = new[] { "foo", "bar", "baz" }.Pad(2); + result.AssertSequenceEqual("foo", "bar", "baz"); + } + + [Test] + public void PadEqualSourceSequence() + { + var result = new[] { "foo", "bar", "baz" }.Pad(3); + result.AssertSequenceEqual("foo", "bar", "baz"); + } + + [Test] + public void PadNarrowSourceSequenceWithDefaultPadding() + { + var result = new[] { "foo", "bar", "baz" }.Pad(5); + result.AssertSequenceEqual("foo", "bar", "baz", null, null); + } + + [Test] + public void PadNarrowSourceSequenceWithNonDefaultPadding() + { + var result = new[] { "foo", "bar", "baz" }.Pad(5, string.Empty); + result.AssertSequenceEqual("foo", "bar", "baz", string.Empty, string.Empty); + } } } } diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 4f94d77d3..992b78bc2 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3747,7 +3747,7 @@ public static partial class PadExtension /// 123, 456, 789 and two zeroes, in turn. /// - public static IEnumerable Pad(this IEnumerable source, int width) + public static IEnumerable Pad(this IEnumerable source, int width) => MoreEnumerable.Pad(source, width); /// @@ -3833,7 +3833,7 @@ public static partial class PadStartExtension /// The result variable will contain { 0, 0, 123, 456, 789 }. /// - public static IEnumerable PadStart(this IEnumerable source, int width) + public static IEnumerable PadStart(this IEnumerable source, int width) => MoreEnumerable.PadStart(source, width); /// diff --git a/MoreLinq/Pad.cs b/MoreLinq/Pad.cs index cfe9c6921..e046b262b 100644 --- a/MoreLinq/Pad.cs +++ b/MoreLinq/Pad.cs @@ -46,9 +46,9 @@ static partial class MoreEnumerable /// 123, 456, 789 and two zeroes, in turn. /// - public static IEnumerable Pad(this IEnumerable source, int width) + public static IEnumerable Pad(this IEnumerable source, int width) { - return Pad(source, width, default(TSource)!); + return Pad(source, width, default(TSource)); } /// @@ -111,11 +111,11 @@ public static IEnumerable Pad(this IEnumerable source if (source == null) throw new ArgumentNullException(nameof(source)); if (paddingSelector == null) throw new ArgumentNullException(nameof(paddingSelector)); if (width < 0) throw new ArgumentException(null, nameof(width)); - return PadImpl(source, width, default!, paddingSelector); + return PadImpl(source, width, default, paddingSelector); } - static IEnumerable PadImpl(IEnumerable source, - int width, T padding, Func? paddingSelector) + static IEnumerable PadImpl(IEnumerable source, int width, + T? padding, Func? paddingSelector) { Debug.Assert(width >= 0); @@ -127,7 +127,7 @@ static IEnumerable PadImpl(IEnumerable source, } while (count < width) { - yield return paddingSelector != null ? paddingSelector(count) : padding; + yield return paddingSelector != null ? paddingSelector(count) : padding!; count++; } } diff --git a/MoreLinq/PadStart.cs b/MoreLinq/PadStart.cs index e630f3d7d..c561c678f 100644 --- a/MoreLinq/PadStart.cs +++ b/MoreLinq/PadStart.cs @@ -45,9 +45,9 @@ static partial class MoreEnumerable /// The result variable will contain { 0, 0, 123, 456, 789 }. /// - public static IEnumerable PadStart(this IEnumerable source, int width) + public static IEnumerable PadStart(this IEnumerable source, int width) { - return PadStart(source, width, default(TSource)!); + return PadStart(source, width, default(TSource)); } /// @@ -112,18 +112,18 @@ public static IEnumerable PadStart(this IEnumerable s if (source == null) throw new ArgumentNullException(nameof(source)); if (paddingSelector == null) throw new ArgumentNullException(nameof(paddingSelector)); if (width < 0) throw new ArgumentException(null, nameof(width)); - return PadStartImpl(source, width, default!, paddingSelector); + return PadStartImpl(source, width, default, paddingSelector); } static IEnumerable PadStartImpl(IEnumerable source, - int width, T padding, Func? paddingSelector) + int width, T? padding, Func? paddingSelector) { return source.TryGetCollectionCount() is {} collectionCount ? collectionCount >= width ? source : Enumerable.Range(0, width - collectionCount) - .Select(i => paddingSelector != null ? paddingSelector(i) : padding) + .Select(i => paddingSelector != null ? paddingSelector(i) : padding!) .Concat(source) : _(); IEnumerable _() { @@ -150,7 +150,7 @@ static IEnumerable PadStartImpl(IEnumerable source, var len = width - count; for (var i = 0; i < len; i++) - yield return paddingSelector != null ? paddingSelector(i) : padding; + yield return paddingSelector != null ? paddingSelector(i) : padding!; for (var i = 0; i < count; i++) yield return array[i]; From 54ccb69f972943a0057a5a546cc795e5e704494b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 22 Mar 2021 22:49:59 +0100 Subject: [PATCH 087/157] Fix "Lag" & "Lead" signatures with more honest nullable annotations This is a squashed merge of PR #805 that adds to #803. --- MoreLinq.Test/LagTest.cs | 27 +++++++++++++++++++++++++++ MoreLinq.Test/LeadTest.cs | 27 +++++++++++++++++++++++++++ MoreLinq/Extensions.g.cs | 4 ++-- MoreLinq/Lag.cs | 9 +++++++-- MoreLinq/Lead.cs | 9 +++++++-- MoreLinq/MoreEnumerable.cs | 4 ++++ 6 files changed, 74 insertions(+), 6 deletions(-) diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index dd864b9f9..331fae6f7 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using NUnit.Framework; @@ -131,5 +133,30 @@ public void TestLagPassesCorrectLagValuesOffsetBy2() Assert.IsTrue(result.Skip(2).All(x => x.B == (x.A - 2))); Assert.IsTrue(result.Take(2).All(x => (x.A - x.B) == x.A)); } + + [Test] + public void TestLagWithNullableReferences() + { + var words = new[] { "foo", "bar", "baz", "qux" }; + var result = words.Lag(2, (a, b) => new { A = a, B = b }); + result.AssertSequenceEqual( + new { A = "foo", B = (string?)null }, + new { A = "bar", B = (string?)null }, + new { A = "baz", B = (string?)"foo" }, + new { A = "qux", B = (string?)"bar" }); + } + + [Test] + public void TestLagWithNonNullableReferences() + { + var words = new[] { "foo", "bar", "baz", "qux" }; + var empty = string.Empty; + var result = words.Lag(2, empty, (a, b) => new { A = a, B = b }); + result.AssertSequenceEqual( + new { A = "foo", B = empty }, + new { A = "bar", B = empty }, + new { A = "baz", B = "foo" }, + new { A = "qux", B = "bar" }); + } } } diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index 4953a3afd..e0d4c592d 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using NUnit.Framework; @@ -133,5 +135,30 @@ public void TestLeadPassesCorrectValueOffsetBy2() Assert.IsTrue(result.Take(count - 2).All(x => x.B == (x.A + 2))); Assert.IsTrue(result.Skip(count - 2).All(x => x.B == leadDefault && (x.A == count || x.A == count - 1))); } + + [Test] + public void TestLagWithNullableReferences() + { + var words = new[] { "foo", "bar", "baz", "qux" }; + var result = words.Lead(2, (a, b) => new { A = a, B = b }); + result.AssertSequenceEqual( + new { A = "foo", B = (string?)"baz" }, + new { A = "bar", B = (string?)"qux" }, + new { A = "baz", B = (string?)null }, + new { A = "qux", B = (string?)null }); + } + + [Test] + public void TestLagWithNonNullableReferences() + { + var words = new[] { "foo", "bar", "baz", "qux" }; + var empty = string.Empty; + var result = words.Lead(2, empty, (a, b) => new { A = a, B = b }); + result.AssertSequenceEqual( + new { A = "foo", B = "baz" }, + new { A = "bar", B = "qux" }, + new { A = "baz", B = empty }, + new { A = "qux", B = empty }); + } } } diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 992b78bc2..9495a2242 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3025,7 +3025,7 @@ public static partial class LagExtension /// A projection function which accepts the current and lagged items (in that order) and returns a result /// A sequence produced by projecting each element of the sequence with its lagged pairing - public static IEnumerable Lag(this IEnumerable source, int offset, Func resultSelector) + public static IEnumerable Lag(this IEnumerable source, int offset, Func resultSelector) => MoreEnumerable.Lag(source, offset, resultSelector); /// @@ -3114,7 +3114,7 @@ public static partial class LeadExtension /// A projection function which accepts the current and subsequent (lead) element (in that order) and produces a result /// A sequence produced by projecting each element of the sequence with its lead pairing - public static IEnumerable Lead(this IEnumerable source, int offset, Func resultSelector) + public static IEnumerable Lead(this IEnumerable source, int offset, Func resultSelector) => MoreEnumerable.Lead(source, offset, resultSelector); /// diff --git a/MoreLinq/Lag.cs b/MoreLinq/Lag.cs index 533bbeac7..66bd4a1e7 100644 --- a/MoreLinq/Lag.cs +++ b/MoreLinq/Lag.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Linq; public static partial class MoreEnumerable { @@ -36,9 +37,13 @@ public static partial class MoreEnumerable /// A projection function which accepts the current and lagged items (in that order) and returns a result /// A sequence produced by projecting each element of the sequence with its lagged pairing - public static IEnumerable Lag(this IEnumerable source, int offset, Func resultSelector) + public static IEnumerable Lag(this IEnumerable source, int offset, Func resultSelector) { - return Lag(source, offset, default!, resultSelector); + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector is null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.Select(Some) + .Lag(offset, default, (curr, lag) => resultSelector(curr.Value, lag is (true, var some) ? some : default)); } /// diff --git a/MoreLinq/Lead.cs b/MoreLinq/Lead.cs index ba627088c..25c7637c7 100644 --- a/MoreLinq/Lead.cs +++ b/MoreLinq/Lead.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Linq; public static partial class MoreEnumerable { @@ -37,9 +38,13 @@ public static partial class MoreEnumerable /// A projection function which accepts the current and subsequent (lead) element (in that order) and produces a result /// A sequence produced by projecting each element of the sequence with its lead pairing - public static IEnumerable Lead(this IEnumerable source, int offset, Func resultSelector) + public static IEnumerable Lead(this IEnumerable source, int offset, Func resultSelector) { - return Lead(source, offset, default!, resultSelector); + if (source is null) throw new ArgumentNullException(nameof(source)); + if (resultSelector is null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.Select(Some) + .Lead(offset, default, (curr, lead) => resultSelector(curr.Value, lead is (true, var some) ? some : default)); } /// diff --git a/MoreLinq/MoreEnumerable.cs b/MoreLinq/MoreEnumerable.cs index 7d0630dbb..ca7938cc7 100644 --- a/MoreLinq/MoreEnumerable.cs +++ b/MoreLinq/MoreEnumerable.cs @@ -53,5 +53,9 @@ static int CountUpTo(this IEnumerable source, int max) return count; } + + // See https://github.com/atifaziz/Optuple + + static (bool HasValue, T Value) Some(T value) => (true, value); } } From b3268c56415ba19a7484b2bd7a8a483845d695ac Mon Sep 17 00:00:00 2001 From: sid-6581 Date: Mon, 22 Mar 2021 14:56:04 -0700 Subject: [PATCH 088/157] Fix "ReverseComparer" ctor to say underlying comparer is nullable --- MoreLinq/ReverseComparer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/ReverseComparer.cs b/MoreLinq/ReverseComparer.cs index 859a1c645..bb46d3da5 100644 --- a/MoreLinq/ReverseComparer.cs +++ b/MoreLinq/ReverseComparer.cs @@ -23,7 +23,7 @@ sealed class ReverseComparer : IComparer { readonly IComparer _underlying; - public ReverseComparer(IComparer underlying) + public ReverseComparer(IComparer? underlying) { _underlying = underlying ?? Comparer.Default; } From 657e327bcf0ca0def3d72870f67ed8f5c5be50c1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 23 Mar 2021 23:05:23 +0100 Subject: [PATCH 089/157] Fix "GroupAdjacent" null key handling bug (#810) Fixes #809. --- MoreLinq.Test/GroupAdjacentTest.cs | 24 ++++++++++++++++++++++++ MoreLinq/GroupAdjacent.cs | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index f8bb13ea9..995405f1d 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -198,6 +198,30 @@ public void GroupAdjacentSourceSequenceResultSelectorComparer() reader.ReadEnd(); } + [Test] + public void GroupAdjacentSourceSequenceWithSomeNullKeys() + { + var groupings = + Enumerable.Range(1, 5) + .SelectMany(x => Enumerable.Repeat((int?)x, x).Append(null)) + .GroupAdjacent(x => x); + + int?[] aNull = { null }; + + using var reader = groupings.Read(); + AssertGrouping(reader, 1, 1); + AssertGrouping(reader, null, aNull); + AssertGrouping(reader, 2, 2, 2); + AssertGrouping(reader, null, aNull); + AssertGrouping(reader, 3, 3, 3, 3); + AssertGrouping(reader, null, aNull); + AssertGrouping(reader, 4, 4, 4, 4, 4); + AssertGrouping(reader, null, aNull); + AssertGrouping(reader, 5, 5, 5, 5, 5, 5); + AssertGrouping(reader, null, aNull); + reader.ReadEnd(); + } + static void AssertGrouping(SequenceReader> reader, TKey key, params TElement[] elements) { diff --git a/MoreLinq/GroupAdjacent.cs b/MoreLinq/GroupAdjacent.cs index f19eeeb9a..cc57e5167 100644 --- a/MoreLinq/GroupAdjacent.cs +++ b/MoreLinq/GroupAdjacent.cs @@ -273,7 +273,7 @@ static IEnumerable GroupAdjacentImpl( var key = keySelector(iterator.Current); var element = elementSelector(iterator.Current); - if (group is ({} k, {} members)) + if (group is (var k, {} members)) { if (comparer.Equals(k, key)) { @@ -290,7 +290,7 @@ static IEnumerable GroupAdjacentImpl( } { - if (group is ({} k, {} members)) + if (group is (var k, {} members)) yield return resultSelector(k, members); } } From ee238241083a9b31dc03c39781b29f4189b4fe73 Mon Sep 17 00:00:00 2001 From: sid-6581 Date: Mon, 29 Mar 2021 03:19:22 -0500 Subject: [PATCH 090/157] Add missing nullability annotation for "FullGroupJoin" comparer (#806) --- MoreLinq.Test/FullGroupJoinTest.cs | 4 +++- MoreLinq/Extensions.g.cs | 4 ++-- MoreLinq/FullGroupJoin.cs | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index d718e2638..40d8778c5 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; @@ -136,7 +138,7 @@ public void FullGroupPreservesOrder(OverloadCase overloadCase) switch (overloadCase) { case CustomResult: - return listA.FullGroupJoin(listB, getKey, getKey, ValueTuple.Create); + return listA.FullGroupJoin(listB, getKey, getKey, ValueTuple.Create, comparer: null); case TupleResult: return listA.FullGroupJoin(listB, getKey, getKey); default: diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 9495a2242..bfae169ac 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -2391,7 +2391,7 @@ public static partial class FullGroupJoinExtension IEnumerable second, Func firstKeySelector, Func secondKeySelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.FullGroupJoin(first, second, firstKeySelector, secondKeySelector, comparer); /// @@ -2450,7 +2450,7 @@ public static IEnumerable FullGroupJoin Func firstKeySelector, Func secondKeySelector, Func, IEnumerable, TResult> resultSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) => MoreEnumerable.FullGroupJoin(first, second, firstKeySelector, secondKeySelector, resultSelector, comparer); } diff --git a/MoreLinq/FullGroupJoin.cs b/MoreLinq/FullGroupJoin.cs index 4a1f3d84d..7ce9b687f 100644 --- a/MoreLinq/FullGroupJoin.cs +++ b/MoreLinq/FullGroupJoin.cs @@ -76,7 +76,7 @@ static partial class MoreEnumerable IEnumerable second, Func firstKeySelector, Func secondKeySelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { return FullGroupJoin(first, second, firstKeySelector, secondKeySelector, ValueTuple.Create, comparer); } @@ -139,7 +139,7 @@ public static IEnumerable FullGroupJoin Func firstKeySelector, Func secondKeySelector, Func, IEnumerable, TResult> resultSelector, - IEqualityComparer comparer) + IEqualityComparer? comparer) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); From cae4bcce7b12e7e4400dd21c803c1850dbdbaca8 Mon Sep 17 00:00:00 2001 From: Leandro Fernandes Date: Wed, 16 Jun 2021 07:52:10 -0300 Subject: [PATCH 091/157] Add test to ensure "Batch" doesn't reuse internal array (#820) --- MoreLinq.Test/BatchTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index bc4bec843..c5c9186e7 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -79,6 +79,22 @@ public void BatchSequenceYieldsListsOfBatches() reader.ReadEnd(); } + [Test] + public void BatchSequencesAreIndependentInstances() + { + var result = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }.Batch(4); + + using var reader = result.Read(); + var first = reader.Read(); + var second = reader.Read(); + var third = reader.Read(); + reader.ReadEnd(); + + first.AssertSequenceEqual(1, 2, 3, 4); + second.AssertSequenceEqual(5, 6, 7, 8); + third.AssertSequenceEqual(9); + } + [Test] public void BatchIsLazy() { From fb9da772ffc7bd2fd01aa364de2def84df6feca2 Mon Sep 17 00:00:00 2001 From: GitHubPang <61439577+GitHubPang@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:15:22 +0800 Subject: [PATCH 092/157] Update/Refresh some links in README (#828) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 03631fdef..b8e64eea5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ MoreLINQ is available for download and installation as [NuGet packages](https://www.nuget.org/packages/morelinq/). Documentation for the stable and beta releases can be found at -[morelinq.github.io](http://morelinq.github.io/). +[morelinq.github.io](https://morelinq.github.io/). ## Usage @@ -54,7 +54,7 @@ extension methods as well as all the regular static methods on [lag]: https://morelinq.github.io/2.0/ref/api/html/Overload_MoreLinq_MoreEnumerable_Lag.htm [lead]: https://morelinq.github.io/2.0/ref/api/html/Overload_MoreLinq_MoreEnumerable_Lead.htm [using-static]: https://docs.microsoft.com/en-us/dotnet/articles/csharp/whats-new/csharp-6#using-static -[netzip]: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.zip--3 +[netzip]: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.zip#System_Linq_Enumerable_Zip__3_System_Collections_Generic_IEnumerable___0__System_Collections_Generic_IEnumerable___1__System_Func___0___1___2__ [zip]: https://morelinq.github.io/1.x/ref/api/html/M_MoreLinq_MoreEnumerable_Zip__3.htm [unfold]: https://morelinq.github.io/2.3/ref/api/html/M_MoreLinq_MoreEnumerable_Unfold__3.htm [random]: https://morelinq.github.io/2.0/ref/api/html/Overload_MoreLinq_MoreEnumerable_Random.htm @@ -83,7 +83,7 @@ locally using any HTTP server of static files, like [mono]: https://www.mono-project.com/ -[dotnet-2.0-sdk-2.1]: https://github.com/dotnet/core/blob/master/release-notes/download-archives/2.1.2-sdk-download.md +[dotnet-2.0-sdk-2.1]: https://github.com/dotnet/core/blob/main/release-notes/download-archives/2.1.2-sdk-download.md [shfb]: https://github.com/EWSoftware/SHFB/releases/tag/v2017.12.30.2 [http-server]: https://www.npmjs.com/package/http-server [t4]: https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates From 890ad3ffa343234863c9e9d6316ea528380bda37 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 3 Jul 2021 18:37:37 +0200 Subject: [PATCH 093/157] Update MyGet API key --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d2b14361a..ed191f84f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ for: - provider: NuGet server: https://www.myget.org/F/morelinq/api/v2/package api_key: - secure: fhGwXyO35FSshRzs5GWmF1LJTrd1sIqmS/jNCSfO2LfOciuYAKiXuFMYZFGiTAl+ + secure: pC316nurUkbqHVmisB9MgK0+n8P2xO+9+fvk9aZ2G7zmr3Vo0hHv05o1w3vwpi0H symbol_server: https://www.myget.org/F/morelinq/symbols/api/v2/package on: branch: deploy From f7e1226acee7a901daccbbc1e8bbcde7d05c8571 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 29 Jul 2022 09:01:40 +0200 Subject: [PATCH 094/157] Revert "Update MyGet API key" This reverts commit 890ad3ffa343234863c9e9d6316ea528380bda37. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index ed191f84f..d2b14361a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ for: - provider: NuGet server: https://www.myget.org/F/morelinq/api/v2/package api_key: - secure: pC316nurUkbqHVmisB9MgK0+n8P2xO+9+fvk9aZ2G7zmr3Vo0hHv05o1w3vwpi0H + secure: fhGwXyO35FSshRzs5GWmF1LJTrd1sIqmS/jNCSfO2LfOciuYAKiXuFMYZFGiTAl+ symbol_server: https://www.myget.org/F/morelinq/symbols/api/v2/package on: branch: deploy From de2796e1e4d98b3da0c59e300fc044c6cda8639b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 15 Oct 2022 19:58:28 +0200 Subject: [PATCH 095/157] Add .NET 6 target while dropping .NET 5 & Core 2.1 (#843) - .NET Core 2.1 reached end-of-life on August 21, 2021 - .NET 5.0 reached end-of-life on May 10, 2022 --- .config/dotnet-tools.json | 2 +- MoreLinq.Test/MoreLinq.Test.csproj | 12 ++++++------ MoreLinq.sln | 4 ++-- MoreLinq/Aggregate.g.tt | 1 - MoreLinq/Cartesian.g.tt | 1 - appveyor.yml | 10 ++++------ .../MoreLinq.ExtensionsGenerator.csproj | 6 +++--- build.cmd | 2 +- build.sh | 2 +- global.json | 2 +- test.cmd | 6 ++---- test.sh | 2 +- 12 files changed, 22 insertions(+), 28 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 0758d4e1c..fb2643e5b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-t4": { - "version": "2.2.0", + "version": "2.3.0", "commands": [ "t4" ] diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 7558169e9..82546061d 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - net5.0;netcoreapp3.1;netcoreapp2.1;net451 + net6.0;netcoreapp3.1;net451 portable MoreLinq.Test Exe @@ -33,15 +33,15 @@ runtime; build; native; contentfiles; analyzers all - - + + - - + + - + diff --git a/MoreLinq.sln b/MoreLinq.sln index bab71edee..ff1b65d6c 100644 --- a/MoreLinq.sln +++ b/MoreLinq.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{835F8FFA-471F-4322-B721-A897F27872FA}" ProjectSection(SolutionItems) = preProject diff --git a/MoreLinq/Aggregate.g.tt b/MoreLinq/Aggregate.g.tt index 50dfdbf19..e9d41b3c5 100644 --- a/MoreLinq/Aggregate.g.tt +++ b/MoreLinq/Aggregate.g.tt @@ -1,7 +1,6 @@ <#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> <#@ assembly name="System.Core" #> -<#@ assembly name="System.Collections" #> <#@ import namespace="System.Globalization" #> <#@ import namespace="System.Linq" #> #region License and Terms diff --git a/MoreLinq/Cartesian.g.tt b/MoreLinq/Cartesian.g.tt index c57605da0..8df601e46 100644 --- a/MoreLinq/Cartesian.g.tt +++ b/MoreLinq/Cartesian.g.tt @@ -1,7 +1,6 @@ <#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> <#@ assembly name="System.Core" #> -<#@ assembly name="System.Collections" #> <#@ import namespace="System.Globalization" #> <#@ import namespace="System.Linq" #> #region License and Terms diff --git a/appveyor.yml b/appveyor.yml index d2b14361a..04b58442d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ version: '{build}' image: -- Visual Studio 2019 +- Visual Studio 2022 - Ubuntu1604 - macos environment: @@ -10,7 +10,7 @@ for: - matrix: only: - - image: Visual Studio 2019 + - image: Visual Studio 2022 environment: IMAGE_NAME: win deploy: @@ -54,10 +54,8 @@ install: - git reset --hard - ps: if ($isWindows) { tools\dotnet-install.ps1 -JSonFile global.json } - ps: if ($isWindows) { tools\dotnet-install.ps1 -Runtime dotnet -Version 3.1.10 -SkipNonVersionedFiles } -- ps: if ($isWindows) { tools\dotnet-install.ps1 -Runtime dotnet -Version 2.1.23 -SkipNonVersionedFiles } - sh: ./tools/dotnet-install.sh --jsonfile global.json - sh: ./tools/dotnet-install.sh --runtime dotnet --version 3.1.10 --skip-non-versioned-files -- sh: ./tools/dotnet-install.sh --runtime dotnet --version 2.1.23 --skip-non-versioned-files - sh: export PATH="~/.dotnet:$PATH" before_build: - dotnet --info @@ -84,13 +82,13 @@ build_script: test_script: - cmd: test.cmd - sh: ./test.sh -- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.net5.0.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) +- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.net6.0.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) - ps: | cd tmp/cover tar -cz -f "../../coverage-report-${IMAGE_NAME}.tar.gz" * - sh: curl -sSL https://codecov.io/bash > codecov - sh: chmod +x codecov -- sh: if [ "$CI_LINUX" = "true" ]; then ./codecov -f ./MoreLinq.Test/coverage.net5.0.opencover.xml; fi +- sh: if [ "$CI_LINUX" = "true" ]; then ./codecov -f ./MoreLinq.Test/coverage.net6.0.opencover.xml; fi artifacts: - path: dist\*.nupkg - path: coverage-report-* diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index e22a4550d..67dfd0664 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -1,11 +1,11 @@  Exe - netcoreapp3.1 + net6.0 false - - + + diff --git a/build.cmd b/build.cmd index 08e0f43bf..18923a015 100644 --- a/build.cmd +++ b/build.cmd @@ -27,7 +27,7 @@ exit /b 2 :codegen echo | set /p=Generating extensions wrappers (%1)... -dotnet run -p bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj -c Release -- %2 %3 %4 %5 %6 %7 %8 %9 > "%temp%\%~nx1" ^ +dotnet run --project bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj -c Release -- %2 %3 %4 %5 %6 %7 %8 %9 > "%temp%\%~nx1" ^ && move "%temp%\%~nx1" "%~dp1" > nul ^ && echo Done. goto :EOF diff --git a/build.sh b/build.sh index 1cedf8ddc..7d188c377 100755 --- a/build.sh +++ b/build.sh @@ -7,7 +7,7 @@ codegen() { dest="$1" printf "Generating extensions wrappers (%s)..." "$1" shift - dotnet run -p bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj -c Release -- "$@" > "$dest" + dotnet run --project bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj -c Release -- "$@" > "$dest" printf "Done.\n" } codegen MoreLinq/Extensions.g.cs -x "[/\\\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq diff --git a/global.json b/global.json index a46dc9b06..18b4598f7 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "5.0.100", + "version": "6.0.400", "rollForward": "latestFeature" } } diff --git a/test.cmd b/test.cmd index 03ed51dc9..1a61a0486 100644 --- a/test.cmd +++ b/test.cmd @@ -7,10 +7,8 @@ goto :EOF :main setlocal call build ^ - && call :test net5.0 Debug ^ - && call :test net5.0 Debug ^ - && call :test netcoreapp2.1 Debug ^ - && call :test netcoreapp2.1 Release ^ + && call :test net6.0 Debug ^ + && call :test net6.0 Release ^ && call :test netcoreapp3.1 Debug ^ && call :test netcoreapp3.1 Release ^ && call :test net451 Debug ^ diff --git a/test.sh b/test.sh index 9108be273..51e42ed74 100755 --- a/test.sh +++ b/test.sh @@ -7,7 +7,7 @@ if [[ -z "$1" ]]; then else configs="$1" fi -for f in netcoreapp2.1 netcoreapp3.1 net5.0; do +for f in netcoreapp3.1 net6.0; do for c in $configs; do if [[ "$c" == "Debug" ]]; then coverage_args="-p:CollectCoverage=true From d26916282c0b900b56f67011f5c58434412ddf1d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 16 Oct 2022 12:23:04 +0200 Subject: [PATCH 096/157] Refresh section on building in read-me doc --- README.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b8e64eea5..420dc324d 100644 --- a/README.md +++ b/README.md @@ -63,13 +63,8 @@ extension methods as well as all the regular static methods on ## Building -To build MoreLINQ from sources, you will need: - -- [.NET Core 2.0 with SDK 2.1][dotnet-2.0-sdk-2.1] -- [Mono][mono] 5.0 if building on other platforms than Windows - -Then run either `build.cmd` if building on Windows or `build.sh` if -building on macOS or a Linux distribution supported by .NET Core. +Run either `build.cmd` if building on Windows or `build.sh` if building on macOS +or a [Linux distribution supported by .NET][dotnet-linux]. Some code in the project is generated using [T4][t4] templates. To regenerate the code from modified templates, run `MoreLinq\tt.cmd` (Windows) or @@ -81,9 +76,7 @@ generates the documentation in the `docs/api` directory. It can be browsed locally using any HTTP server of static files, like [http-server][http-server]. - -[mono]: https://www.mono-project.com/ -[dotnet-2.0-sdk-2.1]: https://github.com/dotnet/core/blob/main/release-notes/download-archives/2.1.2-sdk-download.md +[dotnet-linux]: https://learn.microsoft.com/en-us/dotnet/core/install/linux [shfb]: https://github.com/EWSoftware/SHFB/releases/tag/v2017.12.30.2 [http-server]: https://www.npmjs.com/package/http-server [t4]: https://docs.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates From e7009802de008b43b7e566f1786cb1b1a23d9145 Mon Sep 17 00:00:00 2001 From: MateoMiller <64360473+MateoMiller@users.noreply.github.com> Date: Sun, 16 Oct 2022 20:25:04 +0500 Subject: [PATCH 097/157] Remove redundant null check in "Batch" (#846) --- MoreLinq/Batch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index 4621f46cf..4c3102dd2 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -144,7 +144,7 @@ IEnumerable Batch(int size) } // Return the last bucket with all remaining elements - if (bucket != null && count > 0) + if (count > 0) { Array.Resize(ref bucket, count); yield return resultSelector(bucket); From 9ecd1dc0093757d62d5c55277dbe0afc81cde4da Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 16 Oct 2022 18:28:04 +0200 Subject: [PATCH 098/157] Fix code coverage with new collector (#845) --- .gitignore | 1 + MoreLinq.Test/MoreLinq.Test.csproj | 5 ++-- MoreLinq.Test/coverlet.runsettings | 12 ++++++++++ appveyor.yml | 6 ++--- test.cmd | 38 ++++++++++++++++++++++++------ test.sh | 18 +++++++------- 6 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 MoreLinq.Test/coverlet.runsettings diff --git a/.gitignore b/.gitignore index f9828f54d..68976a434 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.opencover.xml +**/TestResults/ ### VisualStudio ### ## Ignore Visual Studio temporary files, build results, and diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 82546061d..3a90dde77 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -20,7 +20,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -47,9 +47,10 @@ + + - diff --git a/MoreLinq.Test/coverlet.runsettings b/MoreLinq.Test/coverlet.runsettings new file mode 100644 index 000000000..1b88194b5 --- /dev/null +++ b/MoreLinq.Test/coverlet.runsettings @@ -0,0 +1,12 @@ + + + + + + opencover + [NUnit*]*,[MoreLinq]MoreLinq.Extensions.*,[MoreLinq]MoreLinq.Experimental.* + + + + + diff --git a/appveyor.yml b/appveyor.yml index 04b58442d..80e4f4be4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -82,13 +82,13 @@ build_script: test_script: - cmd: test.cmd - sh: ./test.sh -- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.net6.0.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) +- ps: dotnet reportgenerator -reports:MoreLinq.Test/TestResults/coverage-*.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) - ps: | cd tmp/cover - tar -cz -f "../../coverage-report-${IMAGE_NAME}.tar.gz" * + tar -cz -f "../../coverage-report-${env:IMAGE_NAME}.tar.gz" * - sh: curl -sSL https://codecov.io/bash > codecov - sh: chmod +x codecov -- sh: if [ "$CI_LINUX" = "true" ]; then ./codecov -f ./MoreLinq.Test/coverage.net6.0.opencover.xml; fi +- sh: if [ "$CI_LINUX" = "true" ]; then ./codecov; fi artifacts: - path: dist\*.nupkg - path: coverage-report-* diff --git a/test.cmd b/test.cmd index 1a61a0486..93fb641a6 100644 --- a/test.cmd +++ b/test.cmd @@ -7,23 +7,47 @@ goto :EOF :main setlocal call build ^ + && call :clean ^ && call :test net6.0 Debug ^ && call :test net6.0 Release ^ && call :test netcoreapp3.1 Debug ^ && call :test netcoreapp3.1 Release ^ && call :test net451 Debug ^ - && call :test net451 Release + && call :test net451 Release ^ + && call :report-cover +goto :EOF + +:clean +setlocal +cd MoreLinq.Test +if exist TestResults rd /s /q TestResults || exit /b 1 +if exist TestResult.xml del TestResult.xml || exit /b 1 goto :EOF :test setlocal +cd MoreLinq.Test echo Testing %1 (%2)... -if %2==Debug set COVERAGE_ARGS=-p:CollectCoverage=true ^ - -p:CoverletOutputFormat=opencover ^ - -p:Exclude=\"[NUnit*]*,[MoreLinq]MoreLinq.Extensions.*,[MoreLinq]MoreLinq.Experimental.*\" if %1==net451 ( - MoreLinq.Test\bin\%2\net451\MoreLinq.Test.exe -) else ( - dotnet test --no-build MoreLinq.Test -f %1 -c %2 %COVERAGE_ARGS% + bin\%2\net451\MoreLinq.Test.exe + exit /b %ERRORLEVEL% +) +dotnet test --no-build -f %1 -c %2 --settings coverlet.runsettings || exit /b 1 +cd TestResults +set TEST_RESULTS_DIR= +for /f %%d in ('dir /b /od /ad') do if not defined TEST_RESULTS_DIR set TEST_RESULTS_DIR=%%~d +if not defined TEST_RESULTS_DIR ( + echo>&2 Test coverage XML not found! + exit /b 1 ) +copy "%TEST_RESULTS_DIR%\coverage.opencover.xml" coverage-%1-%2.opencover.xml > nul +goto :EOF + +:report-cover +setlocal +cd MoreLinq.Test\TestResults +dotnet reportgenerator -reports:coverage-*.opencover.xml ^ + -reporttypes:Html;TextSummary ^ + -targetdir:reports ^ + && type reports\Summary.txt goto :EOF diff --git a/test.sh b/test.sh index 51e42ed74..c581713b6 100755 --- a/test.sh +++ b/test.sh @@ -2,6 +2,9 @@ set -e cd "$(dirname "$0")" ./build.sh $c +if [[ -d "MoreLinq.Test/TestResults" ]]; then + rm -rf MoreLinq.Test/TestResults +fi if [[ -z "$1" ]]; then configs="Debug Release" else @@ -9,16 +12,15 @@ else fi for f in netcoreapp3.1 net6.0; do for c in $configs; do - if [[ "$c" == "Debug" ]]; then - coverage_args="-p:CollectCoverage=true - -p:CoverletOutputFormat=opencover - -p:Exclude=\"[NUnit*]*,[MoreLinq]MoreLinq.Extensions.*,[MoreLinq]MoreLinq.Experimental.*\"" - else - unset coverage_args - fi - dotnet test --no-build -c $c -f $f MoreLinq.Test $coverage_args + dotnet test --no-build -c $c -f $f --settings MoreLinq.Test/coverlet.runsettings MoreLinq.Test + TEST_RESULTS_DIR="$(ls -dc MoreLinq.Test/TestResults/* | head -1)" + cp "$TEST_RESULTS_DIR/coverage.opencover.xml" "MoreLinq.Test/TestResults/coverage-$f-$c.opencover.xml" done done +dotnet reportgenerator -reports:MoreLinq.Test/TestResults/coverage-*.opencover.xml \ + -reporttypes:Html\;TextSummary \ + -targetdir:MoreLinq.Test/TestResults/reports +cat MoreLinq.Test/TestResults/reports/Summary.txt if [[ -z `which mono 2>/dev/null` ]]; then echo>&2 NOTE! Mono does not appear to be installed so unit tests echo>&2 against the Mono runtime will be skipped. From bed889d7ff2d9d7e46499da6df19741f6cc5b0be Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 17 Oct 2022 08:31:33 +0200 Subject: [PATCH 099/157] Update referenced packages to latest versions (#844) --- MoreLinq.Test/MoreLinq.Test.csproj | 4 ++-- MoreLinq/MoreLinq.csproj | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 3a90dde77..8de050a82 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -25,11 +25,11 @@ all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers all diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 5a1689b90..252535bf1 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -138,6 +138,8 @@ ..\dist true true + + $(NoWarn);NU5118 true false false @@ -148,11 +150,11 @@ - + runtime; build; native; contentfiles; analyzers all - + @@ -245,7 +247,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 57e1d783fab18183704ee4103b5d73e47d4f230e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 17 Oct 2022 22:47:25 +0200 Subject: [PATCH 100/157] Fix "Flatten" signature parts to be null-oblivious This is a squashed merge of PR #847 that adds to #803. --- MoreLinq/Extensions.g.cs | 34 +++++++++++++++++++++++++++++----- MoreLinq/Flatten.cs | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index bfae169ac..e1245aa54 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -1926,7 +1926,13 @@ public static partial class FlattenExtension /// /// is null. - public static IEnumerable Flatten(this IEnumerable source) => MoreEnumerable.Flatten(source); + public static IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*.............................*/ object +#nullable restore +/*...................................*/ > + Flatten(this IEnumerable source) => MoreEnumerable. Flatten(source); /// /// Flattens a sequence containing arbitrarily-nested sequences. An @@ -1950,8 +1956,14 @@ public static partial class FlattenExtension /// /// is null. - public static IEnumerable Flatten(this IEnumerable source, Func predicate) - => MoreEnumerable.Flatten(source, predicate); + public static IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*.............................*/ object +#nullable restore +/*...................................*/ > + Flatten(this IEnumerable source, Func predicate) + => MoreEnumerable. Flatten(source, predicate); /// /// Flattens a sequence containing arbitrarily-nested sequences. An @@ -1975,8 +1987,20 @@ public static IEnumerable Flatten(this IEnumerable source, Func /// is null. - public static IEnumerable Flatten(this IEnumerable source, Func selector) - => MoreEnumerable.Flatten(source, selector); + public static IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*.............................*/ object +#nullable restore +/*...................................*/ > + Flatten(this IEnumerable source, + Func< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*....................*/ object, +#nullable restore +/*....................*/ IEnumerable?> selector) + => MoreEnumerable. Flatten(source, selector); } diff --git a/MoreLinq/Flatten.cs b/MoreLinq/Flatten.cs index 14fd517ea..b1845ea43 100644 --- a/MoreLinq/Flatten.cs +++ b/MoreLinq/Flatten.cs @@ -34,7 +34,13 @@ static partial class MoreEnumerable /// /// is null. - public static IEnumerable Flatten(this IEnumerable source) => + public static IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*.............................*/ object +#nullable restore +/*...................................*/ > + Flatten(this IEnumerable source) => Flatten(source, obj => !(obj is string)); /// @@ -59,7 +65,13 @@ public static IEnumerable Flatten(this IEnumerable source) => /// /// is null. - public static IEnumerable Flatten(this IEnumerable source, Func predicate) + public static IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*.............................*/ object +#nullable restore +/*...................................*/ > + Flatten(this IEnumerable source, Func predicate) { if (predicate == null) throw new ArgumentNullException(nameof(predicate)); @@ -88,12 +100,31 @@ public static IEnumerable Flatten(this IEnumerable source, Func /// is null. - public static IEnumerable Flatten(this IEnumerable source, Func selector) + public static IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*.............................*/ object +#nullable restore +/*...................................*/ > + Flatten(this IEnumerable source, + Func< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*....................*/ object, +#nullable restore +/*....................*/ IEnumerable?> selector) { if (source == null) throw new ArgumentNullException(nameof(source)); if (selector == null) throw new ArgumentNullException(nameof(selector)); - return _(); IEnumerable _() + return _(); + + IEnumerable< +// Just like "IEnumerable.Current" is null-oblivious, so is this: +#nullable disable +/*...................*/ object +#nullable restore +/*.........................*/ > _() { var e = source.GetEnumerator(); var stack = new Stack(); From a573c061a809a664ebca9649f5f1c1748ba6736b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 20 Oct 2022 19:28:04 +0200 Subject: [PATCH 101/157] Add missing remarks tags on "Batch" overload (#849) --- MoreLinq/Batch.cs | 2 ++ MoreLinq/Extensions.g.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index 4c3102dd2..34babc628 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -62,6 +62,7 @@ public static IEnumerable> Batch(this IEnumerable< /// Size of buckets. /// The projection to apply to each bucket. /// A sequence of projections on equally sized buckets containing elements of the source collection. + /// /// /// This operator uses deferred execution and streams its results /// (buckets are streamed but their content buffered). @@ -77,6 +78,7 @@ public static IEnumerable> Batch(this IEnumerable< /// hoping for a single bucket, then it can lead to memory exhaustion /// (). /// + /// public static IEnumerable Batch(this IEnumerable source, int size, Func, TResult> resultSelector) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index e1245aa54..00add5531 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -690,6 +690,7 @@ public static IEnumerable> Batch(this IEnumerable< /// Size of buckets. /// The projection to apply to each bucket. /// A sequence of projections on equally sized buckets containing elements of the source collection. + /// /// /// This operator uses deferred execution and streams its results /// (buckets are streamed but their content buffered). @@ -705,6 +706,7 @@ public static IEnumerable> Batch(this IEnumerable< /// hoping for a single bucket, then it can lead to memory exhaustion /// (). /// + /// public static IEnumerable Batch(this IEnumerable source, int size, Func, TResult> resultSelector) From f0dab598ecd61e83ca3407bf131c83122f880fa7 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 24 Oct 2022 19:50:04 +0200 Subject: [PATCH 102/157] Constrain "ToDictionary" key type param to "notnull" This is a squashed merge of PR #852 that adds to #803. --- MoreLinq/Extensions.g.cs | 8 ++++++-- MoreLinq/ToDictionary.cs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 00add5531..a6910c4a5 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -6364,7 +6364,8 @@ public static partial class ToDictionaryExtension /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) => MoreEnumerable.ToDictionary(source); + public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) + where TKey : notnull => MoreEnumerable.ToDictionary(source); /// /// Creates a from a sequence of /// elements. @@ -6377,7 +6378,8 @@ public static partial class ToDictionaryExtension /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable> source) => MoreEnumerable.ToDictionary(source); + public static Dictionary ToDictionary(this IEnumerable> source) + where TKey : notnull => MoreEnumerable.ToDictionary(source); /// /// Creates a from a sequence of @@ -6395,6 +6397,7 @@ public static partial class ToDictionaryExtension public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, IEqualityComparer? comparer) + where TKey : notnull => MoreEnumerable.ToDictionary(source, comparer); /// @@ -6413,6 +6416,7 @@ public static Dictionary ToDictionary(this IEnumerab public static Dictionary ToDictionary(this IEnumerable> source, IEqualityComparer? comparer) + where TKey : notnull => MoreEnumerable.ToDictionary(source, comparer); } diff --git a/MoreLinq/ToDictionary.cs b/MoreLinq/ToDictionary.cs index 925dbea7f..47db8bf76 100644 --- a/MoreLinq/ToDictionary.cs +++ b/MoreLinq/ToDictionary.cs @@ -35,7 +35,8 @@ static partial class MoreEnumerable /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable> source) => + public static Dictionary ToDictionary(this IEnumerable> source) + where TKey : notnull => source.ToDictionary(null); /// @@ -54,6 +55,7 @@ public static Dictionary ToDictionary(this IEnumerab public static Dictionary ToDictionary(this IEnumerable> source, IEqualityComparer? comparer) + where TKey : notnull { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToDictionary(e => e.Key, e => e.Value, comparer); @@ -72,7 +74,8 @@ public static Dictionary ToDictionary(this IEnumerab /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) => + public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) + where TKey : notnull => source.ToDictionary(null); /// @@ -91,6 +94,7 @@ public static Dictionary ToDictionary(this IEnumerab public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, IEqualityComparer? comparer) + where TKey : notnull { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToDictionary(e => e.Key, e => e.Value, comparer); From 212be62eacd0466b9d6ba2ebb82fc1f5bfd54ea6 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 24 Oct 2022 21:12:00 +0200 Subject: [PATCH 103/157] Allow use of C# 10 (#853) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index fcf9f7d01..33d490cdb 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 9 + 10 true From af98ba4a77932f7697f32b300bcfc7c4c31a941b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 26 Oct 2022 22:17:44 +0200 Subject: [PATCH 104/157] Remove look-up optimizations in "CountBy"/"ScanBy" (#855) --- MoreLinq/CountBy.cs | 11 +---------- MoreLinq/ScanBy.cs | 16 +--------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/MoreLinq/CountBy.cs b/MoreLinq/CountBy.cs index e671fee63..282ace5d4 100644 --- a/MoreLinq/CountBy.cs +++ b/MoreLinq/CountBy.cs @@ -82,19 +82,12 @@ void Loop(IEqualityComparer cmp) keys = new List(); counts = new List(); - (bool, TKey) prevKey = default; - var index = 0; foreach (var item in source) { var key = keySelector(item); - if (// key same as the previous? then re-use the index - prevKey is (true, {} pk) - && cmp.GetHashCode(pk) == cmp.GetHashCode(key) - && cmp.Equals(pk, key) - // otherwise try & find index of the key - || dic.TryGetValue(key, out index)) + if (dic.TryGetValue(key, out var index)) { counts[index]++; } @@ -105,8 +98,6 @@ prevKey is (true, {} pk) keys.Add(key); counts.Add(1); } - - prevKey = (true, key); } } } diff --git a/MoreLinq/ScanBy.cs b/MoreLinq/ScanBy.cs index b91a02dc4..3ca138ced 100644 --- a/MoreLinq/ScanBy.cs +++ b/MoreLinq/ScanBy.cs @@ -91,27 +91,13 @@ IEnumerable> _(IEqualityComparer comparer) { var stateByKey = new Collections.Dictionary(comparer); - (bool, TKey, TState) prev = default; - foreach (var item in source) { var key = keySelector(item); - - var state = // key same as the previous? then re-use the state - prev is (true, {} pk, {} ps) - && comparer.GetHashCode(pk) == comparer.GetHashCode(key) - && comparer.Equals(pk, key) ? ps - : // otherwise try & find state of the key - stateByKey.TryGetValue(key, out var ns) ? ns - : seedSelector(key); - + var state = stateByKey.TryGetValue(key, out var s) ? s : seedSelector(key); state = accumulator(state, key, item); - stateByKey[key] = state; - yield return new KeyValuePair(key, state); - - prev = (true, key, state); } } } From ac88c7ffdb943c88595bd47d36cf2b07ea941ae6 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 30 Oct 2022 12:16:30 +0100 Subject: [PATCH 105/157] Replace Nullable with PolySharp (#857) --- MoreLinq/MoreLinq.csproj | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 252535bf1..e08281ccb 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -138,8 +138,6 @@ ..\dist true true - - $(NoWarn);NU5118 true false false @@ -155,6 +153,7 @@ all + @@ -246,13 +245,6 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - From c16cf64856fe0ed43947c9d34594778d8e29b8fe Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 30 Oct 2022 23:06:42 +0100 Subject: [PATCH 106/157] Fix code coverage using latest coverlet collector (#858) --- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- MoreLinq.Test/coverlet.runsettings | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 8de050a82..93065e422 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -20,7 +20,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/MoreLinq.Test/coverlet.runsettings b/MoreLinq.Test/coverlet.runsettings index 1b88194b5..bbe3b949d 100644 --- a/MoreLinq.Test/coverlet.runsettings +++ b/MoreLinq.Test/coverlet.runsettings @@ -5,6 +5,7 @@ opencover [NUnit*]*,[MoreLinq]MoreLinq.Extensions.*,[MoreLinq]MoreLinq.Experimental.* + **/System.*.g.cs From d684bf868961261bfd3943861460038864a13ab2 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 3 Nov 2022 22:11:15 +0100 Subject: [PATCH 107/157] Adjust library for .NET 6 target (#860) --- MoreLinq.Test/CountByTest.cs | 12 ++++++++ MoreLinq.Test/ToDataTableTest.cs | 8 ++++++ MoreLinq/Collections/Dictionary.cs | 44 ++++++++++++------------------ MoreLinq/Experimental/Await.cs | 1 + MoreLinq/Extensions.g.cs | 16 +++++------ MoreLinq/MoreLinq.csproj | 6 ++-- MoreLinq/PartialSort.cs | 16 +++++------ MoreLinq/Rank.cs | 12 ++++---- MoreLinq/ReverseComparer.cs | 7 ++++- MoreLinq/ToDataTable.cs | 5 ++-- MoreLinq/Trace.cs | 7 ++--- 11 files changed, 75 insertions(+), 59 deletions(-) diff --git a/MoreLinq.Test/CountByTest.cs b/MoreLinq.Test/CountByTest.cs index 26158260d..ea0e7e8a1 100644 --- a/MoreLinq.Test/CountByTest.cs +++ b/MoreLinq.Test/CountByTest.cs @@ -103,5 +103,17 @@ public void CountByWithSomeNullKeys() KeyValuePair.Create("bar", 2), KeyValuePair.Create("baz", 2)); } + + [Test] + public void CountByWithSomeNullKeysAndEqualityComparer() + { + var result = new[] { "a", "B", null, "c", "A", null, "b", "A" }.CountBy(c => c, StringComparer.OrdinalIgnoreCase); + + result.AssertSequenceEqual( + KeyValuePair.Create("a", 3), + KeyValuePair.Create("B", 2), + KeyValuePair.Create((string)null, 2), + KeyValuePair.Create("c", 1)); + } } } diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index cfc95a224..6281c01f1 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -22,6 +22,7 @@ namespace MoreLinq.Test using System.Collections.Generic; using System.Data; using System.Linq.Expressions; + using System.Text.RegularExpressions; using NUnit.Framework; [TestFixture] @@ -115,6 +116,13 @@ public void ToDataTableMemberExpressionIndexer() _testObjects.ToDataTable(t => t[0])); } + [Test] + public void ToDataTableMemberExpressionStatic() + { + AssertThrowsArgument.Exception("lambda", () => + _ = _testObjects.ToDataTable(_ => DateTime.Now)); + } + [Test] public void ToDataTableSchemaInDeclarationOrder() { diff --git a/MoreLinq/Collections/Dictionary.cs b/MoreLinq/Collections/Dictionary.cs index 6366554ab..a325768d8 100644 --- a/MoreLinq/Collections/Dictionary.cs +++ b/MoreLinq/Collections/Dictionary.cs @@ -17,55 +17,45 @@ namespace MoreLinq.Collections { + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; /// /// A minimal wrapper that - /// allows null keys when is a - /// reference type. + /// allows a null key. /// // Add members if and when needed to keep coverage. sealed class Dictionary { - readonly System.Collections.Generic.Dictionary _dict; - (bool, TValue) _null; + readonly System.Collections.Generic.Dictionary, TValue> _dict; public Dictionary(IEqualityComparer comparer) { - _dict = new System.Collections.Generic.Dictionary(comparer); - _null = default; + var keyComparer = ReferenceEquals(comparer, EqualityComparer.Default) + ? null + : new ValueTupleItemComparer(comparer); + _dict = new System.Collections.Generic.Dictionary, TValue>(keyComparer); } public TValue this[TKey key] { - set - { - if (key is null) - _null = (true, value); - else - _dict[key] = value; - } + get => _dict[ValueTuple.Create(key)]; + set => _dict[ValueTuple.Create(key)] = value; } - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => + _dict.TryGetValue(ValueTuple.Create(key), out value); + + sealed class ValueTupleItemComparer : IEqualityComparer> { - if (key is null) - { - switch (_null) - { - case (true, var v): - value = v; - return true; - case (false, _): - value = default; - return false; - } - } + readonly IEqualityComparer _comparer; - return _dict.TryGetValue(key, out value); + public ValueTupleItemComparer(IEqualityComparer comparer) => _comparer = comparer; + public bool Equals(ValueTuple x, ValueTuple y) => _comparer.Equals(x.Item1, y.Item1); + public int GetHashCode(ValueTuple obj) => obj.Item1 is { } some ? _comparer.GetHashCode(some) : 0; } } } diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index 018818f02..e8fb18074 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -528,6 +528,7 @@ void PostNotice(Notice notice, catch (OperationCanceledException e) when (e.CancellationToken == consumerCancellationTokenSource.Token) { var (error1, error2) = lastCriticalErrors; + Debug.Assert(error1 is not null); throw new Exception("One or more critical errors have occurred.", error2 != null ? new AggregateException(error1, error2) : new AggregateException(error1)); diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index a6910c4a5..af8837030 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3963,7 +3963,7 @@ public static partial class PartialSortExtension { /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// /// Type of elements in the sequence. @@ -3980,7 +3980,7 @@ public static IEnumerable PartialSort(this IEnumerable source, int coun /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// An additional parameter specifies the direction of the sort /// @@ -4000,7 +4000,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. An additional parameter specifies how the /// elements compare to each other. /// @@ -4020,7 +4020,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. @@ -4050,7 +4050,7 @@ public static partial class PartialSortByExtension /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// /// Type of elements in the sequence. /// Type of keys. @@ -4070,7 +4070,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies the direction of the sort /// /// Type of elements in the sequence. @@ -4092,7 +4092,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies how the keys compare to each other. /// /// Type of elements in the sequence. @@ -4115,7 +4115,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. /// diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index e08281ccb..3f5a89681 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -119,7 +119,7 @@ en-US 3.3.2 MoreLINQ Developers. - net451;netstandard1.0;netstandard2.0 + net451;netstandard1.0;netstandard2.0;net6.0 enable @@ -188,12 +188,12 @@ - + $(DefineConstants);MORELINQ - $(DefineConstants);MORELINQ;NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC + $(DefineConstants);NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC diff --git a/MoreLinq/PartialSort.cs b/MoreLinq/PartialSort.cs index 60076133a..e8010510f 100644 --- a/MoreLinq/PartialSort.cs +++ b/MoreLinq/PartialSort.cs @@ -25,7 +25,7 @@ static partial class MoreEnumerable { /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// /// Type of elements in the sequence. @@ -44,7 +44,7 @@ public static IEnumerable PartialSort(this IEnumerable source, int coun /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// An additional parameter specifies the direction of the sort /// @@ -66,7 +66,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. An additional parameter specifies how the /// elements compare to each other. /// @@ -89,7 +89,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. @@ -116,7 +116,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// /// Type of elements in the sequence. /// Type of keys. @@ -138,7 +138,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies the direction of the sort /// /// Type of elements in the sequence. @@ -162,7 +162,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies how the keys compare to each other. /// /// Type of elements in the sequence. @@ -189,7 +189,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. /// diff --git a/MoreLinq/Rank.cs b/MoreLinq/Rank.cs index 82b2d6076..5dff3d13c 100644 --- a/MoreLinq/Rank.cs +++ b/MoreLinq/Rank.cs @@ -83,11 +83,13 @@ IEnumerable _(IComparer comparer) { source = source.ToArray(); // avoid enumerating source twice - var rankDictionary = source.Distinct() - .OrderByDescending(keySelector, comparer) - .Index(1) - .ToDictionary(item => item.Value, - item => item.Key); + var rankDictionary = new Collections.Dictionary(EqualityComparer.Default); + var i = 1; + foreach (var item in source.Distinct() + .OrderByDescending(keySelector, comparer)) + { + rankDictionary[item] = i++; + } // The following loop should not be be converted to a query to // keep this RankBy lazy. diff --git a/MoreLinq/ReverseComparer.cs b/MoreLinq/ReverseComparer.cs index bb46d3da5..00086383d 100644 --- a/MoreLinq/ReverseComparer.cs +++ b/MoreLinq/ReverseComparer.cs @@ -28,7 +28,12 @@ public ReverseComparer(IComparer? underlying) _underlying = underlying ?? Comparer.Default; } - public int Compare(T x, T y) + public int Compare +#if NETCOREAPP3_1_OR_GREATER + (T? x, T? y) +#else + (T x, T y) +#endif { var result = _underlying.Compare(x, y); return result < 0 ? 1 : result > 0 ? -1 : 0; diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index dfbf0eca2..bf309642e 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -169,9 +169,8 @@ MemberInfo GetAccessedMember(LambdaExpression lambda) // Check if the member expression is valid and is a "first level" // member access e.g. not a.b.c - return body is MemberExpression memberExpression - && memberExpression.Expression.NodeType == ExpressionType.Parameter - ? memberExpression.Member + return body is MemberExpression { Expression.NodeType: ExpressionType.Parameter, Member: var member } + ? member : throw new ArgumentException($"Illegal expression: {lambda}", nameof(lambda)); } } diff --git a/MoreLinq/Trace.cs b/MoreLinq/Trace.cs index ff9438a3f..768f9b0f1 100644 --- a/MoreLinq/Trace.cs +++ b/MoreLinq/Trace.cs @@ -62,10 +62,9 @@ public static IEnumerable Trace(this IEnumerable sour { if (source == null) throw new ArgumentNullException(nameof(source)); - return TraceImpl(source, - string.IsNullOrEmpty(format) - ? (Func) (x => x == null ? string.Empty : x.ToString()) - : (x => string.Format(format, x))); + return TraceImpl(source, string.IsNullOrEmpty(format) + ? x => x?.ToString() ?? string.Empty + : x => string.Format(format, x)); } /// From bafe64da67fca78d2e00d8deb9391c8d0b8bc1a8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 18:49:50 +0100 Subject: [PATCH 108/157] Use end index "[^1]" (#862) --- MoreLinq/AggregateRight.cs | 2 +- MoreLinq/ScanRight.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/MoreLinq/AggregateRight.cs b/MoreLinq/AggregateRight.cs index 162b0e412..0b07eccb1 100644 --- a/MoreLinq/AggregateRight.cs +++ b/MoreLinq/AggregateRight.cs @@ -52,7 +52,7 @@ public static TSource AggregateRight(this IEnumerable source, if (list.Count == 0) throw new InvalidOperationException("Sequence contains no elements."); - return AggregateRightImpl(list, list[list.Count - 1], func, list.Count - 1); + return AggregateRightImpl(list, list[^1], func, list.Count - 1); } /// diff --git a/MoreLinq/ScanRight.cs b/MoreLinq/ScanRight.cs index 131aced3c..41ba6d270 100644 --- a/MoreLinq/ScanRight.cs +++ b/MoreLinq/ScanRight.cs @@ -50,10 +50,7 @@ public static IEnumerable ScanRight(this IEnumerable if (source == null) throw new ArgumentNullException(nameof(source)); if (func == null) throw new ArgumentNullException(nameof(func)); - return ScanRightImpl(source, func, - list => list.Count > 0 - ? (list[list.Count - 1], list.Count - 1) - : ((TSource, int)?) null); + return ScanRightImpl(source, func, list => list.Count > 0 ? (list[^1], list.Count - 1) : null); } /// From 255b69b4b3d4c2f2014579107e036a828d2cf257 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 19:07:36 +0100 Subject: [PATCH 109/157] Use string interpolation (#863) --- MoreLinq.Test/AggregateRightTest.cs | 8 ++++---- MoreLinq.Test/ScanRightTest.cs | 4 ++-- MoreLinq/AggregateRight.cs | 6 +++--- MoreLinq/Extensions.g.cs | 8 ++++---- MoreLinq/ScanRight.cs | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/MoreLinq.Test/AggregateRightTest.cs b/MoreLinq.Test/AggregateRightTest.cs index 0d3aa62af..55a8e091d 100644 --- a/MoreLinq.Test/AggregateRightTest.cs +++ b/MoreLinq.Test/AggregateRightTest.cs @@ -49,7 +49,7 @@ public void AggregateRight(SourceKind sourceKind) { var enumerable = Enumerable.Range(1, 5).Select(x => x.ToString()).ToSourceKind(sourceKind); - var result = enumerable.AggregateRight((a, b) => string.Format("({0}+{1})", a, b)); + var result = enumerable.AggregateRight((a, b) => $"({a}+{b})"); Assert.That(result, Is.EqualTo("(1+(2+(3+(4+5))))")); } @@ -78,7 +78,7 @@ public void AggregateRightSeedFuncIsNotInvokedOnEmptySequence() public void AggregateRightSeed() { var result = Enumerable.Range(1, 4) - .AggregateRight("5", (a, b) => string.Format("({0}+{1})", a, b)); + .AggregateRight("5", (a, b) => $"({a}+{b})"); Assert.That(result, Is.EqualTo("(1+(2+(3+(4+5))))")); } @@ -90,14 +90,14 @@ public void AggregateRightSeed() [TestCase(true)] public void AggregateRightResultorWithEmptySequence(object defaultValue) { - Assert.That(new int[0].AggregateRight(defaultValue, (a, b) => b, a => a == defaultValue), Is.EqualTo(true)); + Assert.That(new int[0].AggregateRight(defaultValue, (_, b) => b, a => a == defaultValue), Is.EqualTo(true)); } [Test] public void AggregateRightResultor() { var result = Enumerable.Range(1, 4) - .AggregateRight("5", (a, b) => string.Format("({0}+{1})", a, b), a => a.Length); + .AggregateRight("5", (a, b) => $"({a}+{b})", a => a.Length); Assert.That(result, Is.EqualTo("(1+(2+(3+(4+5))))".Length)); } diff --git a/MoreLinq.Test/ScanRightTest.cs b/MoreLinq.Test/ScanRightTest.cs index 3cb334612..948dd8c70 100644 --- a/MoreLinq.Test/ScanRightTest.cs +++ b/MoreLinq.Test/ScanRightTest.cs @@ -64,7 +64,7 @@ public void ScanRight(SourceKind sourceKind) var result = Enumerable.Range(1, 5) .Select(x => x.ToString()) .ToSourceKind(sourceKind) - .ScanRight((a, b) => string.Format("({0}+{1})", a, b)); + .ScanRight((a, b) => $"({a}+{b})"); var expectations = new[] { "(1+(2+(3+(4+5))))", "(2+(3+(4+5)))", "(3+(4+5))", "(4+5)", "5" }; @@ -101,7 +101,7 @@ public void ScanRightSeedFuncIsNotInvokedOnEmptySequence() public void ScanRightSeed() { var result = Enumerable.Range(1, 4) - .ScanRight("5", (a, b) => string.Format("({0}+{1})", a, b)); + .ScanRight("5", (a, b) => $"({a}+{b})"); var expectations = new[] { "(1+(2+(3+(4+5))))", "(2+(3+(4+5)))", "(3+(4+5))", "(4+5)", "5" }; diff --git a/MoreLinq/AggregateRight.cs b/MoreLinq/AggregateRight.cs index 0b07eccb1..b3004919c 100644 --- a/MoreLinq/AggregateRight.cs +++ b/MoreLinq/AggregateRight.cs @@ -34,7 +34,7 @@ static partial class MoreEnumerable /// The final accumulator value. /// /// i.ToString()).AggregateRight((a, b) => string.Format("({0}/{1})", a, b)); + /// string result = Enumerable.Range(1, 5).Select(i => i.ToString()).AggregateRight((a, b) => $"({a}/{b})"); /// ]]> /// The result variable will contain "(1/(2/(3/(4/5))))". /// @@ -70,7 +70,7 @@ public static TSource AggregateRight(this IEnumerable source, /// /// string.Format("({0}/{1})", a, b)); + /// string result = numbers.AggregateRight("6", (a, b) => $"({a}/{b})"); /// ]]> /// The result variable will contain "(1/(2/(3/(4/(5/6)))))". /// @@ -106,7 +106,7 @@ public static TAccumulate AggregateRight(this IEnumerable< /// /// string.Format("({0}/{1})", a, b), str => str.Length); + /// int result = numbers.AggregateRight("6", (a, b) => $"({a}/{b})", str => str.Length); /// ]]> /// The result variable will contain 21. /// diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index af8837030..bead709d2 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -360,7 +360,7 @@ public static partial class AggregateRightExtension /// The final accumulator value. /// /// i.ToString()).AggregateRight((a, b) => string.Format("({0}/{1})", a, b)); + /// string result = Enumerable.Range(1, 5).Select(i => i.ToString()).AggregateRight((a, b) => $"({a}/{b})"); /// ]]> /// The result variable will contain "(1/(2/(3/(4/5))))". /// @@ -386,7 +386,7 @@ public static TSource AggregateRight(this IEnumerable source, /// /// string.Format("({0}/{1})", a, b)); + /// string result = numbers.AggregateRight("6", (a, b) => $"({a}/{b})"); /// ]]> /// The result variable will contain "(1/(2/(3/(4/(5/6)))))". /// @@ -415,7 +415,7 @@ public static TAccumulate AggregateRight(this IEnumerable< /// /// string.Format("({0}/{1})", a, b), str => str.Length); + /// int result = numbers.AggregateRight("6", (a, b) => $"({a}/{b})", str => str.Length); /// ]]> /// The result variable will contain 21. /// @@ -5022,7 +5022,7 @@ public static IEnumerable ScanRight(this IEnumerable /// The scanned sequence. /// /// string.Format("({0}/{1})", a, b)); + /// var result = Enumerable.Range(1, 4).ScanRight("5", (a, b) => $"({a}+{b})"); /// ]]> /// The result variable will contain [ "(1+(2+(3+(4+5))))", "(2+(3+(4+5)))", "(3+(4+5))", "(4+5)", "5" ]. /// diff --git a/MoreLinq/ScanRight.cs b/MoreLinq/ScanRight.cs index 41ba6d270..d7c7b84a5 100644 --- a/MoreLinq/ScanRight.cs +++ b/MoreLinq/ScanRight.cs @@ -67,7 +67,7 @@ public static IEnumerable ScanRight(this IEnumerable /// The scanned sequence. /// /// string.Format("({0}/{1})", a, b)); + /// var result = Enumerable.Range(1, 4).ScanRight("5", (a, b) => $"({a}+{b})"); /// ]]> /// The result variable will contain [ "(1+(2+(3+(4+5))))", "(2+(3+(4+5)))", "(3+(4+5))", "(4+5)", "5" ]. /// From 72d918ece78f20d16cdd61a4ffacc0790308cabd Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 19:24:45 +0100 Subject: [PATCH 110/157] Discard unused parameters (#864) --- MoreLinq.Test/AggregateRightTest.cs | 2 +- MoreLinq.Test/BreakingAction.cs | 8 ++++---- MoreLinq.Test/BreakingFunc.cs | 8 ++++---- MoreLinq.Test/ConsumeTest.cs | 2 +- MoreLinq.Test/GroupAdjacentTest.cs | 4 ++-- MoreLinq.Test/LagTest.cs | 8 ++++---- MoreLinq.Test/LeadTest.cs | 4 ++-- MoreLinq.Test/ScanByTest.cs | 12 ++++++------ MoreLinq.Test/ScanRightTest.cs | 2 +- MoreLinq.Test/SegmentTest.cs | 16 ++++++++-------- MoreLinq.Test/TraverseTest.cs | 4 ++-- MoreLinq/Consume.cs | 2 +- MoreLinq/EquiZip.cs | 2 +- MoreLinq/IndexBy.cs | 2 +- MoreLinq/NestedLoops.cs | 2 +- MoreLinq/Partition.cs | 4 ++-- MoreLinq/Scan.cs | 2 +- MoreLinq/Segment.cs | 4 ++-- MoreLinq/TakeEvery.cs | 2 +- MoreLinq/ZipLongest.cs | 4 ++-- MoreLinq/ZipShortest.cs | 2 +- 21 files changed, 48 insertions(+), 48 deletions(-) diff --git a/MoreLinq.Test/AggregateRightTest.cs b/MoreLinq.Test/AggregateRightTest.cs index 55a8e091d..4128577d8 100644 --- a/MoreLinq.Test/AggregateRightTest.cs +++ b/MoreLinq.Test/AggregateRightTest.cs @@ -61,7 +61,7 @@ public void AggregateRight(SourceKind sourceKind) [TestCase(true)] public void AggregateRightSeedWithEmptySequence(object defaultValue) { - Assert.That(new int[0].AggregateRight(defaultValue, (a, b) => b), Is.EqualTo(defaultValue)); + Assert.That(new int[0].AggregateRight(defaultValue, (_, b) => b), Is.EqualTo(defaultValue)); } [Test] diff --git a/MoreLinq.Test/BreakingAction.cs b/MoreLinq.Test/BreakingAction.cs index 52dd2ab28..f4305835d 100644 --- a/MoreLinq.Test/BreakingAction.cs +++ b/MoreLinq.Test/BreakingAction.cs @@ -28,15 +28,15 @@ static class BreakingAction () => throw new NotImplementedException(); internal static Action Of() => - t => throw new NotImplementedException(); + _ => throw new NotImplementedException(); internal static Action Of() => - (t1, t2) => throw new NotImplementedException(); + (_, _) => throw new NotImplementedException(); internal static Action Of() => - (t1, t2, t3) => throw new NotImplementedException(); + (_, _, _) => throw new NotImplementedException(); internal static Action Of() => - (t1, t2, t3, t4) => throw new NotImplementedException(); + (_, _, _, _) => throw new NotImplementedException(); } } diff --git a/MoreLinq.Test/BreakingFunc.cs b/MoreLinq.Test/BreakingFunc.cs index 16d6fb794..99b1a5692 100644 --- a/MoreLinq.Test/BreakingFunc.cs +++ b/MoreLinq.Test/BreakingFunc.cs @@ -28,15 +28,15 @@ internal static Func Of() => () => throw new NotImplementedException(); internal static Func Of() => - t => throw new NotImplementedException(); + _ => throw new NotImplementedException(); internal static Func Of() => - (t1, t2) => throw new NotImplementedException(); + (_, _) => throw new NotImplementedException(); internal static Func Of() => - (t1, t2, t3) => throw new NotImplementedException(); + (_, _, _) => throw new NotImplementedException(); internal static Func Of() => - (t1, t2, t3, t4) => throw new NotImplementedException(); + (_, _, _, _) => throw new NotImplementedException(); } } diff --git a/MoreLinq.Test/ConsumeTest.cs b/MoreLinq.Test/ConsumeTest.cs index ebc8f4e88..83279cabd 100644 --- a/MoreLinq.Test/ConsumeTest.cs +++ b/MoreLinq.Test/ConsumeTest.cs @@ -26,7 +26,7 @@ public class ConsumeTest public void ConsumeReallyConsumes() { var counter = 0; - var sequence = Enumerable.Range(0, 10).Pipe(x => counter++); + var sequence = Enumerable.Range(0, 10).Pipe(_ => counter++); sequence.Consume(); Assert.AreEqual(10, counter); } diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index 995405f1d..8c23e5552 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -159,7 +159,7 @@ public void GroupAdjacentSourceSequenceResultSelector() new { Month = 1, Value = 781 }, }; - var groupings = source.GroupAdjacent(e => e.Month, (key, group) => group.Sum(v => v.Value)); + var groupings = source.GroupAdjacent(e => e.Month, (_, group) => group.Sum(v => v.Value)); using var reader = groupings.Read(); AssertResult(reader, 123 + 456 + 789); @@ -188,7 +188,7 @@ public void GroupAdjacentSourceSequenceResultSelectorComparer() new { Month = "JAN", Value = 781 }, }; - var groupings = source.GroupAdjacent(e => e.Month, (key, group) => group.Sum(v => v.Value), StringComparer.OrdinalIgnoreCase); + var groupings = source.GroupAdjacent(e => e.Month, (_, group) => group.Sum(v => v.Value), StringComparer.OrdinalIgnoreCase); using var reader = groupings.Read(); AssertResult(reader, 123 + 456 + 789); diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index 331fae6f7..e41a91c83 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -44,7 +44,7 @@ public void TestLagIsLazy() public void TestLagNegativeOffsetException() { AssertThrowsArgument.OutOfRangeException("offset",() => - Enumerable.Repeat(1, 10).Lag(-10, (val, lagVal) => val)); + Enumerable.Repeat(1, 10).Lag(-10, (val, _) => val)); } /// @@ -67,7 +67,7 @@ public void TestLagExplicitDefaultValue() const int lagBy = 10; const int lagDefault = -1; var sequence = Enumerable.Range(1, count); - var result = sequence.Lag(lagBy, lagDefault, (val, lagVal) => lagVal); + var result = sequence.Lag(lagBy, lagDefault, (_, lagVal) => lagVal); Assert.AreEqual(count, result.Count()); Assert.That(result.Take(lagBy), Is.EqualTo(Enumerable.Repeat(lagDefault, lagBy))); @@ -82,7 +82,7 @@ public void TestLagImplicitDefaultValue() const int count = 100; const int lagBy = 10; var sequence = Enumerable.Range(1, count); - var result = sequence.Lag(lagBy, (val, lagVal) => lagVal); + var result = sequence.Lag(lagBy, (_, lagVal) => lagVal); Assert.AreEqual(count, result.Count()); Assert.That(result.Take(lagBy), Is.EqualTo(Enumerable.Repeat(default(int), lagBy))); @@ -97,7 +97,7 @@ public void TestLagOffsetGreaterThanSequenceLength() { const int count = 100; var sequence = Enumerable.Range(1, count); - var result = sequence.Lag(count + 1, (a, b) => a); + var result = sequence.Lag(count + 1, (a, _) => a); Assert.AreEqual(count, result.Count()); Assert.That(result, Is.EqualTo(sequence)); diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index e0d4c592d..f85864893 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -67,7 +67,7 @@ public void TestLeadExplicitDefaultValue() const int leadBy = 10; const int leadDefault = -1; var sequence = Enumerable.Range(1, count); - var result = sequence.Lead(leadBy, leadDefault, (val, leadVal) => leadVal); + var result = sequence.Lead(leadBy, leadDefault, (_, leadVal) => leadVal); Assert.AreEqual(count, result.Count()); Assert.That(result.Skip(count - leadBy), Is.EqualTo(Enumerable.Repeat(leadDefault, leadBy))); @@ -82,7 +82,7 @@ public void TestLeadImplicitDefaultValue() const int count = 100; const int leadBy = 10; var sequence = Enumerable.Range(1, count); - var result = sequence.Lead(leadBy, (val, leadVal) => leadVal); + var result = sequence.Lead(leadBy, (_, leadVal) => leadVal); Assert.AreEqual(count, result.Count()); Assert.That(result.Skip(count - leadBy), Is.EqualTo(Enumerable.Repeat(default(int), leadBy))); diff --git a/MoreLinq.Test/ScanByTest.cs b/MoreLinq.Test/ScanByTest.cs index ab5098389..dc263c9e1 100644 --- a/MoreLinq.Test/ScanByTest.cs +++ b/MoreLinq.Test/ScanByTest.cs @@ -67,7 +67,7 @@ public void ScanBy() [Test] public void ScanByWithSecondOccurenceImmediatelyAfterFirst() { - var result = "jaffer".ScanBy(c => c, k => -1, (i, k, e) => i + 1); + var result = "jaffer".ScanBy(c => c, _ => -1, (i, _, _) => i + 1); result.AssertSequenceEqual( KeyValuePair.Create('j', 0), @@ -83,8 +83,8 @@ public void ScanByWithEqualityComparer() { var source = new[] { "a", "B", "c", "A", "b", "A" }; var result = source.ScanBy(c => c, - k => -1, - (i, k, e) => i + 1, + _ => -1, + (i, _, _) => i + 1, StringComparer.OrdinalIgnoreCase); result.AssertSequenceEqual( @@ -100,7 +100,7 @@ public void ScanByWithEqualityComparer() public void ScanByWithSomeNullKeys() { var source = new[] { "foo", null, "bar", "baz", null, null, "baz", "bar", null, "foo" }; - var result = source.ScanBy(c => c, k => -1, (i, k, e) => i + 1); + var result = source.ScanBy(c => c, _ => -1, (i, _, _) => i + 1); result.AssertSequenceEqual( KeyValuePair.Create("foo" , 0), @@ -120,7 +120,7 @@ public void ScanByWithNullSeed() { var nil = (object)null; var source = new[] { "foo", null, "bar", null, "baz" }; - var result = source.ScanBy(c => c, k => nil, (i, k, e) => nil); + var result = source.ScanBy(c => c, _ => nil, (_, _, _) => nil); result.AssertSequenceEqual( KeyValuePair.Create("foo" , nil), @@ -142,7 +142,7 @@ public void ScanByDoesNotIterateUnnecessaryElements() () => "angelo", () => "carlos"); - var result = source.ScanBy(c => c.First(), k => -1, (i, k, e) => i + 1); + var result = source.ScanBy(c => c.First(), _ => -1, (i, _, _) => i + 1); result.Take(5).AssertSequenceEqual( KeyValuePair.Create('a', 0), diff --git a/MoreLinq.Test/ScanRightTest.cs b/MoreLinq.Test/ScanRightTest.cs index 948dd8c70..45a21fe87 100644 --- a/MoreLinq.Test/ScanRightTest.cs +++ b/MoreLinq.Test/ScanRightTest.cs @@ -84,7 +84,7 @@ public void ScanRightIsLazy() [TestCase(true)] public void ScanRightSeedWithEmptySequence(object defaultValue) { - Assert.That(new int[0].ScanRight(defaultValue, (a, b) => b), Is.EqualTo(new[] { defaultValue })); + Assert.That(new int[0].ScanRight(defaultValue, (_, b) => b), Is.EqualTo(new[] { defaultValue })); } [Test] diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index a9b786568..e15023a17 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -46,7 +46,7 @@ public void TestIdentitySegment() { const int count = 5; var sequence = Enumerable.Range(1, count); - var result = sequence.Segment(x => false); + var result = sequence.Segment(_ => false); Assert.That(result.Single(), Is.EqualTo(sequence)); } @@ -58,7 +58,7 @@ public void TestIdentitySegment() public void TestEmptySequence() { var sequence = Enumerable.Repeat(-1, 0); - var result = sequence.Segment(x => true); + var result = sequence.Segment(_ => true); Assert.That(result, Is.Empty); } @@ -70,7 +70,7 @@ public void TestSegmentIsIdempotent() { const int value = -1; var sequence = Enumerable.Repeat(value, 10); - var result = sequence.Segment(x => true); + var result = sequence.Segment(_ => true); foreach (var segment in result) { @@ -90,9 +90,9 @@ public void TestSegmentIsIdempotent() public void TestFirstSegmentNeverEmpty() { var sequence = Enumerable.Repeat(-1, 10); - var resultA = sequence.Segment(x => true); - var resultB = sequence.Segment((x, index) => true); - var resultC = sequence.Segment((x, prevX, index) => true); + var resultA = sequence.Segment(_ => true); + var resultB = sequence.Segment((_, _) => true); + var resultC = sequence.Segment((_, _, _) => true); Assert.IsTrue(resultA.First().Any()); Assert.IsTrue(resultB.First().Any()); @@ -125,7 +125,7 @@ public void VerifyCanSegmentByIndex() const int segmentSize = 2; var sequence = Enumerable.Repeat(1, count); - var result = sequence.Segment((x, i) => i % segmentSize == 0); + var result = sequence.Segment((_, i) => i % segmentSize == 0); Assert.AreEqual(count / segmentSize, result.Count()); foreach (var segment in result) @@ -143,7 +143,7 @@ public void VerifyCanSegmentByPrevious() const int repCount = 5; var sequence = Enumerable.Range(1, 3) .SelectMany(x => Enumerable.Repeat(x, repCount)); - var result = sequence.Segment((curr, prev, i) => curr != prev); + var result = sequence.Segment((curr, prev, _) => curr != prev); Assert.AreEqual(sequence.Distinct().Count(), result.Count()); Assert.IsTrue(result.All(s => s.Count() == repCount)); diff --git a/MoreLinq.Test/TraverseTest.cs b/MoreLinq.Test/TraverseTest.cs index 37e54fd41..aa3ffb2db 100644 --- a/MoreLinq.Test/TraverseTest.cs +++ b/MoreLinq.Test/TraverseTest.cs @@ -26,13 +26,13 @@ public class TraverseTest [Test] public void TraverseDepthFirstFNullGenerator() { - MoreEnumerable.TraverseDepthFirst(new object(), o => new BreakingSequence()); + MoreEnumerable.TraverseDepthFirst(new object(), _ => new BreakingSequence()); } [Test] public void TraverseBreadthFirstIsStreaming() { - MoreEnumerable.TraverseBreadthFirst(new object(), o => new BreakingSequence()); + MoreEnumerable.TraverseBreadthFirst(new object(), _ => new BreakingSequence()); } [Test] diff --git a/MoreLinq/Consume.cs b/MoreLinq/Consume.cs index 924fc6709..0a65b87ba 100644 --- a/MoreLinq/Consume.cs +++ b/MoreLinq/Consume.cs @@ -32,7 +32,7 @@ static partial class MoreEnumerable public static void Consume(this IEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); - foreach (var element in source) + foreach (var _ in source) { } } diff --git a/MoreLinq/EquiZip.cs b/MoreLinq/EquiZip.cs index 9cde2a6d1..1f2b351f6 100644 --- a/MoreLinq/EquiZip.cs +++ b/MoreLinq/EquiZip.cs @@ -64,7 +64,7 @@ public static IEnumerable EquiZip( if (second == null) throw new ArgumentNullException(nameof(second)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return EquiZipImpl(first, second, null, null, (a, b, c, d) => resultSelector(a, b)); + return EquiZipImpl(first, second, null, null, (a, b, _, _) => resultSelector(a, b)); } /// diff --git a/MoreLinq/IndexBy.cs b/MoreLinq/IndexBy.cs index 64fad32d3..aba93f82f 100644 --- a/MoreLinq/IndexBy.cs +++ b/MoreLinq/IndexBy.cs @@ -70,7 +70,7 @@ public static IEnumerable> this IEnumerable source, Func keySelector, IEqualityComparer? comparer) => - from e in source.ScanBy(keySelector, k => (Index: -1, Item: default(TSource)), (s, k, e) => (s.Index + 1, e), comparer) + from e in source.ScanBy(keySelector, _ => (Index: -1, Item: default(TSource)), (s, _, e) => (s.Index + 1, e), comparer) select new KeyValuePair(e.Value.Index, e.Value.Item); } } diff --git a/MoreLinq/NestedLoops.cs b/MoreLinq/NestedLoops.cs index fd5409259..cb8d74d7a 100644 --- a/MoreLinq/NestedLoops.cs +++ b/MoreLinq/NestedLoops.cs @@ -42,7 +42,7 @@ static IEnumerable NestedLoops(this Action action, IEnumerable loop return _(); IEnumerable _() { var count = loopCounts.Assert(n => n >= 0, - n => new InvalidOperationException("Invalid loop count (must be greater than or equal to zero).")) + _ => new InvalidOperationException("Invalid loop count (must be greater than or equal to zero).")) .DefaultIfEmpty() .Aggregate((acc, x) => acc * x); diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 9066610f4..48a11c7ea 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -179,7 +179,7 @@ public static TResult Partition(this IEnumerable resultSelector(a, rest)); + (a, _, _, rest) => resultSelector(a, rest)); } /// @@ -237,7 +237,7 @@ public static TResult Partition(this IEnumerable resultSelector(a, b, rest)); + (a, b, _, rest) => resultSelector(a, b, rest)); } /// diff --git a/MoreLinq/Scan.cs b/MoreLinq/Scan.cs index a14ffe599..50a8ac4d0 100644 --- a/MoreLinq/Scan.cs +++ b/MoreLinq/Scan.cs @@ -86,7 +86,7 @@ public static IEnumerable Scan(this IEnumerable (true, seed)); + return ScanImpl(source, transformation, _ => (true, seed)); } static IEnumerable ScanImpl(IEnumerable source, diff --git a/MoreLinq/Segment.cs b/MoreLinq/Segment.cs index 42a9f9ce6..72de5fc4b 100644 --- a/MoreLinq/Segment.cs +++ b/MoreLinq/Segment.cs @@ -37,7 +37,7 @@ public static IEnumerable> Segment(this IEnumerable source, { if (newSegmentPredicate == null) throw new ArgumentNullException(nameof(newSegmentPredicate)); - return Segment(source, (curr, prev, index) => newSegmentPredicate(curr)); + return Segment(source, (curr, _, _) => newSegmentPredicate(curr)); } /// @@ -55,7 +55,7 @@ public static IEnumerable> Segment(this IEnumerable source, { if (newSegmentPredicate == null) throw new ArgumentNullException(nameof(newSegmentPredicate)); - return Segment(source, (curr, prev, index) => newSegmentPredicate(curr, index)); + return Segment(source, (curr, _, index) => newSegmentPredicate(curr, index)); } /// diff --git a/MoreLinq/TakeEvery.cs b/MoreLinq/TakeEvery.cs index 32b778cab..dc0f26042 100644 --- a/MoreLinq/TakeEvery.cs +++ b/MoreLinq/TakeEvery.cs @@ -47,7 +47,7 @@ public static IEnumerable TakeEvery(this IEnumerable { if (source == null) throw new ArgumentNullException(nameof(source)); if (step <= 0) throw new ArgumentOutOfRangeException(nameof(step)); - return source.Where((e, i) => i % step == 0); + return source.Where((_, i) => i % step == 0); } } } diff --git a/MoreLinq/ZipLongest.cs b/MoreLinq/ZipLongest.cs index ec5a240e6..d0f663438 100644 --- a/MoreLinq/ZipLongest.cs +++ b/MoreLinq/ZipLongest.cs @@ -62,7 +62,7 @@ public static IEnumerable ZipLongest( if (second == null) throw new ArgumentNullException(nameof(second)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return ZipImpl(first, second, null, null, (a, b, c, d) => resultSelector(a, b), 1); + return ZipImpl(first, second, null, null, (a, b, _, _) => resultSelector(a, b), 1); } /// @@ -110,7 +110,7 @@ public static IEnumerable ZipLongest( if (third == null) throw new ArgumentNullException(nameof(third)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return ZipImpl(first, second, third, null, (a, b, c, d) => resultSelector(a, b, c), 2); + return ZipImpl(first, second, third, null, (a, b, c, _) => resultSelector(a, b, c), 2); } /// diff --git a/MoreLinq/ZipShortest.cs b/MoreLinq/ZipShortest.cs index 0d8039eca..acea0af6c 100644 --- a/MoreLinq/ZipShortest.cs +++ b/MoreLinq/ZipShortest.cs @@ -64,7 +64,7 @@ public static IEnumerable ZipShortest( if (second == null) throw new ArgumentNullException(nameof(second)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return ZipImpl(first, second, null, null, (a, b, c, d) => resultSelector(a, b)); + return ZipImpl(first, second, null, null, (a, b, _, _) => resultSelector(a, b)); } /// From 42dbe2013b4105db8353c1b1fcd2fa69b8982d73 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 22:21:20 +0100 Subject: [PATCH 111/157] Rewrite switch statements as expressions (#865) --- MoreLinq.Test/FlattenTest.cs | 51 +++++++++--------------------- MoreLinq.Test/FromTest.cs | 15 ++++----- MoreLinq.Test/FullGroupJoinTest.cs | 17 ++++------ MoreLinq.Test/NullArgumentTest.cs | 10 +++--- MoreLinq.Test/TestExtensions.cs | 22 +++++-------- MoreLinq/Backsert.cs | 11 ++++--- MoreLinq/Exclude.cs | 11 ++++--- MoreLinq/Fold.cs | 38 +++++++++++----------- MoreLinq/MaxBy.cs | 18 +++++------ MoreLinq/OrderedMerge.cs | 30 ++++++++---------- 10 files changed, 98 insertions(+), 125 deletions(-) diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index a416df347..f7d0ea28a 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -325,21 +325,13 @@ public void FlattenSelector() } }; - var result = source.Flatten(obj => + var result = source.Flatten(obj => obj switch { - switch (obj) - { - case string: - return null; - case IEnumerable inner: - return inner; - case Series s: - return new object[] { s.Name, s.Attributes }; - case Attribute a: - return a.Values; - default: - return null; - } + string => null, + IEnumerable inner => inner, + Series s => new object[] { s.Name, s.Attributes }, + Attribute a => a.Values, + _ => null }); var expectations = new object[] { "series1", 1, 2, 3, 4, "series2", 5, 6 }; @@ -368,17 +360,11 @@ public void FlattenSelectorFilteringOnlyIntegers() 4, }; - var result = source.Flatten(obj => + var result = source.Flatten(obj => obj switch { - switch (obj) - { - case int: - return null; - case IEnumerable inner: - return inner; - default: - return Enumerable.Empty(); - } + int => null, + IEnumerable inner => inner, + _ => Enumerable.Empty() }); var expectations = new object[] { 1, 2, 3, 4 }; @@ -406,19 +392,12 @@ public void FlattenSelectorWithTree() ) ); - var result = new [] { source }.Flatten(obj => + var result = new[] { source }.Flatten(obj => obj switch { - switch (obj) - { - case int: - return null; - case Tree tree: - return new object[] { tree.Left, tree.Value, tree.Right }; - case IEnumerable inner: - return inner; - default: - return Enumerable.Empty(); - } + int => null, + Tree tree => new object[] { tree.Left, tree.Value, tree.Right }, + IEnumerable inner => inner, + _ => Enumerable.Empty() }); var expectations = Enumerable.Range(1, 7); diff --git a/MoreLinq.Test/FromTest.cs b/MoreLinq.Test/FromTest.cs index 9577fe231..8dbe01375 100644 --- a/MoreLinq.Test/FromTest.cs +++ b/MoreLinq.Test/FromTest.cs @@ -66,15 +66,14 @@ public void TestFromInvokesMethodsMultipleTimes(int numArgs) int F3() { evals[2]++; return -2; } int F4() { evals[3]++; return -2; } - IEnumerable results; - switch (numArgs) + var results = numArgs switch { - case 1: results = MoreEnumerable.From(F1); break; - case 2: results = MoreEnumerable.From(F1, F2); break; - case 3: results = MoreEnumerable.From(F1, F2, F3); break; - case 4: results = MoreEnumerable.From(F1, F2, F3, F4); break; - default: throw new ArgumentOutOfRangeException(nameof(numArgs)); - } + 1 => MoreEnumerable.From(F1), + 2 => MoreEnumerable.From(F1, F2), + 3 => MoreEnumerable.From(F1, F2, F3), + 4 => MoreEnumerable.From(F1, F2, F3, F4), + _ => throw new ArgumentOutOfRangeException(nameof(numArgs)) + }; results.Consume(); results.Consume(); diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index 40d8778c5..1b7838ee3 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -133,17 +133,12 @@ public void FullGroupPreservesOrder(OverloadCase overloadCase) } } - static IEnumerable<(int Key, IEnumerable First, IEnumerable Second)> FullGroupJoin(OverloadCase overloadCase, IEnumerable listA, IEnumerable listB, Func getKey) - { - switch (overloadCase) + static IEnumerable<(int Key, IEnumerable First, IEnumerable Second)> FullGroupJoin(OverloadCase overloadCase, IEnumerable listA, IEnumerable listB, Func getKey) => + overloadCase switch { - case CustomResult: - return listA.FullGroupJoin(listB, getKey, getKey, ValueTuple.Create, comparer: null); - case TupleResult: - return listA.FullGroupJoin(listB, getKey, getKey); - default: - throw new ArgumentOutOfRangeException(nameof(overloadCase)); - } - } + CustomResult => listA.FullGroupJoin(listB, getKey, getKey, ValueTuple.Create, comparer: null), + TupleResult => listA.FullGroupJoin(listB, getKey, getKey), + _ => throw new ArgumentOutOfRangeException(nameof(overloadCase)) + }; } } diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index 56f3eb8fc..8e8d0f36d 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -98,10 +98,12 @@ static Type InstantiateType(TypeInfo typeParameter) { var constraints = typeParameter.GetGenericParameterConstraints(); - if (constraints.Length == 0) return typeof (int); - if (constraints.Length == 1) return constraints.Single(); - - throw new NotImplementedException("NullArgumentTest.InstantiateType"); + return constraints.Length switch + { + 0 => typeof(int), + 1 => constraints.Single(), + _ => throw new NotImplementedException("NullArgumentTest.InstantiateType") + }; } static bool IsReferenceType(ParameterInfo parameter) => diff --git a/MoreLinq.Test/TestExtensions.cs b/MoreLinq.Test/TestExtensions.cs index 5331447dd..b6e12aee2 100644 --- a/MoreLinq.Test/TestExtensions.cs +++ b/MoreLinq.Test/TestExtensions.cs @@ -76,21 +76,15 @@ internal static IEnumerable> ArrangeCollectionTestCases(this I internal static IEnumerable ToSourceKind(this IEnumerable input, SourceKind sourceKind) { - switch (sourceKind) + return sourceKind switch { - case SourceKind.Sequence: - return input.Select(x => x); - case SourceKind.BreakingList: - return new BreakingList(input.ToList()); - case SourceKind.BreakingReadOnlyList: - return new BreakingReadOnlyList(input.ToList()); - case SourceKind.BreakingCollection: - return new BreakingCollection(input.ToList()); - case SourceKind.BreakingReadOnlyCollection: - return new BreakingReadOnlyCollection(input.ToList()); - default: - throw new ArgumentException(null, nameof(sourceKind)); - } + SourceKind.Sequence => input.Select(x => x), + SourceKind.BreakingList => new BreakingList(input.ToList()), + SourceKind.BreakingReadOnlyList => new BreakingReadOnlyList(input.ToList()), + SourceKind.BreakingCollection => new BreakingCollection(input.ToList()), + SourceKind.BreakingReadOnlyCollection => new BreakingReadOnlyCollection(input.ToList()), + _ => throw new ArgumentException(null, nameof(sourceKind)) + }; } } } diff --git a/MoreLinq/Backsert.cs b/MoreLinq/Backsert.cs index 1ac6a9d4c..3b0a0b4bf 100644 --- a/MoreLinq/Backsert.cs +++ b/MoreLinq/Backsert.cs @@ -60,12 +60,15 @@ public static IEnumerable Backsert(this IEnumerable first, IEnumerable< { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); - if (index < 0) throw new ArgumentOutOfRangeException(nameof(index), "Index cannot be negative."); - if (index == 0) - return first.Concat(second); + return index switch + { + < 0 => throw new ArgumentOutOfRangeException(nameof(index), "Index cannot be negative."), + 0 => first.Concat(second), + _ => _() + }; - return _(); IEnumerable _() + IEnumerable _() { using var e = first.CountDown(index, ValueTuple.Create).GetEnumerator(); diff --git a/MoreLinq/Exclude.cs b/MoreLinq/Exclude.cs index 6572753ca..4e3d7edc9 100644 --- a/MoreLinq/Exclude.cs +++ b/MoreLinq/Exclude.cs @@ -36,12 +36,15 @@ public static IEnumerable Exclude(this IEnumerable sequence, int startI { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex)); - if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (count == 0) - return sequence; + return count switch + { + < 0 => throw new ArgumentOutOfRangeException(nameof(count)), + 0 => sequence, + _ => _() + }; - return _(); IEnumerable _() + IEnumerable _() { var index = -1; var endIndex = startIndex + count; diff --git a/MoreLinq/Fold.cs b/MoreLinq/Fold.cs index ae4337292..6698cec9a 100644 --- a/MoreLinq/Fold.cs +++ b/MoreLinq/Fold.cs @@ -67,26 +67,26 @@ static TResult FoldImpl(IEnumerable source, int count, foreach (var e in AssertCountImpl(source.Index(), count, OnFolderSourceSizeErrorSelector)) elements[e.Key] = e.Value; - switch (count) + return count switch { - case 1: return folder1 !(elements[0]); - case 2: return folder2 !(elements[0], elements[1]); - case 3: return folder3 !(elements[0], elements[1], elements[2]); - case 4: return folder4 !(elements[0], elements[1], elements[2], elements[3]); - case 5: return folder5 !(elements[0], elements[1], elements[2], elements[3], elements[4]); - case 6: return folder6 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]); - case 7: return folder7 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6]); - case 8: return folder8 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7]); - case 9: return folder9 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]); - case 10: return folder10!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9]); - case 11: return folder11!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10]); - case 12: return folder12!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11]); - case 13: return folder13!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12]); - case 14: return folder14!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13]); - case 15: return folder15!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14]); - case 16: return folder16!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14], elements[15]); - default: throw new NotSupportedException(); - } + 1 => folder1 !(elements[0]), + 2 => folder2 !(elements[0], elements[1]), + 3 => folder3 !(elements[0], elements[1], elements[2]), + 4 => folder4 !(elements[0], elements[1], elements[2], elements[3]), + 5 => folder5 !(elements[0], elements[1], elements[2], elements[3], elements[4]), + 6 => folder6 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]), + 7 => folder7 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6]), + 8 => folder8 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7]), + 9 => folder9 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]), + 10 => folder10!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9]), + 11 => folder11!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10]), + 12 => folder12!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11]), + 13 => folder13!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12]), + 14 => folder14!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13]), + 15 => folder15!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14]), + 16 => folder16!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14], elements[15]), + _ => throw new NotSupportedException() + }; } static readonly Func OnFolderSourceSizeErrorSelector = OnFolderSourceSizeError; diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index 098c15f00..3752c1645 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -340,16 +340,16 @@ IEnumerable Extrema() { var item = e.Current; var key = selector(item); - var comparison = comparer(key, extremaKey); - if (comparison > 0) + switch (comparer(key, extremaKey)) { - extrema.Restart(ref store); - extrema.Add(ref store, limit, item); - extremaKey = key; - } - else if (comparison == 0) - { - extrema.Add(ref store, limit, item); + case > 0: + extrema.Restart(ref store); + extrema.Add(ref store, limit, item); + extremaKey = key; + break; + case 0: + extrema.Add(ref store, limit, item); + break; } } diff --git a/MoreLinq/OrderedMerge.cs b/MoreLinq/OrderedMerge.cs index bb4551ec7..a42e08057 100644 --- a/MoreLinq/OrderedMerge.cs +++ b/MoreLinq/OrderedMerge.cs @@ -300,23 +300,21 @@ IEnumerable _(IComparer comparer) var key1 = firstKeySelector(element1); var element2 = e2.Current; var key2 = secondKeySelector(element2); - var comparison = comparer.Compare(key1, key2); - - if (comparison < 0) - { - yield return firstSelector(element1); - gotFirst = e1.MoveNext(); - } - else if (comparison > 0) - { - yield return secondSelector(element2); - gotSecond = e2.MoveNext(); - } - else + switch (comparer.Compare(key1, key2)) { - yield return bothSelector(element1, element2); - gotFirst = e1.MoveNext(); - gotSecond = e2.MoveNext(); + case < 0: + yield return firstSelector(element1); + gotFirst = e1.MoveNext(); + break; + case > 0: + yield return secondSelector(element2); + gotSecond = e2.MoveNext(); + break; + default: + yield return bothSelector(element1, element2); + gotFirst = e1.MoveNext(); + gotSecond = e2.MoveNext(); + break; } } else if (gotSecond) From f500bc9e95cf2131f4e31303120790339253e450 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 22:21:36 +0100 Subject: [PATCH 112/157] Use "+=" to increment (#869) --- MoreLinq/Sequence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/Sequence.cs b/MoreLinq/Sequence.cs index 5eaae4ed3..6b7f94502 100644 --- a/MoreLinq/Sequence.cs +++ b/MoreLinq/Sequence.cs @@ -71,7 +71,7 @@ public static IEnumerable Sequence(int start, int stop, int step) : stop <= current) { yield return (int)current; - current = current + step; + current += step; } } } From 27aa476be7f73e81be9f91a9209c51525c438c8d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 22:22:06 +0100 Subject: [PATCH 113/157] Convert using statements to declarations (#867) --- MoreLinq.Test/TraceTest.cs | 10 +++------- MoreLinq/MoreEnumerable.cs | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/MoreLinq.Test/TraceTest.cs b/MoreLinq.Test/TraceTest.cs index 0379c2f49..50d07e572 100644 --- a/MoreLinq.Test/TraceTest.cs +++ b/MoreLinq.Test/TraceTest.cs @@ -78,13 +78,9 @@ public void TraceSequenceWithFormatter() static IEnumerable Lines(string str) { - using (var e = _(string.IsNullOrEmpty(str) - ? TextReader.Null - : new StringReader(str))) - { - while (e.MoveNext()) - yield return e.Current; - } + using var e = _(string.IsNullOrEmpty(str) ? TextReader.Null : new StringReader(str)); + while (e.MoveNext()) + yield return e.Current; IEnumerator _(TextReader reader) { diff --git a/MoreLinq/MoreEnumerable.cs b/MoreLinq/MoreEnumerable.cs index ca7938cc7..b80983c3e 100644 --- a/MoreLinq/MoreEnumerable.cs +++ b/MoreLinq/MoreEnumerable.cs @@ -43,13 +43,9 @@ static int CountUpTo(this IEnumerable source, int max) var count = 0; - using (var e = source.GetEnumerator()) - { - while (count < max && e.MoveNext()) - { - count++; - } - } + using var e = source.GetEnumerator(); + while (count < max && e.MoveNext()) + count++; return count; } From db115101a74ce87af3f90ca5bb34b57eb61b2ff6 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 22:29:05 +0100 Subject: [PATCH 114/157] Revise & take advantage of pattern matching (#866) --- MoreLinq.Test/AggregateTest.cs | 4 ++-- MoreLinq.Test/FlattenTest.cs | 2 +- MoreLinq.Test/LeadTest.cs | 2 +- MoreLinq.Test/MemoizeTest.cs | 2 +- MoreLinq.Test/RandomTest.cs | 8 ++++---- MoreLinq.Test/TraceTest.cs | 3 +-- MoreLinq/Experimental/Await.cs | 2 +- MoreLinq/FallbackIfEmpty.cs | 4 ++-- MoreLinq/Flatten.cs | 2 +- MoreLinq/Partition.cs | 2 +- MoreLinq/Split.cs | 2 +- MoreLinq/Subsets.cs | 2 +- MoreLinq/ToDataTable.cs | 2 +- bld/ExtensionsGenerator/Program.cs | 12 ++++++------ 14 files changed, 24 insertions(+), 25 deletions(-) diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 98b49e8b6..930522d58 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -121,8 +121,8 @@ public void SevenUniqueAccumulators() EvenSum = esum, Count = count, Average = (double)sum / count, - Min = min is {} mn ? mn : throw new InvalidOperationException(), - Max = max is {} mx ? mx : throw new InvalidOperationException(), + Min = min ?? throw new InvalidOperationException(), + Max = max ?? throw new InvalidOperationException(), UniqueLengths = lengths, Items = items, } diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index f7d0ea28a..f3b5b4a6c 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -136,7 +136,7 @@ public void FlattenPredicate() 7, }; - var result = source.Flatten(obj => !(obj is IEnumerable)); + var result = source.Flatten(obj => obj is not IEnumerable); var expectations = new object[] { diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index f85864893..2298be7c9 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -133,7 +133,7 @@ public void TestLeadPassesCorrectValueOffsetBy2() Assert.AreEqual(count, result.Count()); Assert.IsTrue(result.Take(count - 2).All(x => x.B == (x.A + 2))); - Assert.IsTrue(result.Skip(count - 2).All(x => x.B == leadDefault && (x.A == count || x.A == count - 1))); + Assert.IsTrue(result.Skip(count - 2).All(x => x.B == leadDefault && x.A is count or count - 1)); } [Test] diff --git a/MoreLinq.Test/MemoizeTest.cs b/MoreLinq.Test/MemoizeTest.cs index f2658fac2..80efcabf4 100644 --- a/MoreLinq.Test/MemoizeTest.cs +++ b/MoreLinq.Test/MemoizeTest.cs @@ -53,7 +53,7 @@ IEnumerable InnerForEach(IEnumerable source) { yield return i; - if (i == 3 || i == 7) + if (i is 3 or 7) { //consume 1-3 already cached //add 4-5 to cache (so go to outer loop) diff --git a/MoreLinq.Test/RandomTest.cs b/MoreLinq.Test/RandomTest.cs index 5f2bafdf0..54b295127 100644 --- a/MoreLinq.Test/RandomTest.cs +++ b/MoreLinq.Test/RandomTest.cs @@ -70,8 +70,8 @@ public void TestRandomDouble() // NOTE: Unclear what should actually be verified here... some additional thought needed. Assert.AreEqual(RandomTrials, resultA.Count()); Assert.AreEqual(RandomTrials, resultB.Count()); - Assert.IsTrue(resultA.All(x => x >= 0.0 && x < 1.0)); - Assert.IsTrue(resultB.All(x => x >= 0.0 && x < 1.0)); + Assert.IsTrue(resultA.All(x => x is >= 0.0 and < 1.0)); + Assert.IsTrue(resultB.All(x => x is >= 0.0 and < 1.0)); } /// @@ -103,8 +103,8 @@ public void TestRandomMinMaxConstraint() Assert.AreEqual(RandomTrials, resultA.Count()); Assert.AreEqual(RandomTrials, resultB.Count()); - Assert.IsTrue(resultA.All(x => x >= min && x < max)); - Assert.IsTrue(resultB.All(x => x >= min && x < max)); + Assert.IsTrue(resultA.All(x => x is >= min and < max)); + Assert.IsTrue(resultB.All(x => x is >= min and < max)); } /// diff --git a/MoreLinq.Test/TraceTest.cs b/MoreLinq.Test/TraceTest.cs index 50d07e572..36a7ca0ed 100644 --- a/MoreLinq.Test/TraceTest.cs +++ b/MoreLinq.Test/TraceTest.cs @@ -85,8 +85,7 @@ static IEnumerable Lines(string str) IEnumerator _(TextReader reader) { Debug.Assert(reader != null); - string line; - while ((line = reader.ReadLine()) != null) + while (reader.ReadLine() is { } line) yield return line; } } diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index e8fb18074..9b54d9e35 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -69,7 +69,7 @@ public sealed class AwaitQueryOptions AwaitQueryOptions(int? maxConcurrency, TaskScheduler scheduler, bool preserveOrder) { - MaxConcurrency = maxConcurrency == null || maxConcurrency > 0 + MaxConcurrency = maxConcurrency is null or > 0 ? maxConcurrency : throw new ArgumentOutOfRangeException( nameof(maxConcurrency), maxConcurrency, diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 0d0fbc867..90256fcad 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -184,11 +184,11 @@ IEnumerable _() IEnumerable Fallback() { - return fallback is {} seq ? seq : FallbackOnArgs(); + return fallback ?? FallbackOnArgs(); IEnumerable FallbackOnArgs() { - Debug.Assert(count >= 1 && count <= 4); + Debug.Assert(count is >= 1 and <= 4); yield return fallback1!; if (count > 1) yield return fallback2!; diff --git a/MoreLinq/Flatten.cs b/MoreLinq/Flatten.cs index b1845ea43..8de35aaf7 100644 --- a/MoreLinq/Flatten.cs +++ b/MoreLinq/Flatten.cs @@ -41,7 +41,7 @@ public static IEnumerable< #nullable restore /*...................................*/ > Flatten(this IEnumerable source) => - Flatten(source, obj => !(obj is string)); + Flatten(source, obj => obj is not string); /// /// Flattens a sequence containing arbitrarily-nested sequences. An diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 48a11c7ea..3b67ab6b3 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -300,7 +300,7 @@ static TResult PartitionImpl(IEnumerable? comparer, Func, IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) { - Debug.Assert(count > 0 && count <= 3); + Debug.Assert(count is > 0 and <= 3); if (source == null) throw new ArgumentNullException(nameof(source)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); diff --git a/MoreLinq/Split.cs b/MoreLinq/Split.cs index 93f52c22e..3ff9f2c97 100644 --- a/MoreLinq/Split.cs +++ b/MoreLinq/Split.cs @@ -292,7 +292,7 @@ public static IEnumerable Split(this IEnumerable 0) + if (items is { Count: > 0 }) yield return resultSelector(items); } } diff --git a/MoreLinq/Subsets.cs b/MoreLinq/Subsets.cs index 1491903bd..357a60f73 100644 --- a/MoreLinq/Subsets.cs +++ b/MoreLinq/Subsets.cs @@ -209,7 +209,7 @@ void ExtractSubset() public SubsetGenerator(IEnumerable sequence, int subsetSize) { - if (sequence == null) + if (sequence is null) throw new ArgumentNullException(nameof(sequence)); if (subsetSize < 0) throw new ArgumentOutOfRangeException(nameof(subsetSize), "{subsetSize} must be between 0 and set.Count()"); diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index bf309642e..d04c66d3c 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -164,7 +164,7 @@ MemberInfo GetAccessedMember(LambdaExpression lambda) var body = lambda.Body; // If it's a field access, boxing was used, we need the field - if (body.NodeType == ExpressionType.Convert || body.NodeType == ExpressionType.ConvertChecked) + if (body.NodeType is ExpressionType.Convert or ExpressionType.ConvertChecked) body = ((UnaryExpression)body).Operand; // Check if the member expression is valid and is a "first level" diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index 3b8c983bb..cd5b864d4 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -173,8 +173,8 @@ where md.ParameterList.Parameters.Count > 0 SortableParameterTypes = from p in md.ParameterList.Parameters select CreateTypeKey(p.Type, - n => typeParameterAbbreviationByName != null - && typeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), + n => typeParameterAbbreviationByName is { } someTypeParameterAbbreviationByName + && someTypeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), } } from e in ms.Select((m, i) => (SourceOrder: i + 1, Method: m)) @@ -385,8 +385,8 @@ abstract class TypeKey : IComparable public virtual int CompareTo(TypeKey other) => ReferenceEquals(this, other) ? 0 : other == null ? 1 - : Parameters.Count.CompareTo(other.Parameters.Count) is {} lc and not 0 ? lc - : string.Compare(Name, other.Name, StringComparison.Ordinal) is {} nc and not 0 ? nc + : Parameters.Count.CompareTo(other.Parameters.Count) is var lc and not 0 ? lc + : string.Compare(Name, other.Name, StringComparison.Ordinal) is var nc and not 0 ? nc : CompareParameters(other); protected virtual int CompareParameters(TypeKey other) => @@ -455,10 +455,10 @@ protected override int CompareParameters(TypeKey other) { if (other is ArrayTypeKey a) { - if (Ranks.Count.CompareTo(a.Ranks.Count) is {} rlc and not 0) + if (Ranks.Count.CompareTo(a.Ranks.Count) is var rlc and not 0) return rlc; if (Ranks.Zip(a.Ranks, (us, them) => (Us: us, Them: them)) - .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is {} rc and not 0) + .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is var rc and not 0) return rc; } From 66cfe260868278eab5c6446ad20fc4b8ad0701c4 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 23:39:00 +0100 Subject: [PATCH 115/157] Rewrite swap more succinctly via tuples (#868) --- MoreLinq/RandomSubset.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MoreLinq/RandomSubset.cs b/MoreLinq/RandomSubset.cs index 1c27e901c..b3c192719 100644 --- a/MoreLinq/RandomSubset.cs +++ b/MoreLinq/RandomSubset.cs @@ -88,9 +88,7 @@ static IEnumerable RandomSubsetImpl(IEnumerable source, Random rand, Fu while (m < subsetSize) { var k = g - rand.Next(w); - var tmp = array[k]; - array[k] = array[m]; - array[m] = tmp; + (array[k], array[m]) = (array[m], array[k]); ++m; --w; } From ac055df4cae5ed466671004b0e066ab5172d2a6f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 4 Nov 2022 23:40:07 +0100 Subject: [PATCH 116/157] Simplify names (IDE0001) (#861) https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0001 --- MoreLinq.Test/ToDataTableTest.cs | 2 +- MoreLinq/Lookup.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index 6281c01f1..b2d43b168 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -71,7 +71,7 @@ public void ToDataTableNullMemberExpressionMethod() Expression> expression = null; AssertThrowsArgument.Exception("expressions",() => - _testObjects.ToDataTable(expression)); + _testObjects.ToDataTable(expression)); } [Test] diff --git a/MoreLinq/Lookup.cs b/MoreLinq/Lookup.cs index c576d99a2..5373f28b7 100644 --- a/MoreLinq/Lookup.cs +++ b/MoreLinq/Lookup.cs @@ -117,7 +117,7 @@ public IEnumerable ApplyResultSelector(Func(ref g.elements, g.count); } + if (g.count != g.elements.Length) { Array.Resize(ref g.elements, g.count); } yield return resultSelector(g.key, g.elements); } while (g != _lastGrouping); } From 6e3b585f82b1a309f5fe98af44177e27506f85a8 Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Tue, 8 Nov 2022 13:48:37 +0100 Subject: [PATCH 117/157] Make PolySharp a build-time dependency (#870) --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 3f5a89681..35e2e7c3f 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -153,7 +153,7 @@ all - + From f426fd0c3be5e96c45e7cad5b02670e5f82ff777 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 9 Nov 2022 00:02:19 +0100 Subject: [PATCH 118/157] Update to .NET 7 (#871) --- MoreLinq.Test/MoreLinq.Test.csproj | 6 +++--- appveyor.yml | 2 ++ bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj | 4 ++-- global.json | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 93065e422..470cfcab3 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - net6.0;netcoreapp3.1;net451 + net7.0;net6.0;netcoreapp3.1;net451 portable MoreLinq.Test Exe @@ -37,8 +37,8 @@ - - + + diff --git a/appveyor.yml b/appveyor.yml index 80e4f4be4..4cff2558c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -54,8 +54,10 @@ install: - git reset --hard - ps: if ($isWindows) { tools\dotnet-install.ps1 -JSonFile global.json } - ps: if ($isWindows) { tools\dotnet-install.ps1 -Runtime dotnet -Version 3.1.10 -SkipNonVersionedFiles } +- ps: if ($isWindows) { tools\dotnet-install.ps1 -Runtime dotnet -Version 6.0.11 -SkipNonVersionedFiles } - sh: ./tools/dotnet-install.sh --jsonfile global.json - sh: ./tools/dotnet-install.sh --runtime dotnet --version 3.1.10 --skip-non-versioned-files +- sh: ./tools/dotnet-install.sh --runtime dotnet --version 6.0.11 --skip-non-versioned-files - sh: export PATH="~/.dotnet:$PATH" before_build: - dotnet --info diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index 67dfd0664..e1c9c8e49 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -1,11 +1,11 @@  Exe - net6.0 + net7.0 false - + diff --git a/global.json b/global.json index 18b4598f7..77c776f82 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.400", + "version": "7.0.100", "rollForward": "latestFeature" } } From d5edf084b51d1e5449b64f20322ffc76b48c2fb4 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 9 Nov 2022 00:48:12 +0100 Subject: [PATCH 119/157] Fix test scripts to test on .NET 7 runtime (#873) --- test.cmd | 2 ++ test.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test.cmd b/test.cmd index 93fb641a6..97648ec5c 100644 --- a/test.cmd +++ b/test.cmd @@ -8,6 +8,8 @@ goto :EOF setlocal call build ^ && call :clean ^ + && call :test net7.0 Debug ^ + && call :test net7.0 Release ^ && call :test net6.0 Debug ^ && call :test net6.0 Release ^ && call :test netcoreapp3.1 Debug ^ diff --git a/test.sh b/test.sh index c581713b6..772991688 100755 --- a/test.sh +++ b/test.sh @@ -10,7 +10,7 @@ if [[ -z "$1" ]]; then else configs="$1" fi -for f in netcoreapp3.1 net6.0; do +for f in netcoreapp3.1 net6.0 net7.0; do for c in $configs; do dotnet test --no-build -c $c -f $f --settings MoreLinq.Test/coverlet.runsettings MoreLinq.Test TEST_RESULTS_DIR="$(ls -dc MoreLinq.Test/TestResults/* | head -1)" From 947ad718aacdbcee5636f1362a935fcc6aa467fc Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 9 Nov 2022 07:50:04 +0100 Subject: [PATCH 120/157] Allow use of C# 11 (#874) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 33d490cdb..f958a257d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 10 + 11 true From fc3ee2f37051f4be79f5a9baa06d9bb3cf4a265b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 9 Nov 2022 07:53:33 +0100 Subject: [PATCH 121/157] Replace "MayBeNull" with "?" in "IExtremaEnumerable<>" members (#872) This is a squashed merge of PR #872 that adds to #803. --- MoreLinq.Test/MaxByTest.cs | 2 ++ MoreLinq.Test/MinByTest.cs | 2 ++ MoreLinq/Extensions.g.cs | 9 +++------ MoreLinq/MaxBy.cs | 10 +++------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 737d10e93..6480e932e 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 934e49dd5..79d2da26e 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index bead709d2..1a77a18e6 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -1907,8 +1907,7 @@ public static partial class FirstOrDefaultExtension /// otherwise, the first element in source. /// - [return: MaybeNull] - public static T FirstOrDefault(this IExtremaEnumerable source) + public static T? FirstOrDefault(this IExtremaEnumerable source) => MoreEnumerable.FirstOrDefault(source); } @@ -3114,8 +3113,7 @@ public static partial class LastOrDefaultExtension /// otherwise, the last element in source. /// - [return: MaybeNull] - public static T LastOrDefault(this IExtremaEnumerable source) + public static T? LastOrDefault(this IExtremaEnumerable source) => MoreEnumerable.LastOrDefault(source); } @@ -5178,8 +5176,7 @@ public static partial class SingleOrDefaultExtension /// if the sequence contains no elements. /// - [return: MaybeNull] - public static T SingleOrDefault(this IExtremaEnumerable source) + public static T? SingleOrDefault(this IExtremaEnumerable source) => MoreEnumerable.SingleOrDefault(source); } diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index 3752c1645..b9dbb1ac6 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -20,7 +20,6 @@ namespace MoreLinq using System; using System.Collections; using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; using System.Linq; /// @@ -88,8 +87,7 @@ public static T First(this IExtremaEnumerable source) /// otherwise, the first element in source. /// - [return: MaybeNull] - public static T FirstOrDefault(this IExtremaEnumerable source) + public static T? FirstOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.Take(1).AsEnumerable().FirstOrDefault(); @@ -125,8 +123,7 @@ public static T Last(this IExtremaEnumerable source) /// otherwise, the last element in source. /// - [return: MaybeNull] - public static T LastOrDefault(this IExtremaEnumerable source) + public static T? LastOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.TakeLast(1).AsEnumerable().LastOrDefault(); @@ -164,8 +161,7 @@ public static T Single(this IExtremaEnumerable source) /// if the sequence contains no elements. /// - [return: MaybeNull] - public static T SingleOrDefault(this IExtremaEnumerable source) + public static T? SingleOrDefault(this IExtremaEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); return source.Take(2).AsEnumerable().SingleOrDefault(); From a39672ce0b37a3056777e6634d71e23dc38296d6 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 9 Nov 2022 09:09:37 +0100 Subject: [PATCH 122/157] Enable nullable context in "Flatten" tests Adds to #803. --- MoreLinq.Test/FlattenTest.cs | 16 +++++++++------- MoreLinq.Test/MoreLinq.Test.csproj | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index f3b5b4a6c..84b9c0a93 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System.Collections.Generic; @@ -395,7 +397,7 @@ public void FlattenSelectorWithTree() var result = new[] { source }.Flatten(obj => obj switch { int => null, - Tree tree => new object[] { tree.Left, tree.Value, tree.Right }, + Tree tree => new object?[] { tree.Left, tree.Value, tree.Right }, IEnumerable inner => inner, _ => Enumerable.Empty() }); @@ -407,23 +409,23 @@ public void FlattenSelectorWithTree() class Series { - public string Name; - public Attribute[] Attributes; + public required string Name; + public required Attribute[] Attributes; } class Attribute { - public int[] Values; + public required int[] Values; } class Tree { public readonly T Value; - public readonly Tree Left; - public readonly Tree Right; + public readonly Tree? Left; + public readonly Tree? Right; public Tree(T value) : this(null, value, null) {} - public Tree(Tree left, T value, Tree right) + public Tree(Tree? left, T value, Tree? right) { Left = left; Value = value; diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 470cfcab3..273866c9b 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -35,6 +35,7 @@ + From 382bcf86b668a81c861e3f5012017eaaf1f8ae63 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 11 Nov 2022 12:33:16 +0100 Subject: [PATCH 123/157] Update ReportGenerator tool to 5.1.11 (#876) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index fb2643e5b..1434359fd 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "4.6.4", + "version": "5.1.11", "commands": [ "reportgenerator" ] From ee8abebfb3b2b9b9b984651c72af32d59f3b1159 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 12 Nov 2022 12:34:08 +0100 Subject: [PATCH 124/157] Enable nullable context for extensions generator (#878) --- .../MoreLinq.ExtensionsGenerator.csproj | 1 + bld/ExtensionsGenerator/Program.cs | 31 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index e1c9c8e49..ee274ddfb 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -3,6 +3,7 @@ Exe net7.0 false + enable diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index cd5b864d4..eda9301f1 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -42,8 +42,8 @@ static void Run(IEnumerable args) { var dir = Directory.GetCurrentDirectory(); - string includePattern = null; - string excludePattern = null; + string? includePattern = null; + string? excludePattern = null; var debug = false; var usings = new List(); var noClassLead = false; @@ -88,7 +88,7 @@ static Exception MissingArgValue() => } static Func - PredicateFromPattern(string pattern, bool @default) => + PredicateFromPattern(string? pattern, bool @default) => string.IsNullOrEmpty(pattern) ? delegate { return @default; } : new Func(new Regex(pattern).IsMatch); @@ -132,7 +132,7 @@ from cd in .SyntaxTree .GetCompilationUnitRoot() .DescendantNodes().OfType() - where (string) cd.Identifier.Value == "MoreEnumerable" + where cd.Identifier.Value is "MoreEnumerable" // // Get all method declarations where method: // @@ -142,10 +142,9 @@ from cd in // - isn't marked as being obsolete // from md in cd.DescendantNodes().OfType() - let mn = (string) md.Identifier.Value where md.ParameterList.Parameters.Count > 0 - && md.ParameterList.Parameters.First().Modifiers.Any(m => (string)m.Value == "this") - && md.Modifiers.Any(m => (string)m.Value == "public") + && md.ParameterList.Parameters.First().Modifiers.Any(m => m.Value is "this") + && md.Modifiers.Any(m => m.Value is "public") && md.AttributeLists.SelectMany(al => al.Attributes).All(a => a.Name.ToString() != "Obsolete") // // Build a dictionary of type abbreviations (e.g. TSource -> a, @@ -172,7 +171,7 @@ where md.ParameterList.Parameters.Count > 0 ParameterCount = md.ParameterList.Parameters.Count, SortableParameterTypes = from p in md.ParameterList.Parameters - select CreateTypeKey(p.Type, + select CreateTypeKey(p.Type ?? throw new NullReferenceException(), n => typeParameterAbbreviationByName is { } someTypeParameterAbbreviationByName && someTypeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), } @@ -251,7 +250,8 @@ from ns in baseImports.Concat(usings) var classes = from md in q select md.Method.Syntax into md - group md by (string) md.Identifier.Value into g + group md by md.Identifier.Value is string id ? id : throw new NullReferenceException() + into g select new { Name = g.Key, @@ -334,8 +334,7 @@ namespace MoreLinq.Extensions .Replace("\n", Environment.NewLine)); } -static TypeKey CreateTypeKey(TypeSyntax root, - Func abbreviator = null) +static TypeKey CreateTypeKey(TypeSyntax root, Func abbreviator) { return Walk(root ?? throw new ArgumentNullException(nameof(root))); @@ -359,12 +358,8 @@ select Walk(te.Type))), }; } -static T Read(IEnumerator e, Func errorFactory = null) -{ - if (!e.MoveNext()) - throw errorFactory?.Invoke() ?? new InvalidOperationException(); - return e.Current; -} +static T Read(IEnumerator e, Func errorFactory) => + e.MoveNext() ? e.Current : throw errorFactory(); // // Logical type nodes designed to be structurally sortable based on: @@ -382,7 +377,7 @@ abstract class TypeKey : IComparable public string Name { get; } public abstract ImmutableList Parameters { get; } - public virtual int CompareTo(TypeKey other) + public virtual int CompareTo(TypeKey? other) => ReferenceEquals(this, other) ? 0 : other == null ? 1 : Parameters.Count.CompareTo(other.Parameters.Count) is var lc and not 0 ? lc From c6ca90dc1ae3113434bf9cbee8b555b54ce37b2b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 12 Nov 2022 14:16:20 +0100 Subject: [PATCH 125/157] Sync "Lookup" + "Grouping" from .NET 7 sources (#879) See also: - https://github.com/dotnet/runtime/blob/v7.0.0/src/libraries/System.Linq/src/System/Linq/Lookup.cs - https://github.com/dotnet/runtime/blob/v7.0.0/src/libraries/System.Linq/src/System/Linq/Grouping.cs#L48-L141 --- MoreLinq/Lookup.cs | 316 ++++++++++++++++++++++----------------------- 1 file changed, 154 insertions(+), 162 deletions(-) diff --git a/MoreLinq/Lookup.cs b/MoreLinq/Lookup.cs index 5373f28b7..8ee430a64 100644 --- a/MoreLinq/Lookup.cs +++ b/MoreLinq/Lookup.cs @@ -24,257 +24,249 @@ // SOFTWARE. #endregion -#nullable disable +#if !NET6_0_OR_GREATER +#nullable enable annotations +#pragma warning disable 8602 // Dereference of a possibly null reference. +#pragma warning disable 8603 // Possible null reference return. +#endif namespace MoreLinq { using System; using System.Collections; using System.Collections.Generic; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Linq; /// /// A implementation that preserves insertion order /// - /// The type of the keys in the - /// The type of the elements in the sequences that make up the values in the /// - /// This implementation preserves insertion order of keys and elements within each - /// Copied over from CoreFX on 2015-10-27 - /// https://github.com/dotnet/corefx/blob/6f1c2a86fb8fa1bdaee7c6e70a684d27842d804c/src/System.Linq/src/System/Linq/Enumerable.cs#L3230-L3403 - /// Modified to remove internal interfaces + /// This implementation preserves insertion order of keys and elements within each . Copied and modified from + /// Lookup.cs /// - internal class Lookup : IEnumerable>, ILookup + + [DebuggerDisplay("Count = {Count}")] + internal sealed class Lookup : ILookup { - private IEqualityComparer _comparer; - private Grouping[] _groupings; - private Grouping _lastGrouping; - private int _count; + readonly IEqualityComparer _comparer; + Grouping[] _groupings; + Grouping? _lastGrouping; + int _count; - internal static Lookup Create(IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + internal static Lookup Create(IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer? comparer) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector)); - Lookup lookup = new Lookup(comparer); - foreach (TSource item in source) { - lookup.GetGrouping(keySelector(item), true).Add(elementSelector(item)); - } + Debug.Assert(source is not null); + Debug.Assert(keySelector is not null); + Debug.Assert(elementSelector is not null); + + var lookup = new Lookup(comparer); + + foreach (var item in source) + lookup.GetGrouping(keySelector(item), create: true)!.Add(elementSelector(item)); + return lookup; } - internal static Lookup CreateForJoin(IEnumerable source, Func keySelector, IEqualityComparer comparer) + internal static Lookup Create(IEnumerable source, Func keySelector, IEqualityComparer? comparer) { - Lookup lookup = new Lookup(comparer); - foreach (TElement item in source) { - TKey key = keySelector(item); - if (key != null) lookup.GetGrouping(key, true).Add(item); - } + Debug.Assert(source is not null); + Debug.Assert(keySelector is not null); + + var lookup = new Lookup(comparer); + + foreach (var item in source) + lookup.GetGrouping(keySelector(item), create: true)!.Add(item); + return lookup; } - private Lookup(IEqualityComparer comparer) + internal static Lookup CreateForJoin(IEnumerable source, Func keySelector, IEqualityComparer? comparer) { - if (comparer == null) comparer = EqualityComparer.Default; - _comparer = comparer; - _groupings = new Grouping[7]; + var lookup = new Lookup(comparer); + + foreach (var item in source) + { + if (keySelector(item) is { } key) + lookup.GetGrouping(key, create: true)!.Add(item); + } + + return lookup; } - public int Count + Lookup(IEqualityComparer? comparer) { - get { return _count; } + _comparer = comparer ?? EqualityComparer.Default; + _groupings = new Grouping[7]; } + public int Count => _count; + public IEnumerable this[TKey key] { get { - Grouping grouping = GetGrouping(key, false); - if (grouping != null) return grouping; - return Enumerable.Empty(); + var grouping = GetGrouping(key, create: false); + return grouping ?? Enumerable.Empty(); } } - public bool Contains(TKey key) - { - return _count > 0 && GetGrouping(key, false) != null; - } + public bool Contains(TKey key) => GetGrouping(key, create: false) is not null; public IEnumerator> GetEnumerator() { - Grouping g = _lastGrouping; - if (g != null) { - do { - g = g.next; - yield return g; - } while (g != _lastGrouping); - } - } + var g = _lastGrouping; + if (g is not null) + { + do + { + g = g._next; - public IEnumerable ApplyResultSelector(Func, TResult> resultSelector) - { - Grouping g = _lastGrouping; - if (g != null) { - do { - g = g.next; - if (g.count != g.elements.Length) { Array.Resize(ref g.elements, g.count); } - yield return resultSelector(g.key, g.elements); - } while (g != _lastGrouping); + Debug.Assert(g is not null); + yield return g; + } + while (g != _lastGrouping); } } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal int InternalGetHashCode(TKey key) - { + int InternalGetHashCode(TKey key) => // Handle comparer implementations that throw when passed null - return (key == null) ? 0 : _comparer.GetHashCode(key) & 0x7FFFFFFF; - } + key is null ? 0 : _comparer.GetHashCode(key) & 0x7FFFFFFF; - internal Grouping GetGrouping(TKey key, bool create) + internal Grouping? GetGrouping(TKey key, bool create) { - int hashCode = InternalGetHashCode(key); - for (Grouping g = _groupings[hashCode % _groupings.Length]; g != null; g = g.hashNext) - if (g.hashCode == hashCode && _comparer.Equals(g.key, key)) return g; - if (create) { - if (_count == _groupings.Length) Resize(); - int index = hashCode % _groupings.Length; - Grouping g = new Grouping(); - g.key = key; - g.hashCode = hashCode; - g.elements = new TElement[1]; - g.hashNext = _groupings[index]; + var hashCode = InternalGetHashCode(key); + for (var g = _groupings[hashCode % _groupings.Length]; g is not null; g = g._hashNext) + { + if (g._hashCode == hashCode && _comparer.Equals(g._key, key)) + return g; + } + + if (create) + { + if (_count == _groupings.Length) + { + Resize(); + } + + var index = hashCode % _groupings.Length; + var g = new Grouping(key, hashCode); + g._hashNext = _groupings[index]; _groupings[index] = g; - if (_lastGrouping == null) { - g.next = g; + if (_lastGrouping is null) + { + g._next = g; } - else { - g.next = _lastGrouping.next; - _lastGrouping.next = g; + else + { + g._next = _lastGrouping._next; + _lastGrouping._next = g; } + _lastGrouping = g; _count++; return g; } + return null; } - private void Resize() + void Resize() { - int newSize = checked(_count * 2 + 1); - Grouping[] newGroupings = new Grouping[newSize]; - Grouping g = _lastGrouping; - do { - g = g.next; - int index = g.hashCode % newSize; - g.hashNext = newGroupings[index]; + var newSize = checked((_count * 2) + 1); + var newGroupings = new Grouping[newSize]; + var g = _lastGrouping!; + do + { + g = g._next!; + var index = g._hashCode % newSize; + g._hashNext = newGroupings[index]; newGroupings[index] = g; - } while (g != _lastGrouping); + } + while (g != _lastGrouping); + _groupings = newGroupings; } } - internal class Grouping : IGrouping, IList + // Modified from: + // https://github.com/dotnet/runtime/blob/v7.0.0/src/libraries/System.Linq/src/System/Linq/Grouping.cs#L48-L141 + + [DebuggerDisplay("Key = {Key}")] + internal sealed class Grouping : IGrouping, IList { - internal TKey key; - internal int hashCode; - internal TElement[] elements; - internal int count; - internal Grouping hashNext; - internal Grouping next; - - internal Grouping() + internal readonly TKey _key; + internal readonly int _hashCode; + internal TElement[] _elements; + internal int _count; + internal Grouping? _hashNext; + internal Grouping? _next; + + internal Grouping(TKey key, int hashCode) { + _key = key; + _hashCode = hashCode; + _elements = new TElement[1]; } internal void Add(TElement element) { - if (elements.Length == count) Array.Resize(ref elements, checked(count * 2)); - elements[count] = element; - count++; - } + if (_elements.Length == _count) + Array.Resize(ref _elements, checked(_count * 2)); - public IEnumerator GetEnumerator() - { - for (int i = 0; i < count; i++) yield return elements[i]; + _elements[_count] = element; + _count++; } - IEnumerator IEnumerable.GetEnumerator() + internal void Trim() { - return GetEnumerator(); + if (_elements.Length != _count) + Array.Resize(ref _elements, _count); } - // DDB195907: implement IGrouping<>.Key implicitly - // so that WPF binding works on this property. - public TKey Key + public IEnumerator GetEnumerator() { - get { return key; } + for (var i = 0; i < _count; i++) + yield return _elements[i]; } - int ICollection.Count - { - get { return count; } - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - bool ICollection.IsReadOnly - { - get { return true; } - } + // DDB195907: implement IGrouping<>.Key implicitly + // so that WPF binding works on this property. + public TKey Key => _key; - void ICollection.Add(TElement item) - { - throw new NotSupportedException("Lookup is immutable"); - } + int ICollection.Count => _count; - void ICollection.Clear() - { - throw new NotSupportedException("Lookup is immutable"); - } + bool ICollection.IsReadOnly => true; - bool ICollection.Contains(TElement item) - { - return Array.IndexOf(elements, item, 0, count) >= 0; - } + bool ICollection.Contains(TElement item) => Array.IndexOf(_elements, item, 0, _count) >= 0; - void ICollection.CopyTo(TElement[] array, int arrayIndex) - { - Array.Copy(elements, 0, array, arrayIndex, count); - } + void ICollection.CopyTo(TElement[] array, int arrayIndex) => + Array.Copy(_elements, 0, array, arrayIndex, _count); - bool ICollection.Remove(TElement item) - { - throw new NotSupportedException("Lookup is immutable"); - } + int IList.IndexOf(TElement item) => Array.IndexOf(_elements, item, 0, _count); - int IList.IndexOf(TElement item) + TElement IList.this[int index] { - return Array.IndexOf(elements, item, 0, count); - } + get => index < 0 || index >= _count + ? throw new ArgumentOutOfRangeException(nameof(index)) + : _elements[index]; - void IList.Insert(int index, TElement item) - { - throw new NotSupportedException("Lookup is immutable"); + set => ThrowModificationNotSupportedException(); } - void IList.RemoveAt(int index) - { - throw new NotSupportedException("Lookup is immutable"); - } + void ICollection.Add(TElement item) => ThrowModificationNotSupportedException(); + void ICollection.Clear() => ThrowModificationNotSupportedException(); + bool ICollection.Remove(TElement item) { ThrowModificationNotSupportedException(); return false; } + void IList.Insert(int index, TElement item) => ThrowModificationNotSupportedException(); + void IList.RemoveAt(int index) => ThrowModificationNotSupportedException(); - TElement IList.this[int index] - { - get - { - if (index < 0 || index >= count) throw new ArgumentOutOfRangeException(nameof(index)); - return elements[index]; - } - set - { - throw new NotSupportedException("Lookup is immutable"); - } - } + [DoesNotReturn] + static void ThrowModificationNotSupportedException() => throw new NotSupportedException("Grouping is immutable."); } } From ba487ddc4e9b4767fa681329d59445dd425ca2f1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 13 Nov 2022 18:55:43 +0100 Subject: [PATCH 126/157] "Batch" overloads using an array pool This is a squashed merge of PR #856 that supersedes #833. Co-authored-by: Stuart Turner --- MoreLinq.Test/BatchTest.cs | 272 ++++++++++++++++++++++- MoreLinq/Experimental/Batch.cs | 294 +++++++++++++++++++++++++ MoreLinq/Experimental/CurrentBuffer.cs | 107 +++++++++ MoreLinq/MoreLinq.csproj | 8 +- README.md | 2 +- 5 files changed, 669 insertions(+), 14 deletions(-) create mode 100644 MoreLinq/Experimental/Batch.cs create mode 100644 MoreLinq/Experimental/CurrentBuffer.cs diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index c5c9186e7..a459ee2dd 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -23,18 +23,12 @@ namespace MoreLinq.Test [TestFixture] public class BatchTest { - [Test] - public void BatchZeroSize() - { - AssertThrowsArgument.OutOfRangeException("size",() => - new object[0].Batch(0)); - } - - [Test] - public void BatchNegativeSize() + [TestCase(0)] + [TestCase(-1)] + public void BatchBadSize(int size) { - AssertThrowsArgument.OutOfRangeException("size",() => - new object[0].Batch(-1)); + AssertThrowsArgument.OutOfRangeException("size", () => + new object[0].Batch(size)); } [Test] @@ -141,3 +135,259 @@ public void BatchEmptySource(SourceKind kind) } } } + +#if NETCOREAPP3_1_OR_GREATER + +namespace MoreLinq.Test +{ + using System; + using System.Buffers; + using System.Collections.Generic; + using MoreLinq.Experimental; + using NUnit.Framework; + + [TestFixture] + public class BatchPoolTest + { + [TestCase(0)] + [TestCase(-1)] + public void BatchBadSize(int size) + { + AssertThrowsArgument.OutOfRangeException("size", () => + new object[0].Batch(size, ArrayPool.Shared, + BreakingFunc.Of, IEnumerable>(), + BreakingFunc.Of, object>())); + } + + [Test] + public void BatchEvenlyDivisibleSequence() + { + using var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); + using var pool = new TestArrayPool(); + + var result = input.Batch(3, pool, Enumerable.ToArray); + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3); + reader.Read().AssertSequenceEqual(4, 5, 6); + reader.Read().AssertSequenceEqual(7, 8, 9); + reader.ReadEnd(); + } + + [Test] + public void BatchUnevenlyDivisibleSequence() + { + using var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); + using var pool = new TestArrayPool(); + + var result = input.Batch(4, pool, Enumerable.ToArray); + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3, 4); + reader.Read().AssertSequenceEqual(5, 6, 7, 8); + reader.Read().AssertSequenceEqual(9); + reader.ReadEnd(); + } + + [Test] + public void BatchIsLazy() + { + var input = new BreakingSequence(); + _ = input.Batch(1, ArrayPool.Shared, + BreakingFunc.Of, IEnumerable>(), + BreakingFunc.Of, object>()); + } + + [TestCase(SourceKind.BreakingList , 0)] + [TestCase(SourceKind.BreakingReadOnlyList, 0)] + [TestCase(SourceKind.BreakingList , 1)] + [TestCase(SourceKind.BreakingReadOnlyList, 1)] + [TestCase(SourceKind.BreakingList , 2)] + [TestCase(SourceKind.BreakingReadOnlyList, 2)] + public void BatchCollectionSmallerThanSize(SourceKind kind, int oversize) + { + var xs = new[] { 1, 2, 3, 4, 5 }; + using var pool = new TestArrayPool(); + + var result = xs.ToSourceKind(kind) + .Batch(xs.Length + oversize, pool, Enumerable.ToArray); + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); + reader.ReadEnd(); + } + + [Test] + public void BatchReadOnlyCollectionSmallerThanSize() + { + var collection = ReadOnlyCollection.From(1, 2, 3, 4, 5); + using var pool = new TestArrayPool(); + + var result = collection.Batch(collection.Count * 2, pool, + Enumerable.ToArray); + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); + reader.ReadEnd(); + } + + [TestCase(SourceKind.Sequence)] + [TestCase(SourceKind.BreakingList)] + [TestCase(SourceKind.BreakingReadOnlyList)] + [TestCase(SourceKind.BreakingReadOnlyCollection)] + [TestCase(SourceKind.BreakingCollection)] + public void BatchEmptySource(SourceKind kind) + { + using var pool = new TestArrayPool(); + + var result = Enumerable.Empty() + .ToSourceKind(kind) + .Batch(100, pool, Enumerable.ToArray); + + Assert.That(result, Is.Empty); + } + + [Test] + public void BatchFilterBucket() + { + const int scale = 2; + var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); + using var pool = new TestArrayPool(); + + var result = input.Batch(3, pool, + current => from n in current + where n % 2 == 0 + select n * scale, + Enumerable.ToArray); + + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(2 * scale); + reader.Read().AssertSequenceEqual(4 * scale, 6 * scale); + reader.Read().AssertSequenceEqual(8 * scale); + reader.ReadEnd(); + } + + [Test] + public void BatchSumBucket() + { + var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); + using var pool = new TestArrayPool(); + + var result = input.Batch(3, pool, Enumerable.Sum); + + using var reader = result.Read(); + Assert.That(reader.Read(), Is.EqualTo(1 + 2 + 3)); + Assert.That(reader.Read(), Is.EqualTo(4 + 5 + 6)); + Assert.That(reader.Read(), Is.EqualTo(7 + 8 + 9)); + reader.ReadEnd(); + } + + /// + /// This test does not exercise the intended usage! + /// + + [Test] + public void BatchUpdatesCurrentListInPlace() + { + var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); + using var pool = new TestArrayPool(); + + var result = input.Batch(4, pool, current => current, current => (ICurrentBuffer)current); + + using var reader = result.Read(); + var current = reader.Read(); + current.AssertSequenceEqual(1, 2, 3, 4); + _ = reader.Read(); + current.AssertSequenceEqual(5, 6, 7, 8); + _ = reader.Read(); + current.AssertSequenceEqual(9); + + reader.ReadEnd(); + + Assert.That(current, Is.Empty); + } + + [Test] + public void BatchCallsBucketSelectorBeforeIteratingSource() + { + var iterations = 0; + IEnumerable Source() + { + iterations++; + yield break; + } + + var input = Source(); + using var pool = new TestArrayPool(); + var initIterations = -1; + + var result = input.Batch(4, pool, + current => + { + initIterations = iterations; + return current; + }, + _ => 0); + + using var enumerator = result.GetEnumerator(); + Assert.That(enumerator.MoveNext(), Is.False); + Assert.That(initIterations, Is.Zero); + } + + [Test] + public void BatchBucketSelectorCurrentList() + { + var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); + using var pool = new TestArrayPool(); + int[] bucketSelectorItems = null; + + var result = input.Batch(4, pool, current => bucketSelectorItems = current.ToArray(), _ => 0); + + using var reader = result.Read(); + _ = reader.Read(); + Assert.That(bucketSelectorItems, Is.Not.Null); + Assert.That(bucketSelectorItems, Is.Empty); + } + + /// + /// An implementation for testing purposes that holds only + /// one array in the pool and ensures that it is returned when the pool is disposed. + /// + + sealed class TestArrayPool : ArrayPool, IDisposable + { + T[] _pooledArray; + T[] _rentedArray; + + public override T[] Rent(int minimumLength) + { + if (_pooledArray is null && _rentedArray is null) + _pooledArray = new T[minimumLength * 2]; + + if (_pooledArray is null) + throw new InvalidOperationException("The pool is exhausted."); + + (_pooledArray, _rentedArray) = (null, _pooledArray); + + return _rentedArray; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (_rentedArray is null) + throw new InvalidOperationException("Cannot return when nothing has been rented from this pool."); + + if (array != _rentedArray) + throw new InvalidOperationException("Cannot return what has not been rented from this pool."); + + _pooledArray = array; + _rentedArray = null; + } + + public void Dispose() => + Assert.That(_rentedArray, Is.Null); + } + } +} + +#endif // NETCOREAPP3_1_OR_GREATER diff --git a/MoreLinq/Experimental/Batch.cs b/MoreLinq/Experimental/Batch.cs new file mode 100644 index 000000000..ae40954ff --- /dev/null +++ b/MoreLinq/Experimental/Batch.cs @@ -0,0 +1,294 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2022 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +#if !NO_BUFFERS + +namespace MoreLinq.Experimental +{ + using System; + using System.Buffers; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + static partial class ExperimentalEnumerable + { + /// + /// Batches the source sequence into sized buckets using an array pool + /// to rent arrays to back each bucket and returns a sequence of + /// elements projected from each bucket. + /// + /// + /// Type of elements in sequence. + /// + /// Type of elements of the resulting sequence. + /// + /// The source sequence. + /// Size of buckets. + /// The pool used to rent the array for each bucket. + /// A function that projects a result from + /// the current bucket. + /// + /// A sequence whose elements are projected from each bucket (returned by + /// ). + /// + /// + /// + /// This operator uses deferred execution and streams its results + /// (however, each bucket provided to is + /// buffered). + /// + /// + /// Each bucket is backed by a rented array that may be at least + /// in length. + /// + /// + /// When more than one bucket is produced, all buckets except the last + /// is guaranteed to have elements. The last + /// bucket may be smaller depending on the remaining elements in the + /// sequence. + /// Each bucket is pre-allocated to elements. + /// If is set to a very large value, e.g. + /// to effectively disable batching by just + /// hoping for a single bucket, then it can lead to memory exhaustion + /// (). + /// + /// + + public static IEnumerable + Batch(this IEnumerable source, int size, + ArrayPool pool, + Func, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (pool == null) throw new ArgumentNullException(nameof(pool)); + if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return source.Batch(size, pool, current => current, + current => resultSelector((ICurrentBuffer)current)); + } + + /// + /// Batches the source sequence into sized buckets using an array pool + /// to rent arrays to back each bucket and returns a sequence of + /// elements projected from each bucket. + /// + /// + /// Type of elements in sequence. + /// + /// Type of elements in the sequence returned by . + /// + /// Type of elements of the resulting sequence. + /// + /// The source sequence. + /// Size of buckets. + /// The pool used to rent the array for each bucket. + /// A function that returns a + /// sequence projection to use for each bucket. It is called initially + /// before iterating over , but the resulting + /// projection is evaluated for each bucket. This has the same effect as + /// calling for each bucket, + /// but allows initialization of the transformation to happen only once. + /// + /// A function that projects a result from + /// the input sequence produced over a bucket. + /// + /// A sequence whose elements are projected from each bucket (returned by + /// ). + /// + /// + /// + /// This operator uses deferred execution and streams its results + /// (however, each bucket is buffered). + /// + /// + /// Each bucket is backed by a rented array that may be at least + /// in length. + /// + /// + /// When more than one bucket is produced, all buckets except the last + /// is guaranteed to have elements. The last + /// bucket may be smaller depending on the remaining elements in the + /// sequence. + /// Each bucket is pre-allocated to elements. + /// If is set to a very large value, e.g. + /// to effectively disable batching by just + /// hoping for a single bucket, then it can lead to memory exhaustion + /// (). + /// + /// + + public static IEnumerable + Batch( + this IEnumerable source, int size, ArrayPool pool, + Func, IEnumerable> bucketProjectionSelector, + Func, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (pool == null) throw new ArgumentNullException(nameof(pool)); + if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size)); + if (bucketProjectionSelector == null) throw new ArgumentNullException(nameof(bucketProjectionSelector)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var batch = source.Batch(size, pool); + var bucket = bucketProjectionSelector(batch.CurrentBuffer); + while (batch.UpdateWithNext()) + yield return resultSelector(bucket); + } + } + + static ICurrentBufferProvider + Batch(this IEnumerable source, int size, ArrayPool pool) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (pool == null) throw new ArgumentNullException(nameof(pool)); + if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size)); + + ICurrentBufferProvider Cursor(IEnumerator<(T[], int)> source) => + new CurrentPoolArrayProvider(source, pool); + + switch (source) + { + case ICollection { Count: 0 }: + { + return Cursor(Enumerable.Empty <(T[], int)>().GetEnumerator()); + } + case ICollection collection when collection.Count <= size: + { + var bucket = pool.Rent(collection.Count); + collection.CopyTo(bucket, 0); + return Cursor(MoreEnumerable.Return((bucket, collection.Count)).GetEnumerator()); + } + case IReadOnlyCollection { Count: 0 }: + { + return Cursor(Enumerable.Empty <(T[], int)>().GetEnumerator()); + } + case IReadOnlyList list when list.Count <= size: + { + return Cursor(_()); IEnumerator<(T[], int)> _() + { + var bucket = pool.Rent(list.Count); + for (var i = 0; i < list.Count; i++) + bucket[i] = list[i]; + yield return (bucket, list.Count); + } + } + case IReadOnlyCollection collection when collection.Count <= size: + { + return Cursor(Batch(collection.Count)); + } + default: + { + return Cursor(Batch(size)); + } + } + + IEnumerator<(T[], int)> Batch(int size) + { + T[]? bucket = null; + var count = 0; + + foreach (var item in source) + { + bucket ??= pool.Rent(size); + bucket[count++] = item; + + // The bucket is fully buffered before it's yielded + if (count != size) + continue; + + yield return (bucket, size); + + bucket = null; + count = 0; + } + + // Return the last bucket with all remaining elements + if (bucket is { } someBucket && count > 0) + yield return (someBucket, count); + } + } + + sealed class CurrentPoolArrayProvider : CurrentBuffer, ICurrentBufferProvider + { + bool _rented; + T[] _array = Array.Empty(); + int _count; + IEnumerator<(T[], int)>? _rental; + ArrayPool? _pool; + + public CurrentPoolArrayProvider(IEnumerator<(T[], int)> rental, ArrayPool pool) => + (_rental, _pool) = (rental, pool); + + ICurrentBuffer ICurrentBufferProvider.CurrentBuffer => this; + + public bool UpdateWithNext() + { + if (_rental is { Current: var (array, _) } rental) + { + Debug.Assert(_pool is not null); + if (_rented) + { + _pool.Return(array); + _rented = false; + } + + if (!rental.MoveNext()) + { + Dispose(); + return false; + } + + _rented = true; + (_array, _count) = rental.Current; + return true; + } + + return false; + } + + public override int Count => _count; + + public override T this[int index] + { + get => index >= 0 && index < Count ? _array[index] : throw new IndexOutOfRangeException(); + set => throw new NotSupportedException(); + + } + + public void Dispose() + { + if (_rental is { Current: var (array, _) } enumerator) + { + Debug.Assert(_pool is not null); + if (_rented) + _pool.Return(array); + enumerator.Dispose(); + _array = Array.Empty(); + _count = 0; + _rental = null; + _pool = null; + } + } + } + } +} + +#endif // !NO_BUFFERS diff --git a/MoreLinq/Experimental/CurrentBuffer.cs b/MoreLinq/Experimental/CurrentBuffer.cs new file mode 100644 index 000000000..e6cb526a1 --- /dev/null +++ b/MoreLinq/Experimental/CurrentBuffer.cs @@ -0,0 +1,107 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2022 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +#if !NO_BUFFERS + +namespace MoreLinq.Experimental +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + /// + /// Represents a current buffered view of a larger result and which + /// is updated in-place (thus current) as it is moved through the overall + /// result. + /// + /// Type of elements in the list. + + public interface ICurrentBuffer : IList { } + + /// + /// A provider of current buffer that updates it in-place. + /// + /// Type of elements in the list. + + interface ICurrentBufferProvider : IDisposable + { + /// + /// Gets the current items of the list. + /// + /// + /// The returned list is updated in-place when + /// is called. + /// + + ICurrentBuffer CurrentBuffer { get; } + + /// + /// Update this instance with the next set of elements from the source. + /// + /// + /// A Boolean that is true if this instance was updated with + /// new elements; otherwise false to indicate that the end of + /// the bucket source has been reached. + /// + + bool UpdateWithNext(); + } + + abstract class CurrentBuffer : ICurrentBuffer + { + public abstract int Count { get; } + public abstract T this[int index] { get; set; } + + public virtual bool IsReadOnly => false; + + public virtual int IndexOf(T item) + { + var comparer = EqualityComparer.Default; + + for (var i = 0; i < Count; i++) + { + if (comparer.Equals(this[i], item)) + return i; + } + + return -1; + } + + public virtual bool Contains(T item) => IndexOf(item) >= 0; + + public virtual void CopyTo(T[] array, int arrayIndex) + { + if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, null); + if (arrayIndex + Count > array.Length) throw new ArgumentException(null, nameof(arrayIndex)); + + for (int i = 0, j = arrayIndex; i < Count; i++, j++) + array[j] = this[i]; + } + + public virtual IEnumerator GetEnumerator() => this.Take(Count).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + void IList.Insert(int index, T item) => throw new NotSupportedException(); + void IList.RemoveAt(int index) => throw new NotSupportedException(); + void ICollection.Add(T item) => throw new NotSupportedException(); + void ICollection.Clear() => throw new NotSupportedException(); + bool ICollection.Remove(T item) => throw new NotSupportedException(); + } +} + +#endif // !NO_BUFFERS diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 35e2e7c3f..f428cf3e3 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -119,7 +119,7 @@ en-US 3.3.2 MoreLINQ Developers. - net451;netstandard1.0;netstandard2.0;net6.0 + net451;netstandard1.0;netstandard2.0;netstandard2.1;net6.0 enable @@ -192,8 +192,12 @@ $(DefineConstants);MORELINQ + + $(DefineConstants);NO_BUFFERS + + - $(DefineConstants);NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC + $(DefineConstants);NO_BUFFERS;NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC diff --git a/README.md b/README.md index 420dc324d..0104175ee 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ the third-last element and so on. Batches the source sequence into sized buckets. -This method has 2 overloads. +This method has 4 overloads, 2 of which are experimental. ### Cartesian From bb01dfae71bab856cf90f13cb65c86351becceeb Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Tue, 15 Nov 2022 15:47:59 -0600 Subject: [PATCH 127/157] Enable nullable context for "PartialSort" tests (#888) --- MoreLinq.Test/PartialSortByTest.cs | 2 ++ MoreLinq.Test/PartialSortTest.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/MoreLinq.Test/PartialSortByTest.cs b/MoreLinq.Test/PartialSortByTest.cs index 9c83dcd7c..7330a151c 100644 --- a/MoreLinq.Test/PartialSortByTest.cs +++ b/MoreLinq.Test/PartialSortByTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PartialSortTest.cs b/MoreLinq.Test/PartialSortTest.cs index 04257a737..53ad4d729 100644 --- a/MoreLinq.Test/PartialSortTest.cs +++ b/MoreLinq.Test/PartialSortTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; From d58741681e4743cde3e56686c5c13ce2b1e34e15 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 16 Nov 2022 20:53:58 +0100 Subject: [PATCH 128/157] Fix null annotation in "Subject" (#891) --- MoreLinq/Reactive/Subject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Reactive/Subject.cs b/MoreLinq/Reactive/Subject.cs index 2f01e8047..f1fc91a6a 100644 --- a/MoreLinq/Reactive/Subject.cs +++ b/MoreLinq/Reactive/Subject.cs @@ -116,12 +116,12 @@ public void OnNext(T value) } public void OnError(Exception error) => - OnFinality(ref _error, error, (observer, err) => observer.OnError(err!)); + OnFinality(ref _error, error, (observer, err) => observer.OnError(err)); public void OnCompleted() => OnFinality(ref _completed, true, (observer, _) => observer.OnCompleted()); - void OnFinality(ref TState state, TState value, Action, TState> action) + void OnFinality(ref TState? state, TState value, Action, TState> action) { if (IsMuted) return; From b31c7fdd6b02a77f37e57c0684e6bdcb8b51e5ba Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 16 Nov 2022 23:04:05 +0100 Subject: [PATCH 129/157] Replace null-forgiving operator uses with debug assertions (#890) --- MoreLinq.Test/FallbackIfEmptyTest.cs | 8 +++++++ MoreLinq/Assume.cs | 31 +++++++++++++++++++++++++++ MoreLinq/Debug.cs | 31 +++++++++++++++++++++++++++ MoreLinq/Experimental/Await.cs | 10 +++------ MoreLinq/Experimental/Memoize.cs | 4 +--- MoreLinq/FallbackIfEmpty.cs | 1 - MoreLinq/Fold.cs | 32 ++++++++++++++-------------- MoreLinq/Lookup.cs | 29 +++++++++++++------------ MoreLinq/Pad.cs | 1 - MoreLinq/PartialSort.cs | 2 +- MoreLinq/Partition.cs | 1 - 11 files changed, 106 insertions(+), 44 deletions(-) create mode 100644 MoreLinq/Assume.cs create mode 100644 MoreLinq/Debug.cs diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index b8e6b7516..cf6a2df83 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -60,5 +60,13 @@ public void FallbackIfEmptyPreservesFallbackCollectionIfPossible(SourceKind sour Assert.AreSame(source.FallbackIfEmpty(fallback), fallback); Assert.AreSame(source.FallbackIfEmpty(fallback.AsEnumerable()), fallback); } + + [Test] + public void FallbackIfEmptyWithEmptyNullableSequence() + { + var source = Enumerable.Empty().Select(x => x); + var fallback = (int?)null; + source.FallbackIfEmpty(fallback).AssertSequenceEqual(fallback); + } } } diff --git a/MoreLinq/Assume.cs b/MoreLinq/Assume.cs new file mode 100644 index 000000000..08b60162a --- /dev/null +++ b/MoreLinq/Assume.cs @@ -0,0 +1,31 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2022 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System.Runtime.CompilerServices; + + static class Assume + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T NotNull(T? obj) where T : class + { + Debug.Assert(obj is not null); + return obj; + } + } +} diff --git a/MoreLinq/Debug.cs b/MoreLinq/Debug.cs new file mode 100644 index 000000000..6b36c21c7 --- /dev/null +++ b/MoreLinq/Debug.cs @@ -0,0 +1,31 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2022 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.CompilerServices; + + static class Debug + { + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool condition, + [CallerArgumentExpression(nameof(condition))] string? message = null) => + System.Diagnostics.Debug.Assert(condition); + } +} diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index 9b54d9e35..3873754ea 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -23,7 +23,6 @@ namespace MoreLinq.Experimental using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; - using System.Diagnostics; using System.Linq; using System.Runtime.ExceptionServices; using System.Threading; @@ -528,18 +527,15 @@ void PostNotice(Notice notice, catch (OperationCanceledException e) when (e.CancellationToken == consumerCancellationTokenSource.Token) { var (error1, error2) = lastCriticalErrors; - Debug.Assert(error1 is not null); throw new Exception("One or more critical errors have occurred.", - error2 != null ? new AggregateException(error1, error2) - : new AggregateException(error1)); + error2 != null ? new AggregateException(Assume.NotNull(error1), error2) + : new AggregateException(Assume.NotNull(error1))); } var (kind, result, error) = notice.Current; if (kind == Notice.Error) - { - error!.Throw(); - } + Assume.NotNull(error).Throw(); if (kind == Notice.End) break; diff --git a/MoreLinq/Experimental/Memoize.cs b/MoreLinq/Experimental/Memoize.cs index 0526cec4f..da261b1a2 100644 --- a/MoreLinq/Experimental/Memoize.cs +++ b/MoreLinq/Experimental/Memoize.cs @@ -115,9 +115,7 @@ public IEnumerator GetEnumerator() if (index >= _cache.Count) { if (index == _errorIndex) - { - _error!.Throw(); - } + Assume.NotNull(_error).Throw(); if (_sourceEnumerator == null) break; diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 90256fcad..30a16726e 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; static partial class MoreEnumerable { diff --git a/MoreLinq/Fold.cs b/MoreLinq/Fold.cs index 6698cec9a..31fa1bbef 100644 --- a/MoreLinq/Fold.cs +++ b/MoreLinq/Fold.cs @@ -69,22 +69,22 @@ static TResult FoldImpl(IEnumerable source, int count, return count switch { - 1 => folder1 !(elements[0]), - 2 => folder2 !(elements[0], elements[1]), - 3 => folder3 !(elements[0], elements[1], elements[2]), - 4 => folder4 !(elements[0], elements[1], elements[2], elements[3]), - 5 => folder5 !(elements[0], elements[1], elements[2], elements[3], elements[4]), - 6 => folder6 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]), - 7 => folder7 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6]), - 8 => folder8 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7]), - 9 => folder9 !(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]), - 10 => folder10!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9]), - 11 => folder11!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10]), - 12 => folder12!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11]), - 13 => folder13!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12]), - 14 => folder14!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13]), - 15 => folder15!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14]), - 16 => folder16!(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14], elements[15]), + 1 => Assume.NotNull(folder1 )(elements[0]), + 2 => Assume.NotNull(folder2 )(elements[0], elements[1]), + 3 => Assume.NotNull(folder3 )(elements[0], elements[1], elements[2]), + 4 => Assume.NotNull(folder4 )(elements[0], elements[1], elements[2], elements[3]), + 5 => Assume.NotNull(folder5 )(elements[0], elements[1], elements[2], elements[3], elements[4]), + 6 => Assume.NotNull(folder6 )(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]), + 7 => Assume.NotNull(folder7 )(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6]), + 8 => Assume.NotNull(folder8 )(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7]), + 9 => Assume.NotNull(folder9 )(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]), + 10 => Assume.NotNull(folder10)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9]), + 11 => Assume.NotNull(folder11)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10]), + 12 => Assume.NotNull(folder12)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11]), + 13 => Assume.NotNull(folder13)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12]), + 14 => Assume.NotNull(folder14)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13]), + 15 => Assume.NotNull(folder15)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14]), + 16 => Assume.NotNull(folder16)(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8], elements[9], elements[10], elements[11], elements[12], elements[13], elements[14], elements[15]), _ => throw new NotSupportedException() }; } diff --git a/MoreLinq/Lookup.cs b/MoreLinq/Lookup.cs index 8ee430a64..a25b104ec 100644 --- a/MoreLinq/Lookup.cs +++ b/MoreLinq/Lookup.cs @@ -24,12 +24,6 @@ // SOFTWARE. #endregion -#if !NET6_0_OR_GREATER -#nullable enable annotations -#pragma warning disable 8602 // Dereference of a possibly null reference. -#pragma warning disable 8603 // Possible null reference return. -#endif - namespace MoreLinq { using System; @@ -65,7 +59,10 @@ internal static Lookup Create(IEnumerable sour var lookup = new Lookup(comparer); foreach (var item in source) - lookup.GetGrouping(keySelector(item), create: true)!.Add(elementSelector(item)); + { + var grouping = Assume.NotNull(lookup.GetGrouping(keySelector(item), create: true)); + grouping.Add(elementSelector(item)); + } return lookup; } @@ -78,7 +75,10 @@ internal static Lookup Create(IEnumerable source, Func var lookup = new Lookup(comparer); foreach (var item in source) - lookup.GetGrouping(keySelector(item), create: true)!.Add(item); + { + var grouping = Assume.NotNull(lookup.GetGrouping(keySelector(item), create: true)); + grouping.Add(item); + } return lookup; } @@ -90,7 +90,10 @@ internal static Lookup CreateForJoin(IEnumerable sourc foreach (var item in source) { if (keySelector(item) is { } key) - lookup.GetGrouping(key, create: true)!.Add(item); + { + var grouping = Assume.NotNull(lookup.GetGrouping(key, create: true)); + grouping.Add(item); + } } return lookup; @@ -122,9 +125,7 @@ public IEnumerator> GetEnumerator() { do { - g = g._next; - - Debug.Assert(g is not null); + g = Assume.NotNull(g._next); yield return g; } while (g != _lastGrouping); @@ -179,10 +180,10 @@ void Resize() { var newSize = checked((_count * 2) + 1); var newGroupings = new Grouping[newSize]; - var g = _lastGrouping!; + var g = Assume.NotNull(_lastGrouping); do { - g = g._next!; + g = Assume.NotNull(g._next); var index = g._hashCode % newSize; g._hashNext = newGroupings[index]; newGroupings[index] = g; diff --git a/MoreLinq/Pad.cs b/MoreLinq/Pad.cs index e046b262b..9fc728c35 100644 --- a/MoreLinq/Pad.cs +++ b/MoreLinq/Pad.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; static partial class MoreEnumerable { diff --git a/MoreLinq/PartialSort.cs b/MoreLinq/PartialSort.cs index e8010510f..ecf8b1aec 100644 --- a/MoreLinq/PartialSort.cs +++ b/MoreLinq/PartialSort.cs @@ -242,7 +242,7 @@ static IEnumerable PartialSortByImpl( { if (keys != null) { - var key = keySelector!(item); + var key = Assume.NotNull(keySelector)(item); if (Insert(keys, key, keyComparer) is {} i) { if (top.Count == count) diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 3b67ab6b3..047c48c0f 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; using System.Linq; static partial class MoreEnumerable From 183723aa9b57b04ec2a40bac80debb8d41786043 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sat, 19 Nov 2022 17:24:48 -0600 Subject: [PATCH 130/157] Update "Memoize" documentation (#895) --- MoreLinq/Experimental/Memoize.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/MoreLinq/Experimental/Memoize.cs b/MoreLinq/Experimental/Memoize.cs index da261b1a2..0888ae455 100644 --- a/MoreLinq/Experimental/Memoize.cs +++ b/MoreLinq/Experimental/Memoize.cs @@ -34,17 +34,21 @@ static partial class ExperimentalEnumerable /// Type of elements in . /// The source sequence. /// - /// Returns a sequence that corresponds to a cached version of the - /// input sequence. + /// Returns a sequence that corresponds to a cached version of the input + /// sequence. + /// + /// + /// is . + /// /// /// The returned will cache items from /// in a thread-safe manner. Each thread can /// call its to acquire an /// iterator but the same iterator should not be used simultanesouly - /// from multiple threads. The sequence supplied in - /// is not expected to be thread-safe but it - /// is required to be thread-agnostic because different threads - /// (though never simultaneously) may iterate over the sequence. + /// from multiple threads. The sequence supplied in is not expected to be thread-safe but it is required + /// to be thread-agnostic because different threads (though never + /// simultaneously) may iterate over the sequence. /// public static IEnumerable Memoize(this IEnumerable source) From c3a7094d196df0858f14ad3d000cb2c9a79c6198 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sat, 19 Nov 2022 18:23:30 -0600 Subject: [PATCH 131/157] Add parameter annotations for "Partition" (#897) --- MoreLinq/Partition.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 047c48c0f..2265f96ec 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -101,7 +101,7 @@ public static TResult Partition(this IEnumerable> Func, IEnumerable, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.Partition(true, false, (t, f, _) => resultSelector(t, f)); + return source.Partition(key1: true, key2: false, (t, f, _) => resultSelector(t, f)); } /// @@ -124,7 +124,7 @@ public static TResult Partition(this IEnumerable Func, IEnumerable, IEnumerable, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return source.Partition(true, false, null, (t, f, n, _) => resultSelector(t, f, n)); + return source.Partition(key1: true, key2: false, key3: null, (t, f, n, _) => resultSelector(t, f, n)); } /// @@ -149,7 +149,7 @@ public static TResult Partition(this IEnumerable public static TResult Partition(this IEnumerable> source, TKey key, Func, IEnumerable>, TResult> resultSelector) => - Partition(source, key, null, resultSelector); + Partition(source, key, comparer: null, resultSelector); /// /// Partitions a grouping and projects a result from group elements @@ -177,7 +177,7 @@ public static TResult Partition(this IEnumerable, IEnumerable>, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return PartitionImpl(source, 1, key, default!, default!, comparer, + return PartitionImpl(source, 1, key, key2: default!, key3: default!, comparer, (a, _, _, rest) => resultSelector(a, rest)); } @@ -205,7 +205,7 @@ public static TResult Partition(this IEnumerable(this IEnumerable> source, TKey key1, TKey key2, Func, IEnumerable, IEnumerable>, TResult> resultSelector) => - Partition(source, key1, key2, null, resultSelector); + Partition(source, key1, key2, comparer: null, resultSelector); /// /// Partitions a grouping and projects a result from elements of @@ -235,7 +235,7 @@ public static TResult Partition(this IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return PartitionImpl(source, 2, key1, key2, default!, comparer, + return PartitionImpl(source, 2, key1, key2, key3: default!, comparer, (a, b, _, rest) => resultSelector(a, b, rest)); } @@ -264,7 +264,7 @@ public static TResult Partition(this IEnumerable(this IEnumerable> source, TKey key1, TKey key2, TKey key3, Func, IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) => - Partition(source, key1, key2, key3, null, resultSelector); + Partition(source, key1, key2, key3, comparer: null, resultSelector); /// /// Partitions a grouping and projects a result from elements groups From a55baa75604638a5c4b4706b6f3377ee958cac79 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sun, 20 Nov 2022 04:43:27 -0600 Subject: [PATCH 132/157] Document "Partition" exceptions (#886) --- MoreLinq/Extensions.g.cs | 47 ++++++++++++++++++++++++++++++++++++---- MoreLinq/Partition.cs | 47 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 1a77a18e6..cbbb688ac 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -4154,6 +4154,8 @@ public static partial class PartitionExtension /// A tuple of elements satisfying the predicate and those that do not, /// respectively. /// + /// is + /// . /// /// True, IEnumerable False) /// /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, Func, IEnumerable, TResult> resultSelector) @@ -4201,6 +4207,10 @@ public static TResult Partition(this IEnumerable> /// /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, Func, IEnumerable, IEnumerable, TResult> resultSelector) @@ -4222,6 +4232,10 @@ public static TResult Partition(this IEnumerable /// /// The return value from . /// + /// + /// , , or + /// is . + /// /// /// (this IEnumerable source, /// matching a key and those groups that do not. /// /// Type of keys in source groupings. - /// Type of elements in source groupings. + /// Type of elements in source + /// groupings. /// Type of the result. /// The source sequence. /// The key to partition. /// /// Function that projects the result from sequences of elements - /// matching and those groups that do not (in - /// the order in which they appear in ), - /// passed as arguments. + /// matching and those groups that do not (in the + /// order in which they appear in ), passed as + /// arguments. /// /// /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key, @@ -4280,6 +4299,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, @@ -4305,6 +4328,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key, IEqualityComparer? comparer, @@ -4332,6 +4359,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, TKey key3, @@ -4359,6 +4390,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, IEqualityComparer? comparer, @@ -4388,6 +4423,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, TKey key3, IEqualityComparer? comparer, diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 2265f96ec..ccd43f7f8 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -33,6 +33,8 @@ static partial class MoreEnumerable /// A tuple of elements satisfying the predicate and those that do not, /// respectively. /// + /// is + /// . /// /// True, IEnumerable False) /// /// The return value from . /// + /// + /// , , or + /// is . + /// /// /// (this IEnumerable source, /// /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, Func, IEnumerable, TResult> resultSelector) @@ -119,6 +129,10 @@ public static TResult Partition(this IEnumerable> /// /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, Func, IEnumerable, IEnumerable, TResult> resultSelector) @@ -132,19 +146,24 @@ public static TResult Partition(this IEnumerable /// matching a key and those groups that do not. /// /// Type of keys in source groupings. - /// Type of elements in source groupings. + /// Type of elements in source + /// groupings. /// Type of the result. /// The source sequence. /// The key to partition. /// /// Function that projects the result from sequences of elements - /// matching and those groups that do not (in - /// the order in which they appear in ), - /// passed as arguments. + /// matching and those groups that do not (in the + /// order in which they appear in ), passed as + /// arguments. /// /// /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key, @@ -171,6 +190,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key, IEqualityComparer? comparer, @@ -201,6 +224,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, @@ -229,6 +256,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, IEqualityComparer? comparer, @@ -260,6 +291,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, TKey key3, @@ -289,6 +324,10 @@ public static TResult Partition(this IEnumerable /// The return value from . /// + /// + /// or is + /// . + /// public static TResult Partition(this IEnumerable> source, TKey key1, TKey key2, TKey key3, IEqualityComparer? comparer, From 41a419559c6741845f4ed957c88b9bf72648c014 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sun, 20 Nov 2022 04:44:25 -0600 Subject: [PATCH 133/157] Add null-check for "Partition" result selector (#896) --- MoreLinq/Partition.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index ccd43f7f8..68bc823a7 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -85,6 +85,8 @@ public static TResult Partition(this IEnumerable source, { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + return source.GroupBy(predicate).Partition(resultSelector); } From 74a282a5ebb43cce2de5b6b466ea8dc9bf29dcaa Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sun, 20 Nov 2022 04:51:37 -0600 Subject: [PATCH 134/157] Rewrite switch in "Memoize" as expression (#894) --- MoreLinq/Experimental/Memoize.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/MoreLinq/Experimental/Memoize.cs b/MoreLinq/Experimental/Memoize.cs index 0888ae455..cc5e2a09a 100644 --- a/MoreLinq/Experimental/Memoize.cs +++ b/MoreLinq/Experimental/Memoize.cs @@ -51,17 +51,15 @@ static partial class ExperimentalEnumerable /// simultaneously) may iterate over the sequence. /// - public static IEnumerable Memoize(this IEnumerable source) - { - switch (source) + public static IEnumerable Memoize(this IEnumerable source) => + source switch { - case null: throw new ArgumentNullException(nameof(source)); - case ICollection : // ... - case IReadOnlyCollection: // ... - case MemoizedEnumerable : return source; - default: return new MemoizedEnumerable(source); - } - } + null => throw new ArgumentNullException(nameof(source)), + ICollection => source, + IReadOnlyCollection => source, + MemoizedEnumerable => source, + _ => new MemoizedEnumerable(source), + }; } sealed class MemoizedEnumerable : IEnumerable, IDisposable From 9ef27ea1b27d056c0ea92ee86e6e999decd1fae9 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Wed, 23 Nov 2022 15:01:13 -0600 Subject: [PATCH 135/157] Clean-up internal "Partition" nullability (#881) --- MoreLinq.Test/PartitionTest.cs | 2 ++ MoreLinq/Partition.cs | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/MoreLinq.Test/PartitionTest.cs b/MoreLinq.Test/PartitionTest.cs index cdc4ac19c..2883885c1 100644 --- a/MoreLinq.Test/PartitionTest.cs +++ b/MoreLinq.Test/PartitionTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index 68bc823a7..aa72f8725 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -202,7 +202,8 @@ public static TResult Partition(this IEnumerable, IEnumerable>, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return PartitionImpl(source, 1, key, key2: default!, key3: default!, comparer, + + return PartitionImpl(source, 1, key, key2: default, key3: default, comparer, (a, _, _, rest) => resultSelector(a, rest)); } @@ -268,8 +269,9 @@ public static TResult Partition(this IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) { if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return PartitionImpl(source, 2, key1, key2, key3: default!, comparer, - (a, b, _, rest) => resultSelector(a, b, rest)); + + return PartitionImpl(source, 2, key1, key2, key3: default, comparer, + (a, b, c, rest) => resultSelector(a, b, rest)); } /// @@ -337,7 +339,7 @@ public static TResult Partition(this IEnumerable(IEnumerable> source, - int count, TKey key1, TKey key2, TKey key3, IEqualityComparer? comparer, + int count, TKey? key1, TKey? key2, TKey? key3, IEqualityComparer? comparer, Func, IEnumerable, IEnumerable, IEnumerable>, TResult> resultSelector) { Debug.Assert(count is > 0 and <= 3); @@ -358,9 +360,9 @@ static TResult PartitionImpl(IEnumerable 0 && comparer.Equals(e.Key, key1) ? 0 - : count > 1 && comparer.Equals(e.Key, key2) ? 1 - : count > 2 && comparer.Equals(e.Key, key3) ? 2 + var i = count > 0 && comparer.Equals(e.Key, key1!) ? 0 + : count > 1 && comparer.Equals(e.Key, key2!) ? 1 + : count > 2 && comparer.Equals(e.Key, key3!) ? 2 : -1; if (i < 0) From 34ce84af6cfeef3824ca4d9b4d93f80c50c1745c Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Wed, 30 Nov 2022 15:39:15 -0600 Subject: [PATCH 136/157] Add exceptions section to "Zip*" docs This is a squashed merge of PR #887. Co-authored-by: Atif Aziz --- MoreLinq/EquiZip.cs | 14 ++++++++++++++ MoreLinq/Extensions.g.cs | 42 ++++++++++++++++++++++++++++++++++++++++ MoreLinq/ZipLongest.cs | 14 ++++++++++++++ MoreLinq/ZipShortest.cs | 14 ++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/MoreLinq/EquiZip.cs b/MoreLinq/EquiZip.cs index 1f2b351f6..61212f122 100644 --- a/MoreLinq/EquiZip.cs +++ b/MoreLinq/EquiZip.cs @@ -42,6 +42,10 @@ static partial class MoreEnumerable /// /// The input sequences are of different lengths. /// + /// + /// , , or is . + /// /// /// EquiZip( /// /// The input sequences are of different lengths. /// + /// + /// , , , or is . + /// /// /// EquiZip( /// /// The input sequences are of different lengths. /// + /// + /// , , , , or is . + /// /// /// /// The input sequences are of different lengths. /// + /// + /// , , or is . + /// /// /// EquiZip( /// /// The input sequences are of different lengths. /// + /// + /// , , , or is . + /// /// /// EquiZip( /// /// The input sequences are of different lengths. /// + /// + /// , , , , or is . + /// /// /// . /// + /// + /// , , or is . + /// /// /// ZipLongest( /// A sequence that contains elements of the three input sequences, /// combined by . /// + /// + /// , , , or is . + /// /// /// ZipLongest( /// A sequence that contains elements of the four input sequences, /// combined by . /// + /// + /// , , , , or is . + /// /// /// /// + /// + /// , , or is . + /// /// ZipShortest( /// /// A projection of tuples, where each tuple contains the N-th element /// from each of the argument sequences. + /// + /// , , , or is . + /// /// /// ZipShortest( /// /// A projection of tuples, where each tuple contains the N-th element /// from each of the argument sequences. + /// + /// , , , , or is . + /// /// /// . /// + /// + /// , , or is . + /// /// /// ZipLongest( /// A sequence that contains elements of the three input sequences, /// combined by . /// + /// + /// , , , or is . + /// /// /// ZipLongest( /// A sequence that contains elements of the four input sequences, /// combined by . /// + /// + /// , , , , or is . + /// /// /// /// + /// + /// , , or is . + /// /// ZipShortest( /// /// A projection of tuples, where each tuple contains the N-th element /// from each of the argument sequences. + /// + /// , , , or is . + /// /// /// ZipShortest( /// /// A projection of tuples, where each tuple contains the N-th element /// from each of the argument sequences. + /// + /// , , , , or is . + /// /// /// Date: Wed, 30 Nov 2022 16:53:43 -0600 Subject: [PATCH 137/157] Add identity method to reduce JIT-ing This is a squashed merge of PR ##880. Co-authored-by: Atif Aziz --- MoreLinq/Batch.cs | 2 +- MoreLinq/GroupAdjacent.cs | 6 +++--- MoreLinq/IdFn.cs | 24 ++++++++++++++++++++++++ MoreLinq/OrderedMerge.cs | 4 ++-- MoreLinq/Rank.cs | 4 ++-- MoreLinq/Split.cs | 6 +++--- 6 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 MoreLinq/IdFn.cs diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index 34babc628..5bb2553e7 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -50,7 +50,7 @@ static partial class MoreEnumerable public static IEnumerable> Batch(this IEnumerable source, int size) { - return Batch(source, size, x => x); + return Batch(source, size, IdFn); } /// diff --git a/MoreLinq/GroupAdjacent.cs b/MoreLinq/GroupAdjacent.cs index cc57e5167..08bd069a6 100644 --- a/MoreLinq/GroupAdjacent.cs +++ b/MoreLinq/GroupAdjacent.cs @@ -87,7 +87,7 @@ public static IEnumerable> GroupAdjacent if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - return GroupAdjacent(source, keySelector, e => e, comparer); + return GroupAdjacent(source, keySelector, IdFn, comparer); } /// @@ -208,7 +208,7 @@ public static IEnumerable GroupAdjacent( // This should be removed once the target framework is bumped to something that supports covariance TResult ResultSelectorWrapper(TKey key, IList group) => resultSelector(key, group); - return GroupAdjacentImpl(source, keySelector, i => i, ResultSelectorWrapper, + return GroupAdjacentImpl(source, keySelector, IdFn, ResultSelectorWrapper, EqualityComparer.Default); } @@ -253,7 +253,7 @@ public static IEnumerable GroupAdjacent( // This should be removed once the target framework is bumped to something that supports covariance TResult ResultSelectorWrapper(TKey key, IList group) => resultSelector(key, group); - return GroupAdjacentImpl(source, keySelector, i => i, ResultSelectorWrapper, + return GroupAdjacentImpl(source, keySelector, IdFn, ResultSelectorWrapper, comparer ?? EqualityComparer.Default); } diff --git a/MoreLinq/IdFn.cs b/MoreLinq/IdFn.cs new file mode 100644 index 000000000..74a32f491 --- /dev/null +++ b/MoreLinq/IdFn.cs @@ -0,0 +1,24 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2022 Turning Code, LLC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + static partial class MoreEnumerable + { + static T IdFn(T x) => x; + } +} diff --git a/MoreLinq/OrderedMerge.cs b/MoreLinq/OrderedMerge.cs index a42e08057..0e30e1517 100644 --- a/MoreLinq/OrderedMerge.cs +++ b/MoreLinq/OrderedMerge.cs @@ -70,7 +70,7 @@ public static IEnumerable OrderedMerge( IEnumerable second, IComparer? comparer) { - return OrderedMerge(first, second, e => e, f => f, s => s, (a, _) => a, comparer); + return OrderedMerge(first, second, IdFn, IdFn, IdFn, (a, _) => a, comparer); } /// @@ -98,7 +98,7 @@ public static IEnumerable OrderedMerge( IEnumerable second, Func keySelector) { - return OrderedMerge(first, second, keySelector, a => a, b => b, (a, _) => a, null); + return OrderedMerge(first, second, keySelector, IdFn, IdFn, (a, _) => a, null); } /// diff --git a/MoreLinq/Rank.cs b/MoreLinq/Rank.cs index 5dff3d13c..d2c81a382 100644 --- a/MoreLinq/Rank.cs +++ b/MoreLinq/Rank.cs @@ -32,7 +32,7 @@ public static partial class MoreEnumerable public static IEnumerable Rank(this IEnumerable source) { - return source.RankBy(x => x); + return source.RankBy(IdFn); } /// @@ -45,7 +45,7 @@ public static IEnumerable Rank(this IEnumerable source) public static IEnumerable Rank(this IEnumerable source, IComparer comparer) { - return source.RankBy(x => x, comparer); + return source.RankBy(IdFn, comparer); } /// diff --git a/MoreLinq/Split.cs b/MoreLinq/Split.cs index 3ff9f2c97..1e101be66 100644 --- a/MoreLinq/Split.cs +++ b/MoreLinq/Split.cs @@ -50,7 +50,7 @@ public static IEnumerable> Split(this IEnumerable< public static IEnumerable> Split(this IEnumerable source, TSource separator, int count) { - return Split(source, separator, count, s => s); + return Split(source, separator, count, IdFn); } /// @@ -129,7 +129,7 @@ public static IEnumerable> Split(this IEnumerable< public static IEnumerable> Split(this IEnumerable source, TSource separator, IEqualityComparer? comparer, int count) { - return Split(source, separator, comparer, count, s => s); + return Split(source, separator, comparer, count, IdFn); } /// @@ -216,7 +216,7 @@ public static IEnumerable> Split(this IEnumerable< public static IEnumerable> Split(this IEnumerable source, Func separatorFunc, int count) { - return Split(source, separatorFunc, count, s => s); + return Split(source, separatorFunc, count, IdFn); } /// From eeddf4dad049c0717cb6df544fc4e0413e968bbf Mon Sep 17 00:00:00 2001 From: Leonid Tsarev Date: Sat, 10 Dec 2022 17:48:51 +0300 Subject: [PATCH 138/157] Use "Random.Shared" on .NET 6+ (#907) Co-authored-by: Atif Aziz --- MoreLinq/GlobalRandom.cs | 71 ++++++++++++++++++++++++++++++++++++++++ MoreLinq/Random.cs | 60 +++++++++------------------------ MoreLinq/RandomSubset.cs | 2 +- MoreLinq/Shuffle.cs | 2 +- 4 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 MoreLinq/GlobalRandom.cs diff --git a/MoreLinq/GlobalRandom.cs b/MoreLinq/GlobalRandom.cs new file mode 100644 index 000000000..7aa165c62 --- /dev/null +++ b/MoreLinq/GlobalRandom.cs @@ -0,0 +1,71 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; +#if !NET6_0_OR_GREATER + using System.Threading; +#endif + + public static partial class MoreEnumerable + { + /// + /// is not thread-safe so the following + /// implementation uses thread-local + /// instances to create the illusion of a global + /// implementation. For some background, + /// see Getting + /// random numbers in a thread-safe way. + /// On .NET 6+, delegates to Random.Shared. + /// + + sealed class GlobalRandom : Random + { +#if NET6_0_OR_GREATER + public static Random Instance => Shared; +#else + public static Random Instance { get; } = new GlobalRandom(); + + static int _seed = Environment.TickCount; + [ThreadStatic] static Random? _threadRandom; + static Random ThreadRandom => _threadRandom ??= new Random(Interlocked.Increment(ref _seed)); + + GlobalRandom() { } + + public override int Next() => ThreadRandom.Next(); + public override int Next(int minValue, int maxValue) => ThreadRandom.Next(minValue, maxValue); + public override int Next(int maxValue) => ThreadRandom.Next(maxValue); + public override double NextDouble() => ThreadRandom.NextDouble(); + public override void NextBytes(byte[] buffer) => ThreadRandom.NextBytes(buffer); + + protected override double Sample() + { + // All the NextXXX calls are hijacked above to use the Random + // instance allocated for the thread so no call from the base + // class should ever end up here. If Random introduces new + // virtual members in the future that call into Sample and + // which end up getting used in the implementation of a + // randomizing operator from the outer class then they will + // need to be overriden. + + throw new NotImplementedException(); + } +#endif + } + } +} diff --git a/MoreLinq/Random.cs b/MoreLinq/Random.cs index 33566a3db..6d9b82700 100644 --- a/MoreLinq/Random.cs +++ b/MoreLinq/Random.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Threading; public static partial class MoreEnumerable { @@ -29,6 +28,7 @@ public static partial class MoreEnumerable /// /// An infinite sequence of random integers /// + /// /// The implementation internally uses a shared, thread-local instance of /// to generate a random number on each /// iteration. The actual instance used @@ -41,7 +41,9 @@ public static partial class MoreEnumerable /// in the generation of the sequence of random numbers. Because the /// instance is shared, if multiple sequences /// are generated on the same thread, the order of enumeration affects the - /// resulting sequences. + /// resulting sequences. + /// + /// On .NET 6 or later, System.Random.Shared is used. /// public static IEnumerable Random() @@ -71,6 +73,7 @@ public static IEnumerable Random(Random rand) /// exclusive upper bound for the random values returned /// An infinite sequence of random integers /// + /// /// The implementation internally uses a shared, thread-local instance of /// to generate a random number on each /// iteration. The actual instance used @@ -83,7 +86,9 @@ public static IEnumerable Random(Random rand) /// in the generation of the sequence of random numbers. Because the /// instance is shared, if multiple sequences /// are generated on the same thread, the order of enumeration affects the - /// resulting sequences. + /// resulting sequences. + /// + /// On .NET 6 or later, System.Random.Shared is used. /// public static IEnumerable Random(int maxValue) @@ -118,6 +123,7 @@ public static IEnumerable Random(Random rand, int maxValue) /// Exclusive upper bound of the values returned /// An infinite sequence of random integers /// + /// /// The implementation internally uses a shared, thread-local instance of /// to generate a random number on each /// iteration. The actual instance used @@ -130,7 +136,9 @@ public static IEnumerable Random(Random rand, int maxValue) /// in the generation of the sequence of random numbers. Because the /// instance is shared, if multiple sequences /// are generated on the same thread, the order of enumeration affects the - /// resulting sequences. + /// resulting sequences. + /// + /// On .NET 6 or later, System.Random.Shared is used. /// public static IEnumerable Random(int minValue, int maxValue) @@ -166,6 +174,7 @@ public static IEnumerable Random(Random rand, int minValue, int maxValue) /// /// An infinite sequence of random doubles /// + /// /// The implementation internally uses a shared, thread-local instance of /// to generate a random number on each /// iteration. The actual instance used @@ -178,7 +187,9 @@ public static IEnumerable Random(Random rand, int minValue, int maxValue) /// in the generation of the sequence of random numbers. Because the /// instance is shared, if multiple sequences /// are generated on the same thread, the order of enumeration affects the - /// resulting sequences. + /// resulting sequences. + /// + /// On .NET 6 or later, System.Random.Shared is used. /// public static IEnumerable RandomDouble() @@ -215,44 +226,5 @@ static IEnumerable RandomImpl(Random rand, Func nextValue) while (true) yield return nextValue(rand); } - - /// - /// is not thread-safe so the following - /// implementation uses thread-local - /// instances to create the illusion of a global - /// implementation. For some background, - /// see Getting - /// random numbers in a thread-safe way - /// - - sealed class GlobalRandom : Random - { - public static readonly Random Instance = new GlobalRandom(); - - static int _seed = Environment.TickCount; - [ThreadStatic] static Random? _threadRandom; - static Random ThreadRandom => _threadRandom ??= new Random(Interlocked.Increment(ref _seed)); - - GlobalRandom() { } - - public override int Next() => ThreadRandom.Next(); - public override int Next(int minValue, int maxValue) => ThreadRandom.Next(minValue, maxValue); - public override int Next(int maxValue) => ThreadRandom.Next(maxValue); - public override double NextDouble() => ThreadRandom.NextDouble(); - public override void NextBytes(byte[] buffer) => ThreadRandom.NextBytes(buffer); - - protected override double Sample() - { - // All the NextXXX calls are hijacked above to use the Random - // instance allocated for the thread so no call from the base - // class should ever end up here. If Random introduces new - // virtual members in the future that call into Sample and - // which end up getting used in the implementation of a - // randomizing operator from the outer class then they will - // need to be overriden. - - throw new NotImplementedException(); - } - } } } diff --git a/MoreLinq/RandomSubset.cs b/MoreLinq/RandomSubset.cs index b3c192719..6aafdc5a1 100644 --- a/MoreLinq/RandomSubset.cs +++ b/MoreLinq/RandomSubset.cs @@ -37,7 +37,7 @@ public static partial class MoreEnumerable public static IEnumerable RandomSubset(this IEnumerable source, int subsetSize) { - return RandomSubset(source, subsetSize, new Random()); + return RandomSubset(source, subsetSize, GlobalRandom.Instance); } /// diff --git a/MoreLinq/Shuffle.cs b/MoreLinq/Shuffle.cs index 3b2f3df57..c7cac7d6e 100644 --- a/MoreLinq/Shuffle.cs +++ b/MoreLinq/Shuffle.cs @@ -42,7 +42,7 @@ public static partial class MoreEnumerable public static IEnumerable Shuffle(this IEnumerable source) { - return Shuffle(source, new Random()); + return Shuffle(source, GlobalRandom.Instance); } /// From 625ee56aa80254626ecd81a1bcf012674fc9b52e Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sat, 17 Dec 2022 08:01:58 -0600 Subject: [PATCH 139/157] Fix "ZipLongest" signatures with more honest nullable annotations This is a squashed merge of PR #900 that adds to #803. --- MoreLinq.Test/ZipLongestTest.cs | 16 +++++++++------- MoreLinq/Extensions.g.cs | 6 +++--- MoreLinq/ZipLongest.cs | 6 +++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/MoreLinq.Test/ZipLongestTest.cs b/MoreLinq.Test/ZipLongestTest.cs index f61470013..5b4e74789 100644 --- a/MoreLinq.Test/ZipLongestTest.cs +++ b/MoreLinq.Test/ZipLongestTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; @@ -31,13 +33,13 @@ public class ZipLongestTest public static readonly IEnumerable TestData = from e in new[] { - new { A = Seq( ), B = Seq("foo", "bar", "baz"), Result = Seq((0, "foo"), (0, "bar"), (0, "baz")) }, - new { A = Seq(1 ), B = Seq("foo", "bar", "baz"), Result = Seq((1, "foo"), (0, "bar"), (0, "baz")) }, - new { A = Seq(1, 2 ), B = Seq("foo", "bar", "baz"), Result = Seq((1, "foo"), (2, "bar"), (0, "baz")) }, - new { A = Seq(1, 2, 3), B = Seq( ), Result = Seq((1, null ), (2, null ), (3, (string) null)) }, - new { A = Seq(1, 2, 3), B = Seq("foo" ), Result = Seq((1, "foo"), (2, null ), (3, null )) }, - new { A = Seq(1, 2, 3), B = Seq("foo", "bar" ), Result = Seq((1, "foo"), (2, "bar"), (3, null )) }, - new { A = Seq(1, 2, 3), B = Seq("foo", "bar", "baz"), Result = Seq((1, "foo"), (2, "bar"), (3, "baz")) }, + new { A = Seq( ), B = Seq("foo", "bar", "baz"), Result = Seq<(int, string?)>((0, "foo"), (0, "bar"), (0, "baz")) }, + new { A = Seq(1 ), B = Seq("foo", "bar", "baz"), Result = Seq<(int, string?)>((1, "foo"), (0, "bar"), (0, "baz")) }, + new { A = Seq(1, 2 ), B = Seq("foo", "bar", "baz"), Result = Seq<(int, string?)>((1, "foo"), (2, "bar"), (0, "baz")) }, + new { A = Seq(1, 2, 3), B = Seq( ), Result = Seq<(int, string?)>((1, null ), (2, null ), (3, null )) }, + new { A = Seq(1, 2, 3), B = Seq("foo" ), Result = Seq<(int, string?)>((1, "foo"), (2, null ), (3, null )) }, + new { A = Seq(1, 2, 3), B = Seq("foo", "bar" ), Result = Seq<(int, string?)>((1, "foo"), (2, "bar"), (3, null )) }, + new { A = Seq(1, 2, 3), B = Seq("foo", "bar", "baz"), Result = Seq<(int, string?)>((1, "foo"), (2, "bar"), (3, "baz")) }, } select new TestCaseData(e.A, e.B) .Returns(e.Result); diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 14d73e462..9e5b16a71 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -6837,7 +6837,7 @@ public static partial class ZipLongestExtension public static IEnumerable ZipLongest( this IEnumerable first, IEnumerable second, - Func resultSelector) + Func resultSelector) => MoreEnumerable.ZipLongest(first, second, resultSelector); /// @@ -6883,7 +6883,7 @@ public static IEnumerable ZipLongest( this IEnumerable first, IEnumerable second, IEnumerable third, - Func resultSelector) + Func resultSelector) => MoreEnumerable.ZipLongest(first, second, third, resultSelector); /// @@ -6933,7 +6933,7 @@ public static IEnumerable ZipLongest( IEnumerable second, IEnumerable third, IEnumerable fourth, - Func resultSelector) + Func resultSelector) => MoreEnumerable.ZipLongest(first, second, third, fourth, resultSelector); } diff --git a/MoreLinq/ZipLongest.cs b/MoreLinq/ZipLongest.cs index 5f93188d7..38e5fdefc 100644 --- a/MoreLinq/ZipLongest.cs +++ b/MoreLinq/ZipLongest.cs @@ -60,7 +60,7 @@ static partial class MoreEnumerable public static IEnumerable ZipLongest( this IEnumerable first, IEnumerable second, - Func resultSelector) + Func resultSelector) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); @@ -112,7 +112,7 @@ public static IEnumerable ZipLongest( this IEnumerable first, IEnumerable second, IEnumerable third, - Func resultSelector) + Func resultSelector) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); @@ -169,7 +169,7 @@ public static IEnumerable ZipLongest( IEnumerable second, IEnumerable third, IEnumerable fourth, - Func resultSelector) + Func resultSelector) { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); From ad137bf3873c4564777c998f335005536ae665ea Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 17 Dec 2022 15:41:29 +0100 Subject: [PATCH 140/157] Enable nullable context for remaining "*Zip*" tests This is a squashed merge of PR #908 that adds to #803. Co-authored-by: Stuart Turner --- MoreLinq.Test/EquiZipTest.cs | 2 ++ MoreLinq.Test/ZipShortestTest.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index f118f21eb..c90c2b4e6 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/ZipShortestTest.cs b/MoreLinq.Test/ZipShortestTest.cs index 3d2d8f80e..ae7de9f14 100644 --- a/MoreLinq.Test/ZipShortestTest.cs +++ b/MoreLinq.Test/ZipShortestTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; From e46acc204a84a0cf57d6848931d8030ccf97b439 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 17 Dec 2022 17:02:47 +0100 Subject: [PATCH 141/157] Add NUnit.Analyzers, addressing errors/warnings Warning addressed: - NUnit2015: https://github.com/nunit/nunit.analyzers/blob/3.5.0/documentation/NUnit2015.md - NUnit2005: https://github.com/nunit/nunit.analyzers/blob/3.5.0/documentation/NUnit2005.md - NUnit2031: https://github.com/nunit/nunit.analyzers/blob/3.5.0/documentation/NUnit2031.md This is a squashed merge of PR #910 that closes #909. --- MoreLinq.Test/AssertCountTest.cs | 4 ++-- MoreLinq.Test/AssertTest.cs | 2 +- MoreLinq.Test/CartesianTest.cs | 12 +++++----- MoreLinq.Test/CompareCountTest.cs | 24 ++++++++++---------- MoreLinq.Test/ConsumeTest.cs | 2 +- MoreLinq.Test/FallbackIfEmptyTest.cs | 16 ++++++------- MoreLinq.Test/FullGroupJoinTest.cs | 14 ++++++------ MoreLinq.Test/GroupAdjacentTest.cs | 2 +- MoreLinq.Test/LagTest.cs | 10 ++++---- MoreLinq.Test/LeadTest.cs | 10 ++++---- MoreLinq.Test/MaxByTest.cs | 12 +++++----- MoreLinq.Test/MinByTest.cs | 12 +++++----- MoreLinq.Test/MoreLinq.Test.csproj | 4 ++++ MoreLinq.Test/PermutationsTest.cs | 8 +++---- MoreLinq.Test/RandomSubsetTest.cs | 10 ++++---- MoreLinq.Test/RandomTest.cs | 12 +++++----- MoreLinq.Test/RankTest.cs | 12 +++++----- MoreLinq.Test/ScanTest.cs | 2 +- MoreLinq.Test/SegmentTest.cs | 8 +++---- MoreLinq.Test/SequenceTest.cs | 2 +- MoreLinq.Test/SliceTest.cs | 2 +- MoreLinq.Test/SubsetTest.cs | 4 ++-- MoreLinq.Test/ToDataTableTest.cs | 34 ++++++++++++++-------------- MoreLinq.Test/TransposeTest.cs | 2 +- MoreLinq.Test/WindowTest.cs | 6 ++--- 25 files changed, 115 insertions(+), 111 deletions(-) diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index fd1c21167..c38ac9661 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -112,7 +112,7 @@ public void AssertCountWithCollectionIsLazy() public void AssertCountWithMatchingCollectionCount() { var xs = new[] { 123, 456, 789 }; - Assert.AreSame(xs, xs.AssertCount(3)); + Assert.That(xs, Is.SameAs(xs.AssertCount(3))); } [TestCase(3, 2, "Sequence contains too many elements when exactly 2 were expected.")] @@ -122,7 +122,7 @@ public void AssertCountWithMismatchingCollectionCount(int sourceCount, int count var xs = new int[sourceCount]; using var enumerator = xs.AssertCount(count).GetEnumerator(); var e = Assert.Throws(() => enumerator.MoveNext()); - Assert.AreEqual(e.Message, message); + Assert.That(e.Message, Is.EqualTo(message)); } [Test] diff --git a/MoreLinq.Test/AssertTest.cs b/MoreLinq.Test/AssertTest.cs index 2d8cdf6a7..a15f0d94e 100644 --- a/MoreLinq.Test/AssertTest.cs +++ b/MoreLinq.Test/AssertTest.cs @@ -57,7 +57,7 @@ public void AssertSequenceWithInvalidElementsAndCustomError() var e = Assert.Throws(() => new[] { 2, 4, 6, 7, 8, 9 }.Assert(n => n % 2 == 0, n => new ValueException(n)).Consume()); - Assert.AreEqual(7, e.Value); + Assert.That(e.Value, Is.EqualTo(7)); } class ValueException : Exception diff --git a/MoreLinq.Test/CartesianTest.cs b/MoreLinq.Test/CartesianTest.cs index 36dcaebcb..19bab4e85 100644 --- a/MoreLinq.Test/CartesianTest.cs +++ b/MoreLinq.Test/CartesianTest.cs @@ -88,7 +88,7 @@ public void TestCartesianProductCount() var result = sequenceA.Cartesian(sequenceB, (a, b) => a + b); - Assert.AreEqual(expectedCount, result.Count() ); + Assert.That(result.Count(), Is.EqualTo(expectedCount)); } /// @@ -111,7 +111,7 @@ public void TestCartesianProductCount_Multidimensional() var result = sequenceA.Cartesian(sequenceB, sequenceC, sequenceD, (a, b, c, d) => a + b + c + d); const int expectedCount = countA * countB * countC * countD; - Assert.AreEqual(expectedCount, result.Count()); + Assert.That(result.Count(), Is.EqualTo(expectedCount)); } /// @@ -139,7 +139,7 @@ public void TestCartesianProductCombinations() .ToArray(); // verify that the expected number of results is correct - Assert.AreEqual(sequenceA.Count() * sequenceB.Count(), result.Count()); + Assert.That(result.Count(), Is.EqualTo(sequenceA.Count() * sequenceB.Count())); // ensure that all "cells" were visited by the cartesian product foreach (var coord in result) @@ -160,9 +160,9 @@ public void TestEmptyCartesianEvaluation() var resultB = Enumerable.Empty().Cartesian(sequence, (a, b) => new { A = a, B = b }); var resultC = Enumerable.Empty().Cartesian(Enumerable.Empty(), (a, b) => new { A = a, B = b }); - Assert.AreEqual(0, resultA.Count()); - Assert.AreEqual(0, resultB.Count()); - Assert.AreEqual(0, resultC.Count()); + Assert.That(resultA.Count(), Is.Zero); + Assert.That(resultB.Count(), Is.Zero); + Assert.That(resultC.Count(), Is.Zero); } } } diff --git a/MoreLinq.Test/CompareCountTest.cs b/MoreLinq.Test/CompareCountTest.cs index 96e2151ab..bc93e08de 100644 --- a/MoreLinq.Test/CompareCountTest.cs +++ b/MoreLinq.Test/CompareCountTest.cs @@ -57,8 +57,8 @@ public void CompareCountWithCollectionAndSequence(int collectionCount, using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); - Assert.AreEqual(expectedCompareCount, collection.CompareCount(seq)); - Assert.AreEqual(expectedMoveNextCallCount, seq.MoveNextCallCount); + Assert.That(collection.CompareCount(seq), Is.EqualTo(expectedCompareCount)); + Assert.That(seq.MoveNextCallCount, Is.EqualTo(expectedMoveNextCallCount)); } [TestCase(0, 0, 0, 1)] @@ -74,8 +74,8 @@ public void CompareCountWithSequenceAndCollection(int sequenceCount, using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); - Assert.AreEqual(expectedCompareCount, seq.CompareCount(collection)); - Assert.AreEqual(expectedMoveNextCallCount, seq.MoveNextCallCount); + Assert.That(seq.CompareCount(collection), Is.EqualTo(expectedCompareCount)); + Assert.That(seq.MoveNextCallCount, Is.EqualTo(expectedMoveNextCallCount)); } [TestCase(0, 0, 0, 1)] @@ -90,9 +90,9 @@ public void CompareCountWithSequenceAndSequence(int sequenceCount1, using var seq1 = Enumerable.Range(0, sequenceCount1).AsTestingSequence(); using var seq2 = Enumerable.Range(0, sequenceCount2).AsTestingSequence(); - Assert.AreEqual(expectedCompareCount, seq1.CompareCount(seq2)); - Assert.AreEqual(expectedMoveNextCallCount, seq1.MoveNextCallCount); - Assert.AreEqual(expectedMoveNextCallCount, seq2.MoveNextCallCount); + Assert.That(seq1.CompareCount(seq2), Is.EqualTo(expectedCompareCount)); + Assert.That(seq1.MoveNextCallCount, Is.EqualTo(expectedMoveNextCallCount)); + Assert.That(seq2.MoveNextCallCount, Is.EqualTo(expectedMoveNextCallCount)); } [Test] @@ -101,7 +101,7 @@ public void CompareCountDisposesSequenceEnumerators() using var seq1 = TestingSequence.Of(); using var seq2 = TestingSequence.Of(); - Assert.AreEqual(0, seq1.CompareCount(seq2)); + Assert.That(seq1.CompareCount(seq2), Is.Zero); } [Test] @@ -111,7 +111,7 @@ public void CompareCountDisposesFirstEnumerator() using var seq = TestingSequence.Of(); - Assert.AreEqual(0, seq.CompareCount(collection)); + Assert.That(seq.CompareCount(collection), Is.Zero); } [Test] @@ -121,7 +121,7 @@ public void CompareCountDisposesSecondEnumerator() using var seq = TestingSequence.Of(); - Assert.AreEqual(0, collection.CompareCount(seq)); + Assert.That(collection.CompareCount(seq), Is.Zero); } [Test] @@ -135,8 +135,8 @@ public void CompareCountDoesNotIterateUnnecessaryElements() var seq2 = Enumerable.Range(1, 3); - Assert.AreEqual( 1, seq1.CompareCount(seq2)); - Assert.AreEqual(-1, seq2.CompareCount(seq1)); + Assert.That(seq1.CompareCount(seq2), Is.EqualTo( 1)); + Assert.That(seq2.CompareCount(seq1), Is.EqualTo(-1)); } enum SequenceKind diff --git a/MoreLinq.Test/ConsumeTest.cs b/MoreLinq.Test/ConsumeTest.cs index 83279cabd..5d802b344 100644 --- a/MoreLinq.Test/ConsumeTest.cs +++ b/MoreLinq.Test/ConsumeTest.cs @@ -28,7 +28,7 @@ public void ConsumeReallyConsumes() var counter = 0; var sequence = Enumerable.Range(0, 10).Pipe(_ => counter++); sequence.Consume(); - Assert.AreEqual(10, counter); + Assert.That(counter, Is.EqualTo(10)); } } } diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index cf6a2df83..65ccfc788 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -42,12 +42,12 @@ public void FallbackIfEmptyPreservesSourceCollectionIfPossible(SourceKind source { var source = new[] { 1 }.ToSourceKind(sourceKind); // ReSharper disable PossibleMultipleEnumeration - Assert.AreSame(source.FallbackIfEmpty(12), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), source); + Assert.That(source.FallbackIfEmpty(12), Is.SameAs(source)); + Assert.That(source.FallbackIfEmpty(12, 23), Is.SameAs(source)); + Assert.That(source.FallbackIfEmpty(12, 23, 34), Is.SameAs(source)); + Assert.That(source.FallbackIfEmpty(12, 23, 34, 45), Is.SameAs(source)); + Assert.That(source.FallbackIfEmpty(12, 23, 34, 45, 56), Is.SameAs(source)); + Assert.That(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), Is.SameAs(source)); // ReSharper restore PossibleMultipleEnumeration } @@ -57,8 +57,8 @@ public void FallbackIfEmptyPreservesFallbackCollectionIfPossible(SourceKind sour { var source = new int[0].ToSourceKind(sourceKind); var fallback = new[] { 1 }; - Assert.AreSame(source.FallbackIfEmpty(fallback), fallback); - Assert.AreSame(source.FallbackIfEmpty(fallback.AsEnumerable()), fallback); + Assert.That(source.FallbackIfEmpty(fallback), Is.SameAs(fallback)); + Assert.That(source.FallbackIfEmpty(fallback.AsEnumerable()), Is.SameAs(fallback)); } [Test] diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index 1b7838ee3..e4cc3dd81 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -48,7 +48,7 @@ public void FullGroupJoinsResults(OverloadCase overloadCase) var result = FullGroupJoin(overloadCase, listA, listB, x => x).ToDictionary(a => a.Key); - Assert.AreEqual(3, result.Keys.Count); + Assert.That(result.Keys.Count, Is.EqualTo(3)); Assert.IsEmpty(result[1].Second); result[1].First.AssertSequenceEqual(1); @@ -69,13 +69,13 @@ public void FullGroupJoinsEmptyLeft(OverloadCase overloadCase) var result = FullGroupJoin(overloadCase, listA, listB, x => x).ToDictionary(a => a.Key); - Assert.AreEqual(2, result.Keys.Count); + Assert.That(result.Keys.Count, Is.EqualTo(2)); Assert.IsEmpty(result[2].First); - Assert.AreEqual(2, result[2].Second.Single()); + Assert.That(result[2].Second.Single(), Is.EqualTo(2)); Assert.IsEmpty(result[3].First); - Assert.AreEqual(3, result[3].Second.Single()); + Assert.That(result[3].Second.Single(), Is.EqualTo(3)); } [TestCase(CustomResult)] @@ -87,12 +87,12 @@ public void FullGroupJoinsEmptyRight(OverloadCase overloadCase) var result = FullGroupJoin(overloadCase, listA, listB, x => x).ToDictionary(a => a.Key); - Assert.AreEqual(2, result.Keys.Count); + Assert.That(result.Keys.Count, Is.EqualTo(2)); - Assert.AreEqual(2, result[2].First.Single()); + Assert.That(result[2].First.Single(), Is.EqualTo(2)); Assert.IsEmpty(result[2].Second); - Assert.AreEqual(3, result[3].First.Single()); + Assert.That(result[3].First.Single(), Is.EqualTo(3)); Assert.IsEmpty(result[3].Second); } diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index 8c23e5552..dbc1e8762 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -235,7 +235,7 @@ static void AssertResult(SequenceReader reader, TElement ele { var result = reader.Read(); Assert.That(result, Is.Not.Null); - Assert.AreEqual(element, result); + Assert.That(result, Is.EqualTo(element)); } } } diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index e41a91c83..192d9028c 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -69,7 +69,7 @@ public void TestLagExplicitDefaultValue() var sequence = Enumerable.Range(1, count); var result = sequence.Lag(lagBy, lagDefault, (_, lagVal) => lagVal); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.That(result.Take(lagBy), Is.EqualTo(Enumerable.Repeat(lagDefault, lagBy))); } @@ -84,7 +84,7 @@ public void TestLagImplicitDefaultValue() var sequence = Enumerable.Range(1, count); var result = sequence.Lag(lagBy, (_, lagVal) => lagVal); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.That(result.Take(lagBy), Is.EqualTo(Enumerable.Repeat(default(int), lagBy))); } @@ -99,7 +99,7 @@ public void TestLagOffsetGreaterThanSequenceLength() var sequence = Enumerable.Range(1, count); var result = sequence.Lag(count + 1, (a, _) => a); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.That(result, Is.EqualTo(sequence)); } @@ -114,7 +114,7 @@ public void TestLagPassesCorrectLagValueOffsetBy1() var sequence = Enumerable.Range(1, count); var result = sequence.Lag(1, (a, b) => new { A = a, B = b }); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.IsTrue(result.All(x => x.B == (x.A - 1))); } @@ -129,7 +129,7 @@ public void TestLagPassesCorrectLagValuesOffsetBy2() var sequence = Enumerable.Range(1, count); var result = sequence.Lag(2, (a, b) => new { A = a, B = b }); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.IsTrue(result.Skip(2).All(x => x.B == (x.A - 2))); Assert.IsTrue(result.Take(2).All(x => (x.A - x.B) == x.A)); } diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index 2298be7c9..4574cd333 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -69,7 +69,7 @@ public void TestLeadExplicitDefaultValue() var sequence = Enumerable.Range(1, count); var result = sequence.Lead(leadBy, leadDefault, (_, leadVal) => leadVal); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.That(result.Skip(count - leadBy), Is.EqualTo(Enumerable.Repeat(leadDefault, leadBy))); } @@ -84,7 +84,7 @@ public void TestLeadImplicitDefaultValue() var sequence = Enumerable.Range(1, count); var result = sequence.Lead(leadBy, (_, leadVal) => leadVal); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.That(result.Skip(count - leadBy), Is.EqualTo(Enumerable.Repeat(default(int), leadBy))); } @@ -100,7 +100,7 @@ public void TestLeadOffsetGreaterThanSequenceLength() var sequence = Enumerable.Range(1, count); var result = sequence.Lead(count + 1, leadDefault, (val, leadVal) => new { A = val, B = leadVal }); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.That(result, Is.EqualTo(sequence.Select(x => new { A = x, B = leadDefault }))); } @@ -115,7 +115,7 @@ public void TestLeadPassesCorrectValueOffsetBy1() var sequence = Enumerable.Range(1, count); var result = sequence.Lead(1, count + 1, (val, leadVal) => new { A = val, B = leadVal }); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.IsTrue(result.All(x => x.B == (x.A + 1))); } @@ -131,7 +131,7 @@ public void TestLeadPassesCorrectValueOffsetBy2() var sequence = Enumerable.Range(1, count); var result = sequence.Lead(2, leadDefault, (val, leadVal) => new { A = val, B = leadVal }); - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); Assert.IsTrue(result.Take(count - 2).All(x => x.B == (x.A + 2))); Assert.IsTrue(result.Skip(count - 2).All(x => x.B == leadDefault && x.A is count or count - 1)); } diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 6480e932e..7cab0440c 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -34,15 +34,15 @@ public void MaxByIsLazy() [Test] public void MaxByReturnsMaxima() { - Assert.AreEqual(new[] { "hello", "world" }, - SampleData.Strings.MaxBy(x => x.Length)); + Assert.That(SampleData.Strings.MaxBy(x => x.Length), + Is.EqualTo(new[] { "hello", "world" })); } [Test] public void MaxByNullComparer() { - Assert.AreEqual(SampleData.Strings.MaxBy(x => x.Length), - SampleData.Strings.MaxBy(x => x.Length, null)); + Assert.That(SampleData.Strings.MaxBy(x => x.Length, null), + Is.EqualTo(SampleData.Strings.MaxBy(x => x.Length))); } [Test] @@ -54,13 +54,13 @@ public void MaxByEmptySequence() [Test] public void MaxByWithNaturalComparer() { - Assert.AreEqual(new[] { "az" }, SampleData.Strings.MaxBy(x => x[1])); + Assert.That(SampleData.Strings.MaxBy(x => x[1]), Is.EqualTo(new[] { "az" })); } [Test] public void MaxByWithComparer() { - Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MaxBy(x => x[1], Comparable.DescendingOrderComparer)); + Assert.That(SampleData.Strings.MaxBy(x => x[1], Comparable.DescendingOrderComparer), Is.EqualTo(new[] { "aa" })); } public class First diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 79d2da26e..8482d29b3 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -34,15 +34,15 @@ public void MinByIsLazy() [Test] public void MinByReturnsMinima() { - Assert.AreEqual(new[] { "ax", "aa", "ab", "ay", "az" }, - SampleData.Strings.MinBy(x => x.Length)); + Assert.That(SampleData.Strings.MinBy(x => x.Length), + Is.EqualTo(new[] { "ax", "aa", "ab", "ay", "az" })); } [Test] public void MinByNullComparer() { - Assert.AreEqual(SampleData.Strings.MinBy(x => x.Length), - SampleData.Strings.MinBy(x => x.Length, null)); + Assert.That(SampleData.Strings.MinBy(x => x.Length, null), + Is.EqualTo(SampleData.Strings.MinBy(x => x.Length))); } [Test] @@ -54,13 +54,13 @@ public void MinByEmptySequence() [Test] public void MinByWithNaturalComparer() { - Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MinBy(x => x[1])); + Assert.That(SampleData.Strings.MinBy(x => x[1]), Is.EqualTo(new[] { "aa" })); } [Test] public void MinByWithComparer() { - Assert.AreEqual(new[] { "az" }, SampleData.Strings.MinBy(x => x[1], Comparable.DescendingOrderComparer)); + Assert.That(SampleData.Strings.MinBy(x => x[1], Comparable.DescendingOrderComparer), Is.EqualTo(new[] { "az" })); } public class First diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 273866c9b..535a85744 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -34,6 +34,10 @@ all + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index fe2897556..768de6738 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -89,7 +89,7 @@ public void TestCardinalityThreePermutation() }; // should contain six permutations (as defined above) - Assert.AreEqual(expectedPermutations.Length, permutations.Count()); + Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); Assert.IsTrue(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y))))); } @@ -132,7 +132,7 @@ public void TestCardinalityFourPermutation() }; // should contain six permutations (as defined above) - Assert.AreEqual(expectedPermutations.Length, permutations.Count()); + Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); Assert.IsTrue(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y))))); } @@ -159,7 +159,7 @@ public void TestHigherCardinalityPermutations() { var permutedSet = set.Permutations(); var permutationCount = permutedSet.Count(); - Assert.AreEqual(Combinatorics.Factorial(set.Count()), permutationCount); + Assert.That(permutationCount, Is.EqualTo(Combinatorics.Factorial(set.Count()))); } } @@ -192,7 +192,7 @@ public void TestPermutationsAreIndependent() for (var j = 1; j < listPermutations.Count; j++) { if (j == i) continue; - Assert.AreNotSame(listPermutations[i], listPermutations[j]); + Assert.That(listPermutations[i], Is.Not.SameAs(listPermutations[j])); } } } diff --git a/MoreLinq.Test/RandomSubsetTest.cs b/MoreLinq.Test/RandomSubsetTest.cs index 50988c862..7f0e5edf5 100644 --- a/MoreLinq.Test/RandomSubsetTest.cs +++ b/MoreLinq.Test/RandomSubsetTest.cs @@ -66,7 +66,7 @@ public void TestRandomSubsetOfEmptySequence() var sequence = Enumerable.Empty(); var result = sequence.RandomSubset(0); // we can only get subsets <= sequence.Count() - Assert.AreEqual(0, result.Count()); + Assert.That(result.Count(), Is.Zero); } /// @@ -81,8 +81,8 @@ public void TestRandomSubsetSameLengthAsSequence() var resultB = sequence.RandomSubset(count, new Random(12345)); // ensure random subset is always a complete reordering of original sequence - Assert.AreEqual(count, resultA.Distinct().Count()); - Assert.AreEqual(count, resultB.Distinct().Count()); + Assert.That(resultA.Distinct().Count(), Is.EqualTo(count)); + Assert.That(resultB.Distinct().Count(), Is.EqualTo(count)); } /// @@ -98,8 +98,8 @@ public void TestRandomSubsetShorterThanSequence() var resultB = sequence.RandomSubset(subsetSize, new Random(12345)); // ensure random subset is always a distinct subset of original sequence - Assert.AreEqual(subsetSize, resultA.Distinct().Count()); - Assert.AreEqual(subsetSize, resultB.Distinct().Count()); + Assert.That(resultA.Distinct().Count(), Is.EqualTo(subsetSize)); + Assert.That(resultB.Distinct().Count(), Is.EqualTo(subsetSize)); } /// diff --git a/MoreLinq.Test/RandomTest.cs b/MoreLinq.Test/RandomTest.cs index 54b295127..6cf9f4ab9 100644 --- a/MoreLinq.Test/RandomTest.cs +++ b/MoreLinq.Test/RandomTest.cs @@ -68,8 +68,8 @@ public void TestRandomDouble() var resultB = MoreEnumerable.RandomDouble(new Random()).Take(RandomTrials); // NOTE: Unclear what should actually be verified here... some additional thought needed. - Assert.AreEqual(RandomTrials, resultA.Count()); - Assert.AreEqual(RandomTrials, resultB.Count()); + Assert.That(resultA.Count(), Is.EqualTo(RandomTrials)); + Assert.That(resultB.Count(), Is.EqualTo(RandomTrials)); Assert.IsTrue(resultA.All(x => x is >= 0.0 and < 1.0)); Assert.IsTrue(resultB.All(x => x is >= 0.0 and < 1.0)); } @@ -84,8 +84,8 @@ public void TestRandomMaxConstraint() var resultA = MoreEnumerable.Random(max).Take(RandomTrials); var resultB = MoreEnumerable.Random(new Random(), max).Take(RandomTrials); - Assert.AreEqual(RandomTrials, resultA.Count()); - Assert.AreEqual(RandomTrials, resultB.Count()); + Assert.That(resultA.Count(), Is.EqualTo(RandomTrials)); + Assert.That(resultB.Count(), Is.EqualTo(RandomTrials)); Assert.IsTrue(resultA.All(x => x < max)); Assert.IsTrue(resultB.All(x => x < max)); } @@ -101,8 +101,8 @@ public void TestRandomMinMaxConstraint() var resultA = MoreEnumerable.Random(min, max).Take(RandomTrials); var resultB = MoreEnumerable.Random(new Random(), min, max).Take(RandomTrials); - Assert.AreEqual(RandomTrials, resultA.Count()); - Assert.AreEqual(RandomTrials, resultB.Count()); + Assert.That(resultA.Count(), Is.EqualTo(RandomTrials)); + Assert.That(resultB.Count(), Is.EqualTo(RandomTrials)); Assert.IsTrue(resultA.All(x => x is >= min and < max)); Assert.IsTrue(resultB.All(x => x is >= min and < max)); } diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index de3852782..1187e408e 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -76,7 +76,7 @@ public void TestRankDescendingSequence() var result = sequence.AsTestingSequence().Rank().ToArray(); var expectedResult = Enumerable.Range(1, count); - Assert.AreEqual(count, result.Length); + Assert.That(result.Length, Is.EqualTo(count)); Assert.That(result, Is.EqualTo(expectedResult)); } @@ -92,7 +92,7 @@ public void TestRankByAscendingSeries() var result = sequence.AsTestingSequence().Rank().ToArray(); var expectedResult = Enumerable.Range(1, count).Reverse(); - Assert.AreEqual(count, result.Length); + Assert.That(result.Length, Is.EqualTo(count)); Assert.That(result, Is.EqualTo(expectedResult)); } @@ -106,7 +106,7 @@ public void TestRankEquivalentItems() var sequence = Enumerable.Repeat(1234, count); var result = sequence.AsTestingSequence().Rank().ToArray(); - Assert.AreEqual(count, result.Length); + Assert.That(result.Length, Is.EqualTo(count)); Assert.That(result, Is.EqualTo(Enumerable.Repeat(1, count))); } @@ -122,7 +122,7 @@ public void TestRankGroupedItems() .Concat(Enumerable.Range(0, count)); var result = sequence.AsTestingSequence().Rank(); - Assert.AreEqual(count, result.Distinct().Count()); + Assert.That(result.Distinct().Count(), Is.EqualTo(count)); Assert.That(result, Is.EqualTo(sequence.Reverse().Select(x => x + 1))); } @@ -136,7 +136,7 @@ public void TestRankOfHighestItemIsOne() var sequence = Enumerable.Range(1, count); var result = sequence.AsTestingSequence().Rank(); - Assert.AreEqual(1, result.OrderBy(x => x).First()); + Assert.That(result.OrderBy(x => x).First(), Is.EqualTo(1)); } /// @@ -158,7 +158,7 @@ public void TestRankByKeySelector() }; var result = sequence.AsTestingSequence().RankBy(x => x.Age).ToArray(); - Assert.AreEqual(sequence.Length, result.Length); + Assert.That(result.Length, Is.EqualTo(sequence.Length)); Assert.That(result, Is.EqualTo(sequence.Select(x => x.ExpectedRank))); } diff --git a/MoreLinq.Test/ScanTest.cs b/MoreLinq.Test/ScanTest.cs index ac6abaa32..e94c47dee 100644 --- a/MoreLinq.Test/ScanTest.cs +++ b/MoreLinq.Test/ScanTest.cs @@ -55,7 +55,7 @@ public void ScanDoesNotIterateExtra() [Test] public void SeededScanEmpty() { - Assert.AreEqual(-1, new int[0].Scan(-1, SampleData.Plus).Single()); + Assert.That(new int[0].Scan(-1, SampleData.Plus).Single(), Is.EqualTo(-1)); } [Test] diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index e15023a17..e072ccbd0 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -77,7 +77,7 @@ public void TestSegmentIsIdempotent() for (var i = 0; i < 2; i++) { Assert.IsTrue(segment.Any()); - Assert.AreEqual(value, segment.Single()); + Assert.That(segment.Single(), Is.EqualTo(value)); } } } @@ -127,10 +127,10 @@ public void VerifyCanSegmentByIndex() var sequence = Enumerable.Repeat(1, count); var result = sequence.Segment((_, i) => i % segmentSize == 0); - Assert.AreEqual(count / segmentSize, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count / segmentSize)); foreach (var segment in result) { - Assert.AreEqual(segmentSize, segment.Count()); + Assert.That(segment.Count(), Is.EqualTo(segmentSize)); } } @@ -145,7 +145,7 @@ public void VerifyCanSegmentByPrevious() .SelectMany(x => Enumerable.Repeat(x, repCount)); var result = sequence.Segment((curr, prev, _) => curr != prev); - Assert.AreEqual(sequence.Distinct().Count(), result.Count()); + Assert.That(result.Count(), Is.EqualTo(sequence.Distinct().Count())); Assert.IsTrue(result.All(s => s.Count() == repCount)); } diff --git a/MoreLinq.Test/SequenceTest.cs b/MoreLinq.Test/SequenceTest.cs index d2e288be7..01f49ad16 100644 --- a/MoreLinq.Test/SequenceTest.cs +++ b/MoreLinq.Test/SequenceTest.cs @@ -124,7 +124,7 @@ public void SequenceEdgeCases(int start, int stop, int step, int count) { var result = MoreEnumerable.Sequence(start, stop, step); - Assert.AreEqual(result.Count(), count); + Assert.That(result.Count(), Is.EqualTo(count)); } [TestCase( 5, 10)] diff --git a/MoreLinq.Test/SliceTest.cs b/MoreLinq.Test/SliceTest.cs index 18e80eeea..4cc5efd86 100644 --- a/MoreLinq.Test/SliceTest.cs +++ b/MoreLinq.Test/SliceTest.cs @@ -142,7 +142,7 @@ public void TestSliceOptimization(SourceKind sourceKind) var result = sequence.Slice(sliceStart, sliceCount); - Assert.AreEqual(sliceCount, result.Count()); + Assert.That(result.Count(), Is.EqualTo(sliceCount)); CollectionAssert.AreEqual(Enumerable.Range(5, sliceCount), result); } } diff --git a/MoreLinq.Test/SubsetTest.cs b/MoreLinq.Test/SubsetTest.cs index 439631eaa..201a1ff36 100644 --- a/MoreLinq.Test/SubsetTest.cs +++ b/MoreLinq.Test/SubsetTest.cs @@ -107,7 +107,7 @@ public void TestAllSubsetsExpectedCount() var expectedCount = Math.Pow(2, count); - Assert.AreEqual(expectedCount, result.Count()); + Assert.That(result.Count(), Is.EqualTo(expectedCount)); } /// @@ -147,7 +147,7 @@ public void TestKSubsetExpectedCount() // number of subsets of a given size is defined by the binomial coefficient: c! / ((c-s)!*s!) var expectedSubsetCount = Combinatorics.Binomial(count, subsetSize); - Assert.AreEqual(expectedSubsetCount, result.Count()); + Assert.That(result.Count(), Is.EqualTo(expectedSubsetCount)); } /// diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index b2d43b168..71118967a 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -130,27 +130,27 @@ public void ToDataTableSchemaInDeclarationOrder() // Assert properties first, then fields, then in declaration order - Assert.AreEqual("AString", dt.Columns[0].Caption); - Assert.AreEqual(typeof(string), dt.Columns[0].DataType); + Assert.That(dt.Columns[0].Caption, Is.EqualTo("AString")); + Assert.That(dt.Columns[0].DataType, Is.EqualTo(typeof(string))); - Assert.AreEqual("ANullableDecimal", dt.Columns[1].Caption); - Assert.AreEqual(typeof(decimal), dt.Columns[1].DataType); + Assert.That(dt.Columns[1].Caption, Is.EqualTo("ANullableDecimal")); + Assert.That(dt.Columns[1].DataType, Is.EqualTo(typeof(decimal))); - Assert.AreEqual("KeyField", dt.Columns[2].Caption); - Assert.AreEqual(typeof(int), dt.Columns[2].DataType); + Assert.That(dt.Columns[2].Caption, Is.EqualTo("KeyField")); + Assert.That(dt.Columns[2].DataType, Is.EqualTo(typeof(int))); - Assert.AreEqual("ANullableGuidField", dt.Columns[3].Caption); - Assert.AreEqual(typeof(Guid), dt.Columns[3].DataType); + Assert.That(dt.Columns[3].Caption, Is.EqualTo("ANullableGuidField")); + Assert.That(dt.Columns[3].DataType, Is.EqualTo(typeof(Guid))); Assert.IsTrue(dt.Columns[3].AllowDBNull); - Assert.AreEqual(4, dt.Columns.Count); + Assert.That(dt.Columns.Count, Is.EqualTo(4)); } [Test] public void ToDataTableContainsAllElements() { var dt = _testObjects.ToDataTable(); - Assert.AreEqual(_testObjects.Count, dt.Rows.Count); + Assert.That(dt.Rows.Count, Is.EqualTo(_testObjects.Count)); } [Test] @@ -158,10 +158,10 @@ public void ToDataTableWithExpression() { var dt = _testObjects.ToDataTable(t => t.AString); - Assert.AreEqual("AString", dt.Columns[0].Caption); - Assert.AreEqual(typeof(string), dt.Columns[0].DataType); + Assert.That(dt.Columns[0].Caption, Is.EqualTo("AString")); + Assert.That(dt.Columns[0].DataType, Is.EqualTo(typeof(string))); - Assert.AreEqual(1, dt.Columns.Count); + Assert.That(dt.Columns.Count, Is.EqualTo(1)); } [Test] @@ -201,15 +201,15 @@ public void ToDataTableIgnoresStaticMembers() { var points = new[] { new Point(12, 34) }.ToDataTable(); - Assert.AreEqual(3, points.Columns.Count); + Assert.That(points.Columns.Count, Is.EqualTo(3)); DataColumn x, y, empty; Assert.NotNull(x = points.Columns["X"]); Assert.NotNull(y = points.Columns["Y"]); Assert.NotNull(empty = points.Columns["IsEmpty"]); var row = points.Rows.Cast().Single(); - Assert.AreEqual(12, row[x]); - Assert.AreEqual(34, row[y]); - Assert.AreEqual(false, row[empty]); + Assert.That(row[x], Is.EqualTo(12)); + Assert.That(row[y], Is.EqualTo(34)); + Assert.That(row[empty], Is.False); } } } diff --git a/MoreLinq.Test/TransposeTest.cs b/MoreLinq.Test/TransposeTest.cs index 097734165..5bdc2422a 100644 --- a/MoreLinq.Test/TransposeTest.cs +++ b/MoreLinq.Test/TransposeTest.cs @@ -215,7 +215,7 @@ static void AssertMatrix(IEnumerable> expectation, IEnumerable var resultList = result.ToList(); var expectationList = expectation.ToList(); - Assert.AreEqual(expectationList.Count, resultList.Count); + Assert.That(resultList.Count, Is.EqualTo(expectationList.Count)); expectationList.Zip(resultList, ValueTuple.Create) .ForEach(t => t.Item1.AssertSequenceEqual(t.Item2)); diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index b9ae4bf18..e711dba11 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -116,11 +116,11 @@ public void TestWindowOfSingleElement() var result = sequence.Window(1); // number of windows should be equal to the source sequence length - Assert.AreEqual(count, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count)); // each window should contain single item consistent of element at that offset var index = -1; foreach (var window in result) - Assert.AreEqual(sequence.ElementAt(++index), window.Single()); + Assert.That(window.Single(), Is.EqualTo(sequence.ElementAt(++index))); } /// @@ -152,7 +152,7 @@ public void TestWindowSmallerThanSequence() var result = sequence.Window(windowSize); // ensure that the number of windows is correct - Assert.AreEqual(count - windowSize + 1, result.Count()); + Assert.That(result.Count(), Is.EqualTo(count - windowSize + 1)); // ensure each window contains the correct set of items var index = -1; foreach (var window in result) From 3d887eeaaadf851560548e0fa86d32fae1e581ad Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 18 Dec 2022 17:52:06 +0100 Subject: [PATCH 142/157] Remove unused imports --- MoreLinq.Test/FromTest.cs | 1 - MoreLinq.Test/PadTest.cs | 1 - MoreLinq.Test/ToDataTableTest.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/MoreLinq.Test/FromTest.cs b/MoreLinq.Test/FromTest.cs index 8dbe01375..cee487dc0 100644 --- a/MoreLinq.Test/FromTest.cs +++ b/MoreLinq.Test/FromTest.cs @@ -18,7 +18,6 @@ namespace MoreLinq.Test { using System; - using System.Collections.Generic; using NUnit.Framework; class FromTest diff --git a/MoreLinq.Test/PadTest.cs b/MoreLinq.Test/PadTest.cs index a7d2ccc58..1c1c2533e 100644 --- a/MoreLinq.Test/PadTest.cs +++ b/MoreLinq.Test/PadTest.cs @@ -19,7 +19,6 @@ namespace MoreLinq.Test { - using System.Collections.Generic; using NUnit.Framework; [TestFixture] diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index 71118967a..f095a93a3 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -22,7 +22,6 @@ namespace MoreLinq.Test using System.Collections.Generic; using System.Data; using System.Linq.Expressions; - using System.Text.RegularExpressions; using NUnit.Framework; [TestFixture] From e603f1c1fabdaea511a3a22c731acb48547b8dd0 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 18 Dec 2022 23:17:44 +0100 Subject: [PATCH 143/157] Update all tests to consistently use NUnit's constraint model This is a squashed merge of PR #912. --- MoreLinq.Test/AcquireTest.cs | 2 +- MoreLinq.Test/AggregateRightTest.cs | 5 +-- MoreLinq.Test/AssertCountTest.cs | 55 ++++++++++++++------------- MoreLinq.Test/AssertTest.cs | 19 +++++---- MoreLinq.Test/AssertThrowsArgument.cs | 48 ----------------------- MoreLinq.Test/AtLeastTest.cs | 24 ++++++------ MoreLinq.Test/AtMostTest.cs | 18 ++++----- MoreLinq.Test/BacksertTest.cs | 8 ++-- MoreLinq.Test/BatchTest.cs | 12 +++--- MoreLinq.Test/CartesianTest.cs | 2 +- MoreLinq.Test/CountBetweenTest.cs | 16 ++++---- MoreLinq.Test/EndsWithTest.cs | 14 +++---- MoreLinq.Test/EquiZipTest.cs | 23 +++++------ MoreLinq.Test/ExactlyTest.cs | 14 +++---- MoreLinq.Test/ExcludeTest.cs | 8 ++-- MoreLinq.Test/FlattenTest.cs | 8 ++-- MoreLinq.Test/FoldTest.cs | 12 +++--- MoreLinq.Test/FullGroupJoinTest.cs | 12 +++--- MoreLinq.Test/FullJoinTest.cs | 20 ++++++---- MoreLinq.Test/IndexByTest.cs | 4 +- MoreLinq.Test/InsertTest.cs | 9 ++--- MoreLinq.Test/InterleaveTest.cs | 7 ++-- MoreLinq.Test/LagTest.cs | 14 +++---- MoreLinq.Test/LeadTest.cs | 14 +++---- MoreLinq.Test/LeftJoinTest.cs | 20 ++++++---- MoreLinq.Test/MaxByTest.cs | 21 +++++----- MoreLinq.Test/MemoizeTest.cs | 34 ++++++++--------- MoreLinq.Test/MinByTest.cs | 17 ++++----- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- MoreLinq.Test/MoveTest.cs | 12 +++--- MoreLinq.Test/PadStartTest.cs | 6 +-- MoreLinq.Test/PadTest.cs | 3 +- MoreLinq.Test/PermutationsTest.cs | 8 ++-- MoreLinq.Test/RandomSubsetTest.cs | 24 +++++------- MoreLinq.Test/RandomTest.cs | 24 ++++++------ MoreLinq.Test/RepeatTest.cs | 6 +-- MoreLinq.Test/RightJoinTest.cs | 20 ++++++---- MoreLinq.Test/ScanByTest.cs | 4 +- MoreLinq.Test/ScanTest.cs | 5 +-- MoreLinq.Test/SegmentTest.cs | 16 ++++---- MoreLinq.Test/SequenceTest.cs | 2 +- MoreLinq.Test/SliceTest.cs | 2 +- MoreLinq.Test/SortedMergeTest.cs | 5 ++- MoreLinq.Test/StartsWithTest.cs | 14 +++---- MoreLinq.Test/SubjectTest.cs | 4 +- MoreLinq.Test/SubsetTest.cs | 12 +++--- MoreLinq.Test/TakeEveryTest.cs | 8 ++-- MoreLinq.Test/TestingSequence.cs | 2 +- MoreLinq.Test/Throws.cs | 52 +++++++++++++++++++++++++ MoreLinq.Test/ToArrayByIndexTest.cs | 16 ++++---- MoreLinq.Test/ToDataTableTest.cs | 36 +++++++++--------- MoreLinq.Test/TransposeTest.cs | 12 +++--- MoreLinq.Test/WindowLeftTest.cs | 4 +- MoreLinq.Test/WindowRightTest.cs | 4 +- MoreLinq.Test/WindowTest.cs | 4 +- MoreLinq.Test/ZipLongestTest.cs | 4 +- MoreLinq.Test/ZipShortestTest.cs | 5 +-- 57 files changed, 392 insertions(+), 384 deletions(-) delete mode 100644 MoreLinq.Test/AssertThrowsArgument.cs create mode 100644 MoreLinq.Test/Throws.cs diff --git a/MoreLinq.Test/AcquireTest.cs b/MoreLinq.Test/AcquireTest.cs index 4e687078b..c29d09933 100644 --- a/MoreLinq.Test/AcquireTest.cs +++ b/MoreLinq.Test/AcquireTest.cs @@ -57,7 +57,7 @@ public void AcquireSome() () => throw new TestException(), () => c = new Disposable()); - Assert.Throws(() => allocators.Acquire()); + Assert.That(allocators.Acquire, Throws.TypeOf()); Assert.That(a, Is.Not.Null); Assert.That(a.Disposed, Is.True); diff --git a/MoreLinq.Test/AggregateRightTest.cs b/MoreLinq.Test/AggregateRightTest.cs index 4128577d8..434d16c93 100644 --- a/MoreLinq.Test/AggregateRightTest.cs +++ b/MoreLinq.Test/AggregateRightTest.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; [TestFixture] @@ -28,8 +27,8 @@ public class AggregateRightTest [Test] public void AggregateRightWithEmptySequence() { - Assert.Throws( - () => new int[0].AggregateRight((a, b) => a + b)); + Assert.That(() => new int[0].AggregateRight((a, b) => a + b), + Throws.InvalidOperationException); } [Test] diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index c38ac9661..a9bf9ca53 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -27,10 +27,10 @@ public class AssertCountTest public void AssertCountNegativeCount() { var source = new object[0]; - AssertThrowsArgument.OutOfRangeException("count", () => - source.AssertCount(-1)); - AssertThrowsArgument.OutOfRangeException("count", () => - source.AssertCount(-1, BreakingFunc.Of())); + Assert.That(() => source.AssertCount(-1), + Throws.ArgumentOutOfRangeException("count")); + Assert.That(() => source.AssertCount(-1, BreakingFunc.Of()), + Throws.ArgumentOutOfRangeException("count")); } [Test] @@ -42,46 +42,47 @@ public void AssertCountSequenceWithMatchingLength() [Test] public void AssertCountShortSequence() { - Assert.Throws(() => - "foo,bar,baz".GenerateSplits(',').AssertCount(4).Consume()); + Assert.That(() => "foo,bar,baz".GenerateSplits(',').AssertCount(4).Consume(), + Throws.TypeOf()); } [Test] public void AssertCountLongSequence() { - Assert.Throws(() => - "foo,bar,baz".GenerateSplits(',').AssertCount(2).Consume()); + Assert.That(() => "foo,bar,baz".GenerateSplits(',').AssertCount(2).Consume(), + Throws.TypeOf()); } - [Test] - public void AssertCountDefaultExceptionMessageVariesWithCase() + [TestCase(4, "Sequence contains too few elements when exactly 4 were expected.")] + [TestCase(2, "Sequence contains too many elements when exactly 2 were expected.")] + public void AssertCountDefaultExceptionMessageVariesWithCase(int count, string expectedMessage) { var tokens = "foo,bar,baz".GenerateSplits(','); - var e1 = Assert.Throws(() => tokens.AssertCount(4).Consume()); - var e2 = Assert.Throws(() => tokens.AssertCount(2).Consume()); - Assert.That(e1.Message, Is.Not.EqualTo(e2.Message)); + + Assert.That(() => tokens.AssertCount(count).Consume(), + Throws.TypeOf().With.Message.EqualTo(expectedMessage)); } [Test] public void AssertCountLongSequenceWithErrorSelector() { - var e = - Assert.Throws(() => - "foo,bar,baz".GenerateSplits(',').AssertCount(2, (cmp, count) => new TestException(cmp, count)) - .Consume()); - Assert.That(e.Cmp, Is.GreaterThan(0)); - Assert.That(e.Count, Is.EqualTo(2)); + Assert.That(() => + "foo,bar,baz".GenerateSplits(',').AssertCount(2, (cmp, count) => new TestException(cmp, count)) + .Consume(), + Throws.TypeOf() + .With.Property(nameof(TestException.Cmp)).GreaterThan(0) + .And.Count.EqualTo(2)); } [Test] public void AssertCountShortSequenceWithErrorSelector() { - var e = - Assert.Throws(() => - "foo,bar,baz".GenerateSplits(',').AssertCount(4, (cmp, count) => new TestException(cmp, count)) - .Consume()); - Assert.That(e.Cmp, Is.LessThan(0)); - Assert.That(e.Count, Is.EqualTo(4)); + Assert.That(() => + "foo,bar,baz".GenerateSplits(',').AssertCount(4, (cmp, count) => new TestException(cmp, count)) + .Consume(), + Throws.TypeOf() + .With.Property(nameof(TestException.Cmp)).LessThan(0) + .And.Count.EqualTo(4)); } sealed class TestException : Exception @@ -121,8 +122,8 @@ public void AssertCountWithMismatchingCollectionCount(int sourceCount, int count { var xs = new int[sourceCount]; using var enumerator = xs.AssertCount(count).GetEnumerator(); - var e = Assert.Throws(() => enumerator.MoveNext()); - Assert.That(e.Message, Is.EqualTo(message)); + Assert.That(enumerator.MoveNext, + Throws.TypeOf().With.Message.EqualTo(message)); } [Test] diff --git a/MoreLinq.Test/AssertTest.cs b/MoreLinq.Test/AssertTest.cs index a15f0d94e..ff77f609f 100644 --- a/MoreLinq.Test/AssertTest.cs +++ b/MoreLinq.Test/AssertTest.cs @@ -40,24 +40,27 @@ public void AssertSequenceWithValidAllElements() [Test] public void AssertSequenceWithValidSomeInvalidElements() { - Assert.Throws(() => - new[] { 2, 4, 6, 7, 8, 9 }.Assert(n => n % 2 == 0).Consume()); + var source = new[] { 2, 4, 6, 7, 8, 9 }; + Assert.That(() => source.Assert(n => n % 2 == 0).Consume(), + Throws.InvalidOperationException); } [Test] public void AssertSequenceWithInvalidElementsAndCustomErrorReturningNull() { - Assert.Throws(() => - new[] { 2, 4, 6, 7, 8, 9 }.Assert(n => n % 2 == 0, _ => null).Consume()); + var source = new[] { 2, 4, 6, 7, 8, 9 }; + Assert.That(() => source.Assert(n => n % 2 == 0, _ => null).Consume(), + Throws.InvalidOperationException); } [Test] public void AssertSequenceWithInvalidElementsAndCustomError() { - var e = - Assert.Throws(() => - new[] { 2, 4, 6, 7, 8, 9 }.Assert(n => n % 2 == 0, n => new ValueException(n)).Consume()); - Assert.That(e.Value, Is.EqualTo(7)); + var source = new[] { 2, 4, 6, 7, 8, 9 }; + Assert.That(() => + source.Assert(n => n % 2 == 0, n => new ValueException(n)).Consume(), + Throws.TypeOf() + .With.Property(nameof(ValueException.Value)).EqualTo(7)); } class ValueException : Exception diff --git a/MoreLinq.Test/AssertThrowsArgument.cs b/MoreLinq.Test/AssertThrowsArgument.cs deleted file mode 100644 index ade934394..000000000 --- a/MoreLinq.Test/AssertThrowsArgument.cs +++ /dev/null @@ -1,48 +0,0 @@ -#region License and Terms -// MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2017 George Vovos. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -namespace MoreLinq.Test -{ - using System; - using NUnit.Framework; - - sealed class AssertThrowsArgument - { - [Obsolete("This is redundant with the NullArgumentTest fixture.")] - public static void NullException(string expectedParamName, TestDelegate code) - { - Exception(expectedParamName, code); - } - - public static void Exception(string expectedParamName, TestDelegate code) - { - Exception(expectedParamName, code); - } - - public static void OutOfRangeException(string expectedParamName, TestDelegate code) - { - Exception(expectedParamName, code); - } - - static void Exception(string expectedParamName, TestDelegate code) where TActual : ArgumentException - { - var e = Assert.Throws(code); - - Assert.That(e.ParamName, Is.EqualTo(expectedParamName)); - } - } -} diff --git a/MoreLinq.Test/AtLeastTest.cs b/MoreLinq.Test/AtLeastTest.cs index 691b7ae99..f4ad24f88 100644 --- a/MoreLinq.Test/AtLeastTest.cs +++ b/MoreLinq.Test/AtLeastTest.cs @@ -25,71 +25,71 @@ public class AtLeastTest [Test] public void AtLeastWithNegativeCount() { - AssertThrowsArgument.OutOfRangeException("count", () => - new[] { 1 }.AtLeast(-1)); + Assert.That(() => new[] { 1 }.AtLeast(-1), + Throws.ArgumentOutOfRangeException("count")); } [Test] public void AtLeastWithEmptySequenceHasAtLeastZeroElements() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtLeast(0)); + Assert.That(xs.AtLeast(0), Is.True); } [Test] public void AtLeastWithEmptySequenceHasAtLeastOneElement() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsFalse(xs.AtLeast(1)); + Assert.That(xs.AtLeast(1), Is.False); } [Test] public void AtLeastWithEmptySequenceHasAtLeastManyElements() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsFalse(xs.AtLeast(2)); + Assert.That(xs.AtLeast(2), Is.False); } [Test] public void AtLeastWithSingleElementHasAtLeastZeroElements() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtLeast(0)); + Assert.That(xs.AtLeast(0), Is.True); } [Test] public void AtLeastWithSingleElementHasAtLeastOneElement() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtLeast(1)); + Assert.That(xs.AtLeast(1), Is.True); } [Test] public void AtLeastWithSingleElementHasAtLeastManyElements() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsFalse(xs.AtLeast(2)); + Assert.That(xs.AtLeast(2), Is.False); } [Test] public void AtLeastWithManyElementsHasAtLeastZeroElements() { foreach (var xs in new[] { 1, 2, 3 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtLeast(0)); + Assert.That(xs.AtLeast(0), Is.True); } [Test] public void AtLeastWithManyElementsHasAtLeastOneElement() { foreach (var xs in new[] { 1, 2, 3 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtLeast(1)); + Assert.That(xs.AtLeast(1), Is.True); } [Test] public void AtLeastWithManyElementsHasAtLeastManyElements() { foreach (var xs in new[] { 1, 2, 3 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtLeast(2)); + Assert.That(xs.AtLeast(2), Is.True); } [Test] @@ -98,7 +98,7 @@ public void AtLeastDoesNotIterateUnnecessaryElements() var source = MoreEnumerable.From(() => 1, () => 2, () => throw new TestException()); - Assert.IsTrue(source.AtLeast(2)); + Assert.That(source.AtLeast(2), Is.True); } } } diff --git a/MoreLinq.Test/AtMostTest.cs b/MoreLinq.Test/AtMostTest.cs index f16b9e41c..14135b930 100644 --- a/MoreLinq.Test/AtMostTest.cs +++ b/MoreLinq.Test/AtMostTest.cs @@ -25,50 +25,50 @@ public class AtMostTest [Test] public void AtMostWithNegativeCount() { - AssertThrowsArgument.OutOfRangeException("count", - () => new[] { 1 }.AtMost(-1)); + Assert.That(() => new[] { 1 }.AtMost(-1), + Throws.ArgumentOutOfRangeException("count")); } [Test] public void AtMostWithEmptySequenceHasAtMostZeroElements() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtMost(0)); + Assert.That(xs.AtMost(0), Is.True); } [Test] public void AtMostWithEmptySequenceHasAtMostOneElement() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtMost(1)); + Assert.That(xs.AtMost(1), Is.True); } [Test] public void AtMostWithSingleElementHasAtMostZeroElements() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsFalse(xs.AtMost(0)); + Assert.That(xs.AtMost(0), Is.False); } [Test] public void AtMostWithSingleElementHasAtMostOneElement() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtMost(1)); + Assert.That(xs.AtMost(1), Is.True); } [Test] public void AtMostWithSingleElementHasAtMostManyElements() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.AtMost(2)); + Assert.That(xs.AtMost(2), Is.True); } [Test] public void AtMostWithManyElementsHasAtMostOneElements() { foreach (var xs in new[] { 1, 2, 3 }.ArrangeCollectionTestCases()) - Assert.IsFalse(xs.AtMost(1)); + Assert.That(xs.AtMost(1), Is.False); } [Test] @@ -78,7 +78,7 @@ public void AtMostDoesNotIterateUnnecessaryElements() () => 2, () => 3, () => throw new TestException()); - Assert.IsFalse(source.AtMost(2)); + Assert.That(source.AtMost(2), Is.False); } } } diff --git a/MoreLinq.Test/BacksertTest.cs b/MoreLinq.Test/BacksertTest.cs index 511a2f88f..a26e6ac8f 100644 --- a/MoreLinq.Test/BacksertTest.cs +++ b/MoreLinq.Test/BacksertTest.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using System.Collections.Generic; using NUnit.Framework; @@ -33,8 +32,8 @@ public void BacksertIsLazy() [Test] public void BacksertWithNegativeIndex() { - AssertThrowsArgument.OutOfRangeException("index", () => - Enumerable.Range(1, 10).Backsert(new[] { 97, 98, 99 }, -1)); + Assert.That(() => Enumerable.Range(1, 10).Backsert(new[] { 97, 98, 99 }, -1), + Throws.ArgumentOutOfRangeException("index")); } [TestCase(new[] { 1, 2, 3 }, 4, new[] { 9 })] @@ -45,7 +44,8 @@ public void BacksertWithIndexGreaterThanSourceLength(int[] seq1, int index, int[ var result = test1.Backsert(test2, index); - Assert.Throws(() => result.ElementAt(0)); + Assert.That(() => result.ElementAt(0), + Throws.ArgumentOutOfRangeException()); } [TestCase(new[] { 1, 2, 3 }, 0, new[] { 8, 9 }, ExpectedResult = new[] { 1, 2, 3, 8, 9 })] diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index a459ee2dd..ef923b9dd 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -27,8 +27,8 @@ public class BatchTest [TestCase(-1)] public void BatchBadSize(int size) { - AssertThrowsArgument.OutOfRangeException("size", () => - new object[0].Batch(size)); + Assert.That(() => new object[0].Batch(size), + Throws.ArgumentOutOfRangeException("size")); } [Test] @@ -153,10 +153,10 @@ public class BatchPoolTest [TestCase(-1)] public void BatchBadSize(int size) { - AssertThrowsArgument.OutOfRangeException("size", () => - new object[0].Batch(size, ArrayPool.Shared, - BreakingFunc.Of, IEnumerable>(), - BreakingFunc.Of, object>())); + Assert.That(() => new object[0].Batch(size, ArrayPool.Shared, + BreakingFunc.Of, IEnumerable>(), + BreakingFunc.Of, object>()), + Throws.ArgumentOutOfRangeException("size")); } [Test] diff --git a/MoreLinq.Test/CartesianTest.cs b/MoreLinq.Test/CartesianTest.cs index 19bab4e85..4277eb7bf 100644 --- a/MoreLinq.Test/CartesianTest.cs +++ b/MoreLinq.Test/CartesianTest.cs @@ -144,7 +144,7 @@ public void TestCartesianProductCombinations() // ensure that all "cells" were visited by the cartesian product foreach (var coord in result) expectedSet[coord.A][coord.B] = true; - Assert.IsTrue(expectedSet.SelectMany(x => x).All(z => z)); + Assert.That(expectedSet.SelectMany(x => x).All(z => z), Is.True); } /// diff --git a/MoreLinq.Test/CountBetweenTest.cs b/MoreLinq.Test/CountBetweenTest.cs index 2fc5fe4cc..6e57ba32c 100644 --- a/MoreLinq.Test/CountBetweenTest.cs +++ b/MoreLinq.Test/CountBetweenTest.cs @@ -25,29 +25,29 @@ public class CountBetweenTest [Test] public void CountBetweenWithNegativeMin() { - AssertThrowsArgument.OutOfRangeException("min", () => - new[] { 1 }.CountBetween(-1, 0)); + Assert.That(() => new[] { 1 }.CountBetween(-1, 0), + Throws.ArgumentOutOfRangeException("min")); } [Test] public void CountBetweenWithNegativeMax() { - AssertThrowsArgument.OutOfRangeException("max", () => - new[] { 1 }.CountBetween(0, -1)); + Assert.That(() => new[] { 1 }.CountBetween(0, -1), + Throws.ArgumentOutOfRangeException("max")); } [Test] public void CountBetweenWithMaxLesserThanMin() { - AssertThrowsArgument.OutOfRangeException("max", () => - new[] { 1 }.CountBetween(1, 0)); + Assert.That(() => new[] { 1 }.CountBetween(1, 0), + Throws.ArgumentOutOfRangeException("max")); } [Test] public void CountBetweenWithMaxEqualsMin() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.CountBetween(1, 1)); + Assert.That(xs.CountBetween(1, 1), Is.True); } [TestCase(1, 2, 4, false)] @@ -69,7 +69,7 @@ public void CountBetweenDoesNotIterateUnnecessaryElements() () => 3, () => 4, () => throw new TestException()); - Assert.False(source.CountBetween(2, 3)); + Assert.That(source.CountBetween(2, 3), Is.False); } } } diff --git a/MoreLinq.Test/EndsWithTest.cs b/MoreLinq.Test/EndsWithTest.cs index 7d4c1cb14..316dc7271 100644 --- a/MoreLinq.Test/EndsWithTest.cs +++ b/MoreLinq.Test/EndsWithTest.cs @@ -52,13 +52,13 @@ public bool EndsWithWithStrings(string first, string second) [Test] public void EndsWithReturnsTrueIfBothEmpty() { - Assert.True(new int[0].EndsWith(new int[0])); + Assert.That(new int[0].EndsWith(new int[0]), Is.True); } [Test] public void EndsWithReturnsFalseIfOnlyFirstIsEmpty() { - Assert.False(new int[0].EndsWith(new[] {1,2,3})); + Assert.That(new int[0].EndsWith(new[] {1,2,3}), Is.False); } [TestCase("", "", ExpectedResult = true)] @@ -85,10 +85,10 @@ public void EndsWithUsesSpecifiedEqualityComparerOrDefault() var first = new[] {1,2,3}; var second = new[] {4,5,6}; - Assert.False(first.EndsWith(second)); - Assert.False(first.EndsWith(second, null)); - Assert.False(first.EndsWith(second, EqualityComparer.Create(delegate { return false; }))); - Assert.True(first.EndsWith(second, EqualityComparer.Create(delegate { return true; }))); + Assert.That(first.EndsWith(second), Is.False); + Assert.That(first.EndsWith(second, null), Is.False); + Assert.That(first.EndsWith(second, EqualityComparer.Create(delegate { return false; })), Is.False); + Assert.That(first.EndsWith(second, EqualityComparer.Create(delegate { return true; })), Is.True); } [TestCase(SourceKind.BreakingCollection)] @@ -98,7 +98,7 @@ public void EndsWithUsesCollectionsCountToAvoidUnnecessaryIteration(SourceKind s var first = new[] { 1, 2 }.ToSourceKind(sourceKind); var second = new[] { 1, 2, 3 }.ToSourceKind(sourceKind); - Assert.False(first.EndsWith(second)); + Assert.That(first.EndsWith(second), Is.False); } } } diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index c90c2b4e6..c0d18b0af 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -19,7 +19,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; using Tuple = System.ValueTuple; @@ -33,8 +32,8 @@ public void BothSequencesDisposedWithUnequalLengthsAndLongerFirst() using var shorter = TestingSequence.Of(1, 2); // Yes, this will throw... but then we should still have disposed both sequences - Assert.Throws(() => - longer.EquiZip(shorter, (x, y) => x + y).Consume()); + Assert.That(() => longer.EquiZip(shorter, (x, y) => x + y).Consume(), + Throws.InvalidOperationException); } [Test] @@ -44,8 +43,8 @@ public void BothSequencesDisposedWithUnequalLengthsAndShorterFirst() using var shorter = TestingSequence.Of(1, 2); // Yes, this will throw... but then we should still have disposed both sequences - Assert.Throws(() => - shorter.EquiZip(longer, (x, y) => x + y).Consume()); + Assert.That(() => shorter.EquiZip(longer, (x, y) => x + y).Consume(), + Throws.InvalidOperationException); } [Test] @@ -61,8 +60,7 @@ public void ZipWithFirstSequenceShorterThanSecondFailStrategy() { var zipped = new[] { 1, 2 }.EquiZip(new[] { 4, 5, 6 }, Tuple.Create); Assert.That(zipped, Is.Not.Null); - Assert.Throws(() => - zipped.Consume()); + Assert.That(zipped.Consume, Throws.InvalidOperationException); } [Test] @@ -70,8 +68,7 @@ public void ZipWithFirstSequnceLongerThanSecondFailStrategy() { var zipped = new[] { 1, 2, 3 }.EquiZip(new[] { 4, 5 }, Tuple.Create); Assert.That(zipped, Is.Not.Null); - Assert.Throws(() => - zipped.Consume()); + Assert.That(zipped.Consume, Throws.InvalidOperationException); } [Test] @@ -91,8 +88,8 @@ public void MoveNextIsNotCalledUnnecessarily() () => throw new TestException()) .AsTestingSequence(); - Assert.Throws(() => - s1.EquiZip(s2, s3, (x, y, z) => x + y + z).Consume()); + Assert.That(() => s1.EquiZip(s2, s3, (x, y, z) => x + y + z).Consume(), + Throws.InvalidOperationException); } [Test] @@ -100,8 +97,8 @@ public void ZipDisposesInnerSequencesCaseGetEnumeratorThrows() { using var s1 = TestingSequence.Of(1, 2); - Assert.Throws(() => - s1.EquiZip(new BreakingSequence(), Tuple.Create).Consume()); + Assert.That(() => s1.EquiZip(new BreakingSequence(), Tuple.Create).Consume(), + Throws.InvalidOperationException); } } } diff --git a/MoreLinq.Test/ExactlyTest.cs b/MoreLinq.Test/ExactlyTest.cs index 938847f14..0d765d139 100644 --- a/MoreLinq.Test/ExactlyTest.cs +++ b/MoreLinq.Test/ExactlyTest.cs @@ -25,36 +25,36 @@ public class ExactlyTest [Test] public void ExactlyWithNegativeCount() { - AssertThrowsArgument.OutOfRangeException("count", () => - new[] { 1 }.Exactly(-1)); + Assert.That(() => new[] { 1 }.Exactly(-1), + Throws.ArgumentOutOfRangeException("count")); } [Test] public void ExactlyWithEmptySequenceHasExactlyZeroElements() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsTrue(xs.Exactly(0)); + Assert.That(xs.Exactly(0), Is.True); } [Test] public void ExactlyWithEmptySequenceHasExactlyOneElement() { foreach (var xs in Enumerable.Empty().ArrangeCollectionTestCases()) - Assert.IsFalse(xs.Exactly(1)); + Assert.That(xs.Exactly(1), Is.False); } [Test] public void ExactlyWithSingleElementHasExactlyOneElements() { foreach (var xs in new[] { 1 }.ArrangeCollectionTestCases()) - Assert.IsTrue(xs.Exactly(1)); + Assert.That(xs.Exactly(1), Is.True); } [Test] public void ExactlyWithManyElementHasExactlyOneElement() { foreach (var xs in new[] { 1, 2, 3 }.ArrangeCollectionTestCases()) - Assert.IsFalse(xs.Exactly(1)); + Assert.That(xs.Exactly(1), Is.False); } [Test] @@ -64,7 +64,7 @@ public void ExactlyDoesNotIterateUnnecessaryElements() () => 2, () => 3, () => throw new TestException()); - Assert.IsFalse(source.Exactly(2)); + Assert.That(source.Exactly(2), Is.False); } } } diff --git a/MoreLinq.Test/ExcludeTest.cs b/MoreLinq.Test/ExcludeTest.cs index 61cd5241f..07e54cb2a 100644 --- a/MoreLinq.Test/ExcludeTest.cs +++ b/MoreLinq.Test/ExcludeTest.cs @@ -40,8 +40,8 @@ public void TestExcludeIsLazy() [Test] public void TestExcludeNegativeStartIndexException() { - AssertThrowsArgument.OutOfRangeException("startIndex",() => - Enumerable.Range(1, 10).Exclude(-10, 10)); + Assert.That(() => Enumerable.Range(1, 10).Exclude(-10, 10), + Throws.ArgumentOutOfRangeException("startIndex")); } /// @@ -50,8 +50,8 @@ public void TestExcludeNegativeStartIndexException() [Test] public void TestExcludeNegativeCountException() { - AssertThrowsArgument.OutOfRangeException("count",() => - Enumerable.Range(1, 10).Exclude(0, -5)); + Assert.That(() => Enumerable.Range(1, 10).Exclude(0, -5), + Throws.ArgumentOutOfRangeException("count")); } /// diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index 84b9c0a93..2cbbdb940 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -257,8 +257,8 @@ public void FlattenInterruptedIterationDisposesInnerSequences() using var inner3 = TestingSequence.Of(6, inner2, 7); using var source = TestingSequence.Of(inner1, inner3); - Assert.Throws(() => - source.Flatten().Consume()); + Assert.That(() => source.Flatten().Consume(), + Throws.TypeOf()); } [Test] @@ -291,8 +291,8 @@ public void FlattenEvaluatesInnerSequencesLazily() Assert.That(result.Take(10), Is.EqualTo(expectations)); - Assert.Throws(() => - source.Flatten().ElementAt(11)); + Assert.That(() => source.Flatten().ElementAt(11), + Throws.TypeOf()); } // Flatten(this IEnumerable source, Func selector) diff --git a/MoreLinq.Test/FoldTest.cs b/MoreLinq.Test/FoldTest.cs index 0c1c7185a..25b2d8101 100644 --- a/MoreLinq.Test/FoldTest.cs +++ b/MoreLinq.Test/FoldTest.cs @@ -26,22 +26,22 @@ public class FoldTest [Test] public void FoldWithTooFewItems() { - Assert.Throws(() => - Enumerable.Range(1, 3).Fold(BreakingFunc.Of())); + Assert.That(() => Enumerable.Range(1, 3).Fold(BreakingFunc.Of()), + Throws.TypeOf()); } [Test] public void FoldWithEmptySequence() { - Assert.Throws(() => - Enumerable.Empty().Fold(BreakingFunc.Of())); + Assert.That(() => Enumerable.Empty().Fold(BreakingFunc.Of()), + Throws.TypeOf()); } [Test] public void FoldWithTooManyItems() { - Assert.Throws(() => - Enumerable.Range(1, 3).Fold(BreakingFunc.Of())); + Assert.That(() => Enumerable.Range(1, 3).Fold(BreakingFunc.Of()), + Throws.TypeOf()); } [Test] diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index e4cc3dd81..9a73c6ba9 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -50,10 +50,10 @@ public void FullGroupJoinsResults(OverloadCase overloadCase) Assert.That(result.Keys.Count, Is.EqualTo(3)); - Assert.IsEmpty(result[1].Second); + Assert.That(result[1].Second, Is.Empty); result[1].First.AssertSequenceEqual(1); - Assert.IsEmpty(result[3].First); + Assert.That(result[3].First, Is.Empty); result[3].Second.AssertSequenceEqual(3); result[2].First.AssertSequenceEqual(2); @@ -71,10 +71,10 @@ public void FullGroupJoinsEmptyLeft(OverloadCase overloadCase) Assert.That(result.Keys.Count, Is.EqualTo(2)); - Assert.IsEmpty(result[2].First); + Assert.That(result[2].First, Is.Empty); Assert.That(result[2].Second.Single(), Is.EqualTo(2)); - Assert.IsEmpty(result[3].First); + Assert.That(result[3].First, Is.Empty); Assert.That(result[3].Second.Single(), Is.EqualTo(3)); } @@ -90,10 +90,10 @@ public void FullGroupJoinsEmptyRight(OverloadCase overloadCase) Assert.That(result.Keys.Count, Is.EqualTo(2)); Assert.That(result[2].First.Single(), Is.EqualTo(2)); - Assert.IsEmpty(result[2].Second); + Assert.That(result[2].Second, Is.Empty); Assert.That(result[3].First.Single(), Is.EqualTo(3)); - Assert.IsEmpty(result[3].Second); + Assert.That(result[3].Second, Is.Empty); } [TestCase(CustomResult)] diff --git a/MoreLinq.Test/FullJoinTest.cs b/MoreLinq.Test/FullJoinTest.cs index 88d22b6f2..f648727de 100644 --- a/MoreLinq.Test/FullJoinTest.cs +++ b/MoreLinq.Test/FullJoinTest.cs @@ -32,11 +32,12 @@ public void FullJoinWithHomogeneousSequencesIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.FullJoin(ys, e => e, BreakingFunc.Of(), BreakingFunc.Of(), - BreakingFunc.Of())); + BreakingFunc.Of()), + Throws.Nothing); } [Test] @@ -45,12 +46,13 @@ public void FullJoinWithHomogeneousSequencesWithComparerIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.FullJoin(ys, e => e, BreakingFunc.Of(), BreakingFunc.Of(), BreakingFunc.Of(), - comparer: null)); + comparer: null), + Throws.Nothing); } [Test] @@ -59,11 +61,12 @@ public void FullJoinIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.FullJoin(ys, x => x, y => y.GetHashCode(), BreakingFunc.Of(), BreakingFunc.Of(), - BreakingFunc.Of())); + BreakingFunc.Of()), + Throws.Nothing); } [Test] @@ -72,12 +75,13 @@ public void FullJoinWithComparerIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.FullJoin(ys, x => x, y => y.GetHashCode(), BreakingFunc.Of(), BreakingFunc.Of(), BreakingFunc.Of(), - comparer: null)); + comparer: null), + Throws.Nothing); } [Test] diff --git a/MoreLinq.Test/IndexByTest.cs b/MoreLinq.Test/IndexByTest.cs index 2a02d7474..da62a738a 100644 --- a/MoreLinq.Test/IndexByTest.cs +++ b/MoreLinq.Test/IndexByTest.cs @@ -116,8 +116,8 @@ public void IndexBytDoesNotIterateUnnecessaryElements() KeyValuePair.Create(1, "bob" ), KeyValuePair.Create(0, "davi" )); - Assert.Throws(() => - result.ElementAt(5)); + Assert.That(() => result.ElementAt(5), + Throws.TypeOf()); } } } diff --git a/MoreLinq.Test/InsertTest.cs b/MoreLinq.Test/InsertTest.cs index b8d51f514..7f9af900f 100644 --- a/MoreLinq.Test/InsertTest.cs +++ b/MoreLinq.Test/InsertTest.cs @@ -25,8 +25,8 @@ public class InsertTest [Test] public void InsertWithNegativeIndex() { - AssertThrowsArgument.OutOfRangeException("index", () => - Enumerable.Range(1, 10).Insert(new[] { 97, 98, 99 }, -1)); + Assert.That(() => Enumerable.Range(1, 10).Insert(new[] { 97, 98, 99 }, -1), + Throws.ArgumentOutOfRangeException("index")); } [TestCase(7)] @@ -42,9 +42,8 @@ public void InsertWithIndexGreaterThanSourceLengthMaterialized(int count) var result = test1.Insert(test2, count + 1); - AssertThrowsArgument.OutOfRangeException("index", () => - result.ForEach((e, index) => - Assert.That(e, Is.EqualTo(seq1[index])))); + Assert.That(() => result.ForEach((e, index) => Assert.That(e, Is.EqualTo(seq1[index]))), + Throws.ArgumentOutOfRangeException("index")); } [TestCase(7)] diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index b201a776e..ad9b02c6a 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; /// @@ -46,7 +45,8 @@ public void TestInterleaveDisposesOnErrorAtGetEnumerator() var sequenceB = new BreakingSequence(); // Expected and thrown by BreakingSequence - Assert.Throws(() => sequenceA.Interleave(sequenceB).Consume()); + Assert.That(() => sequenceA.Interleave(sequenceB).Consume(), + Throws.InvalidOperationException); } /// @@ -60,7 +60,8 @@ public void TestInterleaveDisposesOnErrorAtMoveNext() using var sequenceB = MoreEnumerable.From(() => throw new TestException()).AsTestingSequence(); // Expected and thrown by sequenceB - Assert.Throws(() => sequenceA.Interleave(sequenceB).Consume()); + Assert.That(() => sequenceA.Interleave(sequenceB).Consume(), + Throws.TypeOf()); } /// diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index 192d9028c..fbd0262ff 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -43,8 +43,8 @@ public void TestLagIsLazy() [Test] public void TestLagNegativeOffsetException() { - AssertThrowsArgument.OutOfRangeException("offset",() => - Enumerable.Repeat(1, 10).Lag(-10, (val, _) => val)); + Assert.That(() => Enumerable.Repeat(1, 10).Lag(-10, (val, _) => val), + Throws.ArgumentOutOfRangeException("offset")); } /// @@ -53,8 +53,8 @@ public void TestLagNegativeOffsetException() [Test] public void TestLagZeroOffset() { - AssertThrowsArgument.OutOfRangeException("offset", () => - Enumerable.Range(1, 10).Lag(0, (val, lagVal) => val + lagVal)); + Assert.That(() => Enumerable.Range(1, 10).Lag(0, (val, lagVal) => val + lagVal), + Throws.ArgumentOutOfRangeException("offset")); } /// @@ -115,7 +115,7 @@ public void TestLagPassesCorrectLagValueOffsetBy1() var result = sequence.Lag(1, (a, b) => new { A = a, B = b }); Assert.That(result.Count(), Is.EqualTo(count)); - Assert.IsTrue(result.All(x => x.B == (x.A - 1))); + Assert.That(result.All(x => x.B == (x.A - 1)), Is.True); } /// @@ -130,8 +130,8 @@ public void TestLagPassesCorrectLagValuesOffsetBy2() var result = sequence.Lag(2, (a, b) => new { A = a, B = b }); Assert.That(result.Count(), Is.EqualTo(count)); - Assert.IsTrue(result.Skip(2).All(x => x.B == (x.A - 2))); - Assert.IsTrue(result.Take(2).All(x => (x.A - x.B) == x.A)); + Assert.That(result.Skip(2).All(x => x.B == (x.A - 2)), Is.True); + Assert.That(result.Take(2).All(x => (x.A - x.B) == x.A), Is.True); } [Test] diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index 4574cd333..ccff7fb03 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -43,8 +43,8 @@ public void TestLeadIsLazy() [Test] public void TestLeadNegativeOffset() { - AssertThrowsArgument.OutOfRangeException("offset", () => - Enumerable.Range(1, 100).Lead(-5, (val, leadVal) => val + leadVal)); + Assert.That(() => Enumerable.Range(1, 100).Lead(-5, (val, leadVal) => val + leadVal), + Throws.ArgumentOutOfRangeException("offset")); } /// @@ -53,8 +53,8 @@ public void TestLeadNegativeOffset() [Test] public void TestLeadZeroOffset() { - AssertThrowsArgument.OutOfRangeException("offset", () => - Enumerable.Range(1, 100).Lead(0, (val, leadVal) => val + leadVal)); + Assert.That(() => Enumerable.Range(1, 100).Lead(0, (val, leadVal) => val + leadVal), + Throws.ArgumentOutOfRangeException("offset")); } /// @@ -116,7 +116,7 @@ public void TestLeadPassesCorrectValueOffsetBy1() var result = sequence.Lead(1, count + 1, (val, leadVal) => new { A = val, B = leadVal }); Assert.That(result.Count(), Is.EqualTo(count)); - Assert.IsTrue(result.All(x => x.B == (x.A + 1))); + Assert.That(result.All(x => x.B == (x.A + 1)), Is.True); } /// @@ -132,8 +132,8 @@ public void TestLeadPassesCorrectValueOffsetBy2() var result = sequence.Lead(2, leadDefault, (val, leadVal) => new { A = val, B = leadVal }); Assert.That(result.Count(), Is.EqualTo(count)); - Assert.IsTrue(result.Take(count - 2).All(x => x.B == (x.A + 2))); - Assert.IsTrue(result.Skip(count - 2).All(x => x.B == leadDefault && x.A is count or count - 1)); + Assert.That(result.Take(count - 2).All(x => x.B == (x.A + 2)), Is.True); + Assert.That(result.Skip(count - 2).All(x => x.B == leadDefault && x.A is count or count - 1), Is.True); } [Test] diff --git a/MoreLinq.Test/LeftJoinTest.cs b/MoreLinq.Test/LeftJoinTest.cs index 9bd5d7bdc..76214fb8d 100644 --- a/MoreLinq.Test/LeftJoinTest.cs +++ b/MoreLinq.Test/LeftJoinTest.cs @@ -32,10 +32,11 @@ public void LeftJoinWithHomogeneousSequencesIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.LeftJoin(ys, e => e, BreakingFunc.Of(), - BreakingFunc.Of())); + BreakingFunc.Of()), + Throws.Nothing); } [Test] @@ -44,11 +45,12 @@ public void LeftJoinWithHomogeneousSequencesWithComparerIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.LeftJoin(ys, e => e, BreakingFunc.Of(), BreakingFunc.Of(), - comparer: null)); + comparer: null), + Throws.Nothing); } [Test] @@ -57,10 +59,11 @@ public void LeftJoinIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.LeftJoin(ys, x => x, y => y.GetHashCode(), BreakingFunc.Of(), - BreakingFunc.Of())); + BreakingFunc.Of()), + Throws.Nothing); } [Test] @@ -69,11 +72,12 @@ public void LeftJoinWithComparerIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.LeftJoin(ys, x => x, y => y.GetHashCode(), BreakingFunc.Of(), BreakingFunc.Of(), - comparer: null)); + comparer: null), + Throws.Nothing); } [Test] diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 7cab0440c..59c1bc712 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -19,7 +19,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; [TestFixture] @@ -85,16 +84,18 @@ public void WithComparerReturnsMaximum() public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.First(strings.MaxBy(s => s.Length))); + Assert.That(() => + MoreEnumerable.First(strings.MaxBy(s => s.Length)), + Throws.InvalidOperationException); } [Test] public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.First(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer))); + Assert.That(() => + MoreEnumerable.First(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer)), + Throws.InvalidOperationException); } } @@ -155,16 +156,18 @@ public void WithComparerReturnsMaximumPerComparer() public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.Last(strings.MaxBy(s => s.Length))); + Assert.That(() => + MoreEnumerable.Last(strings.MaxBy(s => s.Length)), + Throws.InvalidOperationException); } [Test] public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.Last(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer))); + Assert.That(() => + MoreEnumerable.Last(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer)), + Throws.InvalidOperationException); } } diff --git a/MoreLinq.Test/MemoizeTest.cs b/MoreLinq.Test/MemoizeTest.cs index 80efcabf4..2d94bdf42 100644 --- a/MoreLinq.Test/MemoizeTest.cs +++ b/MoreLinq.Test/MemoizeTest.cs @@ -15,6 +15,10 @@ // limitations under the License. #endregion +// Following warnings are disabled due to false negatives: +#pragma warning disable NUnit2040 // Non-reference types for SameAs constraint +#pragma warning disable NUnit2020 // Incompatible types for SameAs constraint + namespace MoreLinq.Test { using System; @@ -127,13 +131,15 @@ public void MemoizeEnumeratesOnlyOnce() [Test] public void MemoizeDoesNotDisposeOnEarlyExitByDefault() { - Assert.Throws(() => + static void Act() { using var xs = new[] { 1, 2 }.AsTestingSequence(); xs.Memoize().Take(1).Consume(); xs.Memoize().Take(1).Consume(); - }); + } + + Assert.That(Act, Throws.TypeOf()); } [Test] @@ -220,8 +226,9 @@ public static void MemoizeIteratorThrowsWhenCacheDisposedDuringIteration() disposable.Dispose(); - var e = Assert.Throws(() => reader.Read()); - Assert.That(e.ObjectName, Is.EqualTo("MemoizedEnumerable")); + Assert.That(reader.Read, + Throws.ObjectDisposedException + .With.Property(nameof(ObjectDisposedException.ObjectName)).EqualTo("MemoizedEnumerable")); } [Test] @@ -251,13 +258,11 @@ IEnumerable TestSequence() using (var r2 = memoized.Read()) { Assert.That(r1.Read(), Is.EqualTo(r2.Read())); - var e1 = Assert.Throws(() => r1.Read()); - Assert.That(e1, Is.SameAs(error)); + Assert.That(r1.Read, Throws.TypeOf().And.SameAs(error)); Assert.That(xs.IsDisposed, Is.True); - var e2 = Assert.Throws(() => r2.Read()); - Assert.That(e2, Is.SameAs(error)); + Assert.That(r2.Read, Throws.TypeOf().And.SameAs(error)); } using (var r1 = memoized.Read()) Assert.That(r1.Read(), Is.EqualTo(123)); @@ -282,13 +287,9 @@ IEnumerable TestSequence() using (var r1 = memoized.Read()) using (var r2 = memoized.Read()) { - var e1 = Assert.Throws(() => r1.Read()); - Assert.That(e1, Is.SameAs(error)); - + Assert.That(r1.Read, Throws.TypeOf().And.SameAs(error)); Assert.That(xs.IsDisposed, Is.True); - - var e2 = Assert.Throws(() => r2.Read()); - Assert.That(e2, Is.SameAs(error)); + Assert.That(r2.Read, Throws.TypeOf().And.SameAs(error)); } using (var r1 = memoized.Read()) @@ -309,10 +310,7 @@ public void MemoizeRethrowsErrorDuringFirstIterationStartToAllIterationsUntilDis var memo = source.Memoize(); for (var i = 0; i < 2; i++) - { - var e = Assert.Throws(() => memo.First()); - Assert.That(e, Is.SameAs(error)); - } + Assert.That(memo.First, Throws.TypeOf().And.SameAs(error)); ((IDisposable) memo).Dispose(); Assert.That(memo.Single(), Is.EqualTo(obj)); diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index 8482d29b3..d8745c1a6 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -19,7 +19,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; [TestFixture] @@ -85,16 +84,16 @@ public void WithComparerReturnsMinimum() public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.First(strings.MinBy(s => s.Length))); + Assert.That(() => MoreEnumerable.First(strings.MinBy(s => s.Length)), + Throws.InvalidOperationException); } [Test] public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.First(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer))); + Assert.That(() => MoreEnumerable.First(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer)), + Throws.InvalidOperationException); } } @@ -155,16 +154,16 @@ public void WithComparerReturnsMinimumPerComparer() public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.Last(strings.MinBy(s => s.Length))); + Assert.That(() => MoreEnumerable.Last(strings.MinBy(s => s.Length)), + Throws.InvalidOperationException); } [Test] public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); - Assert.Throws(() => - MoreEnumerable.Last(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer))); + Assert.That(() => MoreEnumerable.Last(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer)), + Throws.InvalidOperationException); } } diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 535a85744..c27c04025 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -58,7 +58,7 @@ - + diff --git a/MoreLinq.Test/MoveTest.cs b/MoreLinq.Test/MoveTest.cs index f001a01b7..abe85b2ef 100644 --- a/MoreLinq.Test/MoveTest.cs +++ b/MoreLinq.Test/MoveTest.cs @@ -27,22 +27,22 @@ public class MoveTest [Test] public void MoveWithNegativeFromIndex() { - AssertThrowsArgument.OutOfRangeException("fromIndex", () => - new[] { 1 }.Move(-1, 0, 0)); + Assert.That(() => new[] { 1 }.Move(-1, 0, 0), + Throws.ArgumentOutOfRangeException("fromIndex")); } [Test] public void MoveWithNegativeCount() { - AssertThrowsArgument.OutOfRangeException("count", () => - new[] { 1 }.Move(0, -1, 0)); + Assert.That(() => new[] { 1 }.Move(0, -1, 0), + Throws.ArgumentOutOfRangeException("count")); } [Test] public void MoveWithNegativeToIndex() { - AssertThrowsArgument.OutOfRangeException("toIndex", () => - new[] { 1 }.Move(0, 0, -1)); + Assert.That(() => new[] { 1 }.Move(0, 0, -1), + Throws.ArgumentOutOfRangeException("toIndex")); } [Test] diff --git a/MoreLinq.Test/PadStartTest.cs b/MoreLinq.Test/PadStartTest.cs index a1124712c..2282a28ba 100644 --- a/MoreLinq.Test/PadStartTest.cs +++ b/MoreLinq.Test/PadStartTest.cs @@ -31,7 +31,7 @@ public class PadStartTest [Test] public void PadStartWithNegativeWidth() { - AssertThrowsArgument.Exception("width", () => new int[0].PadStart(-1)); + Assert.That(() => new int[0].PadStart(-1), Throws.ArgumentException("width")); } [Test] @@ -66,7 +66,7 @@ public void ReferenceTypeElements(ICollection source, int width, IEnume [Test] public void PadStartWithPaddingWithNegativeWidth() { - AssertThrowsArgument.Exception("width", () => new int[0].PadStart(-1, 1)); + Assert.That(() => new int[0].PadStart(-1, 1), Throws.ArgumentException("width")); } [Test] @@ -101,7 +101,7 @@ public void ReferenceTypeElements(ICollection source, int width, IEnumer [Test] public void PadStartWithSelectorWithNegativeWidth() { - AssertThrowsArgument.Exception("width", () => new int[0].PadStart(-1, x => x)); + Assert.That(() => new int[0].PadStart(-1, x => x), Throws.ArgumentException("width")); } [Test] diff --git a/MoreLinq.Test/PadTest.cs b/MoreLinq.Test/PadTest.cs index 1c1c2533e..f9677a22d 100644 --- a/MoreLinq.Test/PadTest.cs +++ b/MoreLinq.Test/PadTest.cs @@ -27,8 +27,7 @@ public class PadTest [Test] public void PadNegativeWidth() { - AssertThrowsArgument.Exception("width",() => - new object[0].Pad(-1)); + Assert.That(() => new object[0].Pad(-1), Throws.ArgumentException("width")); } [Test] diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index 768de6738..67e5c4446 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -63,7 +63,7 @@ public void TestCardinalityTwoPermutation() var permutations = set.Permutations(); // should contain two results: the set itself and its reverse - Assert.IsTrue(permutations.Count() == 2); + Assert.That(permutations.Count(), Is.EqualTo(2)); Assert.That(permutations.First(), Is.EqualTo(set)); Assert.That(permutations.Last(), Is.EqualTo(set.Reverse())); } @@ -90,7 +90,7 @@ public void TestCardinalityThreePermutation() // should contain six permutations (as defined above) Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); - Assert.IsTrue(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y))))); + Assert.That(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y)))), Is.True); } /// @@ -133,7 +133,7 @@ public void TestCardinalityFourPermutation() // should contain six permutations (as defined above) Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); - Assert.IsTrue(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y))))); + Assert.That(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y)))), Is.True); } /// @@ -185,7 +185,7 @@ public void TestPermutationsAreIndependent() var listPermutations = new List>(); listPermutations.AddRange(permutedSets); - Assert.IsNotEmpty(listPermutations); + Assert.That(listPermutations, Is.Not.Empty); for (var i = 0; i < listPermutations.Count; i++) { diff --git a/MoreLinq.Test/RandomSubsetTest.cs b/MoreLinq.Test/RandomSubsetTest.cs index 7f0e5edf5..265abdbbb 100644 --- a/MoreLinq.Test/RandomSubsetTest.cs +++ b/MoreLinq.Test/RandomSubsetTest.cs @@ -43,8 +43,8 @@ public void TestRandomSubsetIsLazy() [Test] public void TestRandomSubsetNegativeSubsetSize() { - AssertThrowsArgument.OutOfRangeException("subsetSize", () => - Enumerable.Range(1, 10).RandomSubset(-5)); + Assert.That(() => Enumerable.Range(1, 10).RandomSubset(-5), + Throws.ArgumentOutOfRangeException("subsetSize")); } /// @@ -53,8 +53,8 @@ public void TestRandomSubsetNegativeSubsetSize() [Test] public void TestRandomSubsetNegativeSubsetSize2() { - AssertThrowsArgument.OutOfRangeException("subsetSize", () => - Enumerable.Range(1, 10).RandomSubset(-1, new Random())); + Assert.That(() => Enumerable.Range(1, 10).RandomSubset(-1, new Random()), + Throws.ArgumentOutOfRangeException("subsetSize")); } /// @@ -113,10 +113,8 @@ public void TestRandomSubsetLongerThanSequence() const int subsetSize = count + 5; var sequence = Enumerable.Range(1, count); - AssertThrowsArgument.OutOfRangeException("subsetSize", () => - { - sequence.RandomSubset(subsetSize).Consume(); - }); + Assert.That(() => sequence.RandomSubset(subsetSize).Consume(), + Throws.ArgumentOutOfRangeException("subsetSize")); } /// @@ -130,10 +128,8 @@ public void TestRandomSubsetLongerThanSequence2() const int subsetSize = count + 5; var sequence = Enumerable.Range(1, count); - AssertThrowsArgument.OutOfRangeException("subsetSize", () => - { - sequence.RandomSubset(subsetSize, new Random(1234)).Consume(); - }); + Assert.That(() => sequence.RandomSubset(subsetSize, new Random(1234)).Consume(), + Throws.ArgumentOutOfRangeException("subsetSize")); } /// @@ -186,10 +182,10 @@ public void TestRandomSubsetIsUnbiased() // ensure that wth increasing trial size the a RSD% continually decreases for (var j = 0; j < rsdResults.Length - 1; j++) - Assert.Less(rsdResults[j + 1], rsdResults[j]); + Assert.That(rsdResults[j + 1], Is.LessThan(rsdResults[j])); // ensure that the RSD% for the 5M trial size is < 1.0 (this is somewhat arbitrary) - Assert.Less(rsdResults.Last(), 1.0); + Assert.That(rsdResults.Last(), Is.LessThan(1.0)); // for sanity, we output the RSD% values as a cross-check, the expected result should be // that the RSD% rapidly decreases and eventually drops below 1.0 diff --git a/MoreLinq.Test/RandomTest.cs b/MoreLinq.Test/RandomTest.cs index 6cf9f4ab9..3a25d5b4b 100644 --- a/MoreLinq.Test/RandomTest.cs +++ b/MoreLinq.Test/RandomTest.cs @@ -36,10 +36,10 @@ public class RandomTest public void TestNegativeMaxValueException() { const int maxValue = -10; - Assert.Less(maxValue, 0); + Assert.That(maxValue, Is.LessThan(0)); - AssertThrowsArgument.OutOfRangeException("maxValue",() => - MoreEnumerable.Random(maxValue)); + Assert.That(() => MoreEnumerable.Random(maxValue), + Throws.ArgumentOutOfRangeException("maxValue")); } /// @@ -52,10 +52,10 @@ public void TestMinValueGreaterThanMaxValueException() const int minValue = 100; const int maxValue = 10; - Assert.Greater(minValue, maxValue); + Assert.That(minValue, Is.GreaterThan(maxValue)); - AssertThrowsArgument.OutOfRangeException("minValue",() => - MoreEnumerable.Random(minValue, maxValue)); + Assert.That(() => MoreEnumerable.Random(minValue, maxValue), + Throws.ArgumentOutOfRangeException("minValue")); } /// @@ -70,8 +70,8 @@ public void TestRandomDouble() // NOTE: Unclear what should actually be verified here... some additional thought needed. Assert.That(resultA.Count(), Is.EqualTo(RandomTrials)); Assert.That(resultB.Count(), Is.EqualTo(RandomTrials)); - Assert.IsTrue(resultA.All(x => x is >= 0.0 and < 1.0)); - Assert.IsTrue(resultB.All(x => x is >= 0.0 and < 1.0)); + Assert.That(resultA.All(x => x is >= 0.0 and < 1.0), Is.True); + Assert.That(resultB.All(x => x is >= 0.0 and < 1.0), Is.True); } /// @@ -86,8 +86,8 @@ public void TestRandomMaxConstraint() Assert.That(resultA.Count(), Is.EqualTo(RandomTrials)); Assert.That(resultB.Count(), Is.EqualTo(RandomTrials)); - Assert.IsTrue(resultA.All(x => x < max)); - Assert.IsTrue(resultB.All(x => x < max)); + Assert.That(resultA.All(x => x < max), Is.True); + Assert.That(resultB.All(x => x < max), Is.True); } /// @@ -103,8 +103,8 @@ public void TestRandomMinMaxConstraint() Assert.That(resultA.Count(), Is.EqualTo(RandomTrials)); Assert.That(resultB.Count(), Is.EqualTo(RandomTrials)); - Assert.IsTrue(resultA.All(x => x is >= min and < max)); - Assert.IsTrue(resultB.All(x => x is >= min and < max)); + Assert.That(resultA.All(x => x is >= min and < max), Is.True); + Assert.That(resultB.All(x => x is >= min and < max), Is.True); } /// diff --git a/MoreLinq.Test/RepeatTest.cs b/MoreLinq.Test/RepeatTest.cs index 20399644d..12c8b2bf6 100644 --- a/MoreLinq.Test/RepeatTest.cs +++ b/MoreLinq.Test/RepeatTest.cs @@ -62,8 +62,8 @@ public void TestRepeatBehavior() [Test] public void TestNegativeRepeatCount() { - AssertThrowsArgument.OutOfRangeException("count", () => - Enumerable.Range(1, 10).Repeat(-3)); + Assert.That(() => Enumerable.Range(1, 10).Repeat(-3), + Throws.ArgumentOutOfRangeException("count")); } /// @@ -77,7 +77,7 @@ public void TestRepeatForeverBehaviorSingleElementList() var result = sequence.Repeat(); - Assert.IsTrue(result.Take(100).All(x => x == value)); + Assert.That(result.Take(100).All(x => x == value), Is.True); } /// diff --git a/MoreLinq.Test/RightJoinTest.cs b/MoreLinq.Test/RightJoinTest.cs index 9d7076077..a11386a45 100644 --- a/MoreLinq.Test/RightJoinTest.cs +++ b/MoreLinq.Test/RightJoinTest.cs @@ -32,10 +32,11 @@ public void RightJoinWithHomogeneousSequencesIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.RightJoin(ys, e => e, BreakingFunc.Of(), - BreakingFunc.Of())); + BreakingFunc.Of()), + Throws.Nothing); } [Test] @@ -44,11 +45,12 @@ public void RightJoinWithHomogeneousSequencesWithComparerIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.RightJoin(ys, e => e, BreakingFunc.Of(), BreakingFunc.Of(), - comparer: null)); + comparer: null), + Throws.Nothing); } [Test] @@ -57,10 +59,11 @@ public void RightJoinIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.RightJoin(ys, x => x.GetHashCode(), y => y, BreakingFunc.Of(), - BreakingFunc.Of())); + BreakingFunc.Of()), + Throws.Nothing); } [Test] @@ -69,11 +72,12 @@ public void RightJoinWithComparerIsLazy() var xs = new BreakingSequence(); var ys = new BreakingSequence(); - Assert.DoesNotThrow(() => + Assert.That(() => xs.RightJoin(ys, x => x.GetHashCode(), y => y, BreakingFunc.Of(), BreakingFunc.Of(), - comparer: null)); + comparer: null), + Throws.Nothing); } [Test] diff --git a/MoreLinq.Test/ScanByTest.cs b/MoreLinq.Test/ScanByTest.cs index dc263c9e1..343b7bd9d 100644 --- a/MoreLinq.Test/ScanByTest.cs +++ b/MoreLinq.Test/ScanByTest.cs @@ -151,8 +151,8 @@ public void ScanByDoesNotIterateUnnecessaryElements() KeyValuePair.Create('b', 1), KeyValuePair.Create('d', 0)); - Assert.Throws(() => - result.ElementAt(5)); + Assert.That(() => result.ElementAt(5), + Throws.TypeOf()); } } } diff --git a/MoreLinq.Test/ScanTest.cs b/MoreLinq.Test/ScanTest.cs index e94c47dee..15ec08a83 100644 --- a/MoreLinq.Test/ScanTest.cs +++ b/MoreLinq.Test/ScanTest.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; [TestFixture] @@ -48,7 +47,7 @@ public void ScanDoesNotIterateExtra() { var sequence = Enumerable.Range(1, 3).Concat(new BreakingSequence()).Scan(SampleData.Plus); var gold = new[] {1, 3, 6}; - Assert.Throws(sequence.Consume); + Assert.That(sequence.Consume, Throws.InvalidOperationException); sequence.Take(3).AssertSequenceEqual(gold); } @@ -77,7 +76,7 @@ public void SeededScanDoesNotIterateExtra() { var sequence = Enumerable.Range(1, 3).Concat(new BreakingSequence()).Scan(0, SampleData.Plus); var gold = new[] { 0, 1, 3, 6 }; - Assert.Throws(sequence.Consume); + Assert.That(sequence.Consume, Throws.InvalidOperationException); sequence.Take(4).AssertSequenceEqual(gold); } diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index e072ccbd0..44217e0b6 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -76,7 +76,7 @@ public void TestSegmentIsIdempotent() { for (var i = 0; i < 2; i++) { - Assert.IsTrue(segment.Any()); + Assert.That(segment.Any(), Is.True); Assert.That(segment.Single(), Is.EqualTo(value)); } } @@ -94,9 +94,9 @@ public void TestFirstSegmentNeverEmpty() var resultB = sequence.Segment((_, _) => true); var resultC = sequence.Segment((_, _, _) => true); - Assert.IsTrue(resultA.First().Any()); - Assert.IsTrue(resultB.First().Any()); - Assert.IsTrue(resultC.First().Any()); + Assert.That(resultA.First().Any(), Is.True); + Assert.That(resultB.First().Any(), Is.True); + Assert.That(resultC.First().Any(), Is.True); } /// @@ -110,9 +110,9 @@ public void TestSegmentationStartsWithSecondItem() var resultB = sequence.Segment(BreakingFunc.Of()); var resultC = sequence.Segment(BreakingFunc.Of()); - Assert.IsTrue(resultA.Any()); - Assert.IsTrue(resultB.Any()); - Assert.IsTrue(resultC.Any()); + Assert.That(resultA.Any(), Is.True); + Assert.That(resultB.Any(), Is.True); + Assert.That(resultC.Any(), Is.True); } /// @@ -146,7 +146,7 @@ public void VerifyCanSegmentByPrevious() var result = sequence.Segment((curr, prev, _) => curr != prev); Assert.That(result.Count(), Is.EqualTo(sequence.Distinct().Count())); - Assert.IsTrue(result.All(s => s.Count() == repCount)); + Assert.That(result.All(s => s.Count() == repCount), Is.True); } static IEnumerable Seq(params T[] values) => values; diff --git a/MoreLinq.Test/SequenceTest.cs b/MoreLinq.Test/SequenceTest.cs index 01f49ad16..ad01a274d 100644 --- a/MoreLinq.Test/SequenceTest.cs +++ b/MoreLinq.Test/SequenceTest.cs @@ -134,7 +134,7 @@ public void SequenceWithStepZero(int start, int stop) { var result = MoreEnumerable.Sequence(start, stop, 0); - Assert.IsTrue(result.Take(100).All(x => x == start)); + Assert.That(result.Take(100).All(x => x == start), Is.True); } } } diff --git a/MoreLinq.Test/SliceTest.cs b/MoreLinq.Test/SliceTest.cs index 4cc5efd86..e5fd11c56 100644 --- a/MoreLinq.Test/SliceTest.cs +++ b/MoreLinq.Test/SliceTest.cs @@ -143,7 +143,7 @@ public void TestSliceOptimization(SourceKind sourceKind) var result = sequence.Slice(sliceStart, sliceCount); Assert.That(result.Count(), Is.EqualTo(sliceCount)); - CollectionAssert.AreEqual(Enumerable.Range(5, sliceCount), result); + Assert.That(Enumerable.Range(5, sliceCount), Is.EqualTo(result)); } } } diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index 7c7cae4d3..3ef82578d 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -49,8 +49,9 @@ public void TestSortedMergeDisposesOnError() using var sequenceA = TestingSequence.Of(); // Expected and thrown by BreakingSequence - Assert.Throws(() => - sequenceA.SortedMerge(OrderByDirection.Ascending, new BreakingSequence()).Consume()); + Assert.That(() => sequenceA.SortedMerge(OrderByDirection.Ascending, new BreakingSequence()) + .Consume(), + Throws.InvalidOperationException); } /// diff --git a/MoreLinq.Test/StartsWithTest.cs b/MoreLinq.Test/StartsWithTest.cs index 3aeb83e43..99d3f01c0 100644 --- a/MoreLinq.Test/StartsWithTest.cs +++ b/MoreLinq.Test/StartsWithTest.cs @@ -52,13 +52,13 @@ public bool StartsWithWithStrings(string first, string second) [Test] public void StartsWithReturnsTrueIfBothEmpty() { - Assert.True(new int[0].StartsWith(new int[0])); + Assert.That(new int[0].StartsWith(new int[0]), Is.True); } [Test] public void StartsWithReturnsFalseIfOnlyFirstIsEmpty() { - Assert.False(new int[0].StartsWith(new[] {1,2,3})); + Assert.That(new int[0].StartsWith(new[] {1,2,3}), Is.False); } [TestCase("", "", ExpectedResult = true)] @@ -85,10 +85,10 @@ public void StartsWithUsesSpecifiedEqualityComparerOrDefault() var first = new[] {1,2,3}; var second = new[] {4,5,6}; - Assert.False(first.StartsWith(second)); - Assert.False(first.StartsWith(second, null)); - Assert.False(first.StartsWith(second, EqualityComparer.Create(delegate { return false; }))); - Assert.True(first.StartsWith(second, EqualityComparer.Create(delegate { return true; }))); + Assert.That(first.StartsWith(second), Is.False); + Assert.That(first.StartsWith(second, null), Is.False); + Assert.That(first.StartsWith(second, EqualityComparer.Create(delegate { return false; })), Is.False); + Assert.That(first.StartsWith(second, EqualityComparer.Create(delegate { return true; })), Is.True); } [TestCase(SourceKind.BreakingCollection)] @@ -98,7 +98,7 @@ public void StartsWithUsesCollectionsCountToAvoidUnnecessaryIteration(SourceKind var first = new[] { 1, 2 }.ToSourceKind(sourceKind); var second = new[] { 1, 2, 3 }.ToSourceKind(sourceKind); - Assert.False(first.StartsWith(second)); + Assert.That(first.StartsWith(second), Is.False); } } } diff --git a/MoreLinq.Test/SubjectTest.cs b/MoreLinq.Test/SubjectTest.cs index cd571589d..334c82730 100644 --- a/MoreLinq.Test/SubjectTest.cs +++ b/MoreLinq.Test/SubjectTest.cs @@ -36,8 +36,8 @@ static IDisposable Subscribe(IObservable subject, public void SubscribeWithNullObserverThrows() { var subject = new Subject(); - var e = Assert.Throws(() => subject.Subscribe(null)); - Assert.That(e.ParamName, Is.EqualTo("observer")); + Assert.That(() => subject.Subscribe(null), + Throws.ArgumentNullException("observer")); } [Test] diff --git a/MoreLinq.Test/SubsetTest.cs b/MoreLinq.Test/SubsetTest.cs index 201a1ff36..e08174ef8 100644 --- a/MoreLinq.Test/SubsetTest.cs +++ b/MoreLinq.Test/SubsetTest.cs @@ -45,8 +45,8 @@ public void TestNegativeSubsetSize() const int count = 10; var sequence = Enumerable.Range(1, count); - AssertThrowsArgument.OutOfRangeException("subsetSize",() => - sequence.Subsets(-5)); + Assert.That(() => sequence.Subsets(-5), + Throws.ArgumentOutOfRangeException("subsetSize")); } /// @@ -59,10 +59,8 @@ public void TestSubsetLargerThanSequence() var sequence = Enumerable.Range(1, count); var result = sequence.Subsets(count + 5); - AssertThrowsArgument.OutOfRangeException("subsetSize", () => - { - result.Consume(); // this particular exception is deferred until sequence evaluation - }); + Assert.That(result.Consume, // this particular exception is deferred until sequence evaluation + Throws.ArgumentOutOfRangeException("subsetSize")); } /// @@ -90,7 +88,7 @@ public void TestSubsetsInIncreasingOrder() var prevSubset = Enumerable.Empty(); foreach (var subset in result) { - Assert.GreaterOrEqual(subset.Count, prevSubset.Count()); + Assert.That(subset.Count, Is.GreaterThanOrEqualTo(prevSubset.Count())); prevSubset = subset; } } diff --git a/MoreLinq.Test/TakeEveryTest.cs b/MoreLinq.Test/TakeEveryTest.cs index a10d9d5e9..a3fc857cf 100644 --- a/MoreLinq.Test/TakeEveryTest.cs +++ b/MoreLinq.Test/TakeEveryTest.cs @@ -25,15 +25,15 @@ public class TakeEveryTest [Test] public void TakeEveryNegativeSkip() { - AssertThrowsArgument.OutOfRangeException("step",() => - new object[0].TakeEvery(-1)); + Assert.That(() => new object[0].TakeEvery(-1), + Throws.ArgumentOutOfRangeException("step")); } [Test] public void TakeEveryOutOfRangeZeroStep() { - AssertThrowsArgument.OutOfRangeException("step", () => - new object[0].TakeEvery(0)); + Assert.That(() => new object[0].TakeEvery(0), + Throws.ArgumentOutOfRangeException("step")); } [Test] diff --git a/MoreLinq.Test/TestingSequence.cs b/MoreLinq.Test/TestingSequence.cs index 9a6e3ed7f..aa8bf84ee 100644 --- a/MoreLinq.Test/TestingSequence.cs +++ b/MoreLinq.Test/TestingSequence.cs @@ -58,7 +58,7 @@ void AssertDisposed() { if (_disposed == null) return; - Assert.IsTrue(_disposed, "Expected sequence to be disposed."); + Assert.That(_disposed, Is.True, "Expected sequence to be disposed."); _disposed = null; } diff --git a/MoreLinq.Test/Throws.cs b/MoreLinq.Test/Throws.cs new file mode 100644 index 000000000..ef4326408 --- /dev/null +++ b/MoreLinq.Test/Throws.cs @@ -0,0 +1,52 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2022 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System; + using NUnit.Framework.Constraints; + + static class Throws + { + public static ThrowsNothingConstraint Nothing => NUnit.Framework.Throws.Nothing; + public static ExactTypeConstraint InvalidOperationException => NUnit.Framework.Throws.InvalidOperationException; + public static ExactTypeConstraint ObjectDisposedException => NUnit.Framework.Throws.TypeOf(); + + public static InstanceOfTypeConstraint InstanceOf() + where T : Exception => + NUnit.Framework.Throws.InstanceOf(); + + public static ExactTypeConstraint TypeOf() + where T : Exception => + NUnit.Framework.Throws.TypeOf(); + + public static EqualConstraint ArgumentException(string expectedParamName) => + NUnit.Framework.Throws.ArgumentException.With.ParamName().EqualTo(expectedParamName); + + public static EqualConstraint ArgumentNullException(string expectedParamName) => + NUnit.Framework.Throws.ArgumentNullException.With.ParamName().EqualTo(expectedParamName); + + public static ExactTypeConstraint ArgumentOutOfRangeException() => + NUnit.Framework.Throws.TypeOf(); + + public static EqualConstraint ArgumentOutOfRangeException(string expectedParamName) => + ArgumentOutOfRangeException().With.ParamName().EqualTo(expectedParamName); + + static ResolvableConstraintExpression ParamName(this ConstraintExpression constraint) => + constraint.Property(nameof(System.ArgumentException.ParamName)); + } +} diff --git a/MoreLinq.Test/ToArrayByIndexTest.cs b/MoreLinq.Test/ToArrayByIndexTest.cs index f5f06fb89..27d3567c4 100644 --- a/MoreLinq.Test/ToArrayByIndexTest.cs +++ b/MoreLinq.Test/ToArrayByIndexTest.cs @@ -68,11 +68,11 @@ public void ToArrayByIndexWithBadIndexSelectorThrows() { var input = new[] { 42 }; - Assert.Throws(() => - input.ToArrayByIndex(_ => -1)); + Assert.That(() => input.ToArrayByIndex(_ => -1), + Throws.TypeOf()); - Assert.Throws(() => - input.ToArrayByIndex(_ => -1, BreakingFunc.Of())); + Assert.That(() => input.ToArrayByIndex(_ => -1, BreakingFunc.Of()), + Throws.TypeOf()); } [TestCase(10, -1)] @@ -80,11 +80,11 @@ public void ToArrayByIndexWithBadIndexSelectorThrows() public void ToArrayByIndexWithLengthWithBadIndexSelectorThrows(int length, int badIndex) { var input = new[] { 42 }; - Assert.Throws(() => - input.ToArrayByIndex(length, _ => badIndex)); + Assert.That(() => input.ToArrayByIndex(length, _ => badIndex), + Throws.TypeOf()); - Assert.Throws(() => - input.ToArrayByIndex(10, _ => -1, BreakingFunc.Of())); + Assert.That(() => input.ToArrayByIndex(10, _ => -1, BreakingFunc.Of()), + Throws.TypeOf()); } [Test] diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index f095a93a3..13e3bf825 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -69,8 +69,8 @@ public void ToDataTableNullMemberExpressionMethod() { Expression> expression = null; - AssertThrowsArgument.Exception("expressions",() => - _testObjects.ToDataTable(expression)); + Assert.That(() => _testObjects.ToDataTable(expression), + Throws.ArgumentException("expressions")); } [Test] @@ -79,8 +79,8 @@ public void ToDataTableTableWithWrongColumnNames() var dt = new DataTable(); dt.Columns.Add("Test"); - AssertThrowsArgument.Exception("table",() => - _testObjects.ToDataTable(dt)); + Assert.That(() => _testObjects.ToDataTable(dt), + Throws.ArgumentException("table")); } [Test] @@ -89,37 +89,37 @@ public void ToDataTableTableWithWrongColumnDataType() var dt = new DataTable(); dt.Columns.Add("AString", typeof(int)); - AssertThrowsArgument.Exception("table",() => - _testObjects.ToDataTable(dt, t=>t.AString)); + Assert.That(() => _testObjects.ToDataTable(dt, t=>t.AString), + Throws.ArgumentException("table")); } [Test] public void ToDataTableMemberExpressionMethod() { - AssertThrowsArgument.Exception("lambda", () => - _testObjects.ToDataTable(t => t.ToString())); + Assert.That(() => _testObjects.ToDataTable(t => t.ToString()), + Throws.ArgumentException("lambda")); } [Test] public void ToDataTableMemberExpressionNonMember() { - AssertThrowsArgument.Exception("lambda", () => - _testObjects.ToDataTable(t => t.ToString().Length)); + Assert.That(() => _testObjects.ToDataTable(t => t.ToString().Length), + Throws.ArgumentException("lambda")); } [Test] public void ToDataTableMemberExpressionIndexer() { - AssertThrowsArgument.Exception("lambda",() => - _testObjects.ToDataTable(t => t[0])); + Assert.That(() => _testObjects.ToDataTable(t => t[0]), + Throws.ArgumentException("lambda")); } [Test] public void ToDataTableMemberExpressionStatic() { - AssertThrowsArgument.Exception("lambda", () => - _ = _testObjects.ToDataTable(_ => DateTime.Now)); + Assert.That(() => _ = _testObjects.ToDataTable(_ => DateTime.Now), + Throws.ArgumentException("lambda")); } [Test] @@ -140,7 +140,7 @@ public void ToDataTableSchemaInDeclarationOrder() Assert.That(dt.Columns[3].Caption, Is.EqualTo("ANullableGuidField")); Assert.That(dt.Columns[3].DataType, Is.EqualTo(typeof(Guid))); - Assert.IsTrue(dt.Columns[3].AllowDBNull); + Assert.That(dt.Columns[3].AllowDBNull, Is.True); Assert.That(dt.Columns.Count, Is.EqualTo(4)); } @@ -202,9 +202,9 @@ public void ToDataTableIgnoresStaticMembers() Assert.That(points.Columns.Count, Is.EqualTo(3)); DataColumn x, y, empty; - Assert.NotNull(x = points.Columns["X"]); - Assert.NotNull(y = points.Columns["Y"]); - Assert.NotNull(empty = points.Columns["IsEmpty"]); + Assert.That(x = points.Columns["X"], Is.Not.Null); + Assert.That(y = points.Columns["Y"], Is.Not.Null); + Assert.That(empty = points.Columns["IsEmpty"], Is.Not.Null); var row = points.Rows.Cast().Single(); Assert.That(row[x], Is.EqualTo(12)); Assert.That(row[y], Is.EqualTo(34)); diff --git a/MoreLinq.Test/TransposeTest.cs b/MoreLinq.Test/TransposeTest.cs index 5bdc2422a..f400a0c7c 100644 --- a/MoreLinq.Test/TransposeTest.cs +++ b/MoreLinq.Test/TransposeTest.cs @@ -38,8 +38,8 @@ public void TransposeWithOneNullRow() using var seq3 = TestingSequence.Of(30, 31, 32); using var matrix = TestingSequence.Of(seq1, seq2, seq3, null); - Assert.Throws(() => - matrix.Transpose().FirstOrDefault()); + Assert.That(() => matrix.Transpose().FirstOrDefault(), + Throws.TypeOf()); } [Test] @@ -174,8 +174,8 @@ public void TransposeConsumesRowsLazily() result.ElementAt(0).AssertSequenceEqual(10, 20, 30); - Assert.Throws(() => - result.ElementAt(1)); + Assert.That(() => result.ElementAt(1), + Throws.TypeOf()); } [Test] @@ -188,8 +188,8 @@ public void TransposeWithErroneousRowDisposesRowIterators() using var row3 = TestingSequence.Of(30, 32); using var matrix = TestingSequence.Of(row1, row2, row3); - Assert.Throws(() => - matrix.Transpose().Consume()); + Assert.That(() => matrix.Transpose().Consume(), + Throws.TypeOf()); } static bool IsPrime(int number) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index 10a49cfe1..e24cf2dbd 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -77,8 +77,8 @@ public void WindowModifiedDoesNotAffectPreviousWindow() [Test] public void WindowLeftWithNegativeWindowSize() { - AssertThrowsArgument.OutOfRangeException("size", () => - Enumerable.Repeat(1, 10).WindowLeft(-5)); + Assert.That(() => Enumerable.Repeat(1, 10).WindowLeft(-5), + Throws.ArgumentOutOfRangeException("size")); } [Test] diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index 6f2aa27df..57d5f5040 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -77,8 +77,8 @@ public void WindowModifiedDoesNotAffectPreviousWindow() [Test] public void WindowRightWithNegativeWindowSize() { - AssertThrowsArgument.OutOfRangeException("size", () => - Enumerable.Repeat(1, 10).WindowRight(-5)); + Assert.That(() => Enumerable.Repeat(1, 10).WindowRight(-5), + Throws.ArgumentOutOfRangeException("size")); } [Test] diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index e711dba11..fb5c93c97 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -87,8 +87,8 @@ public void TestWindowNegativeWindowSizeException() { var sequence = Enumerable.Repeat(1, 10); - AssertThrowsArgument.OutOfRangeException("size", () => - sequence.Window(-5)); + Assert.That(() => sequence.Window(-5), + Throws.ArgumentOutOfRangeException("size")); } /// diff --git a/MoreLinq.Test/ZipLongestTest.cs b/MoreLinq.Test/ZipLongestTest.cs index 5b4e74789..05fbd663c 100644 --- a/MoreLinq.Test/ZipLongestTest.cs +++ b/MoreLinq.Test/ZipLongestTest.cs @@ -80,8 +80,8 @@ public void ZipLongestDisposesInnerSequencesCaseGetEnumeratorThrows() { using var s1 = TestingSequence.Of(1, 2); - Assert.Throws(() => - s1.ZipLongest(new BreakingSequence(), Tuple.Create).Consume()); + Assert.That(() => s1.ZipLongest(new BreakingSequence(), Tuple.Create).Consume(), + Throws.InvalidOperationException); } } } diff --git a/MoreLinq.Test/ZipShortestTest.cs b/MoreLinq.Test/ZipShortestTest.cs index ae7de9f14..9c6c59f04 100644 --- a/MoreLinq.Test/ZipShortestTest.cs +++ b/MoreLinq.Test/ZipShortestTest.cs @@ -19,7 +19,6 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; using Tuple = System.ValueTuple; @@ -111,8 +110,8 @@ public void ZipShortestDisposesInnerSequencesCaseGetEnumeratorThrows() { using var s1 = TestingSequence.Of(1, 2); - Assert.Throws(() => - s1.ZipShortest(new BreakingSequence(), Tuple.Create).Consume()); + Assert.That(() => s1.ZipShortest(new BreakingSequence(), Tuple.Create).Consume(), + Throws.InvalidOperationException); } } } From 2914136999609d1862cb488abdc3340d3618a105 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 20 Dec 2022 21:16:37 +0100 Subject: [PATCH 144/157] Fix code alignment in "AggregateTest" --- MoreLinq.Test/AggregateTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 930522d58..94b6c4ade 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -52,8 +52,8 @@ from m in typeof(MoreEnumerable).GetMethods(BindingFlags.Public | BindingFlags.S Source = source, Expectation = sum, Instantiation = m.MakeGenericMethod(Enumerable.Repeat(typeof(int), m.GetGenericArguments().Length - 1) - .Append(typeof(int[])) // TResult - .ToArray()), + .Append(typeof(int[])) // TResult + .ToArray()), } into m let rst = m.Instantiation.GetParameters().Last().ParameterType From 79a2794871e65c046f9e9ec79c2f5894ba734736 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 20 Dec 2022 22:57:41 +0100 Subject: [PATCH 145/157] Fix nullability of "Rank" comparer argument This is a squashed merge of PR #913 that adds to #803. --- MoreLinq.Test/RankTest.cs | 2 ++ MoreLinq/Extensions.g.cs | 2 +- MoreLinq/Rank.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index 1187e408e..5fd0a78db 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 9e5b16a71..52ed15837 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -4634,7 +4634,7 @@ public static IEnumerable Rank(this IEnumerable source) /// A object that defines comparison semantics for the elements in the sequence /// A sequence of position integers representing the ranks of the corresponding items in the sequence - public static IEnumerable Rank(this IEnumerable source, IComparer comparer) + public static IEnumerable Rank(this IEnumerable source, IComparer? comparer) => MoreEnumerable.Rank(source, comparer); } diff --git a/MoreLinq/Rank.cs b/MoreLinq/Rank.cs index d2c81a382..4e5586382 100644 --- a/MoreLinq/Rank.cs +++ b/MoreLinq/Rank.cs @@ -43,7 +43,7 @@ public static IEnumerable Rank(this IEnumerable source) /// A object that defines comparison semantics for the elements in the sequence /// A sequence of position integers representing the ranks of the corresponding items in the sequence - public static IEnumerable Rank(this IEnumerable source, IComparer comparer) + public static IEnumerable Rank(this IEnumerable source, IComparer? comparer) { return source.RankBy(IdFn, comparer); } From 69cc9315088d2d0a5180a1cd4088e089fb1ae127 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 20 Dec 2022 22:56:01 +0100 Subject: [PATCH 146/157] Fix nullability of "ToDataTable" expressions arg --- MoreLinq.Test/ToDataTableTest.cs | 20 +++++++++++++------- MoreLinq/Extensions.ToDataTable.g.cs | 4 ++-- MoreLinq/ToDataTable.cs | 10 +++++----- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index 13e3bf825..66f9399d1 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +#nullable enable + namespace MoreLinq.Test { using System; @@ -51,6 +53,8 @@ public TestObject(int key) ANullableDecimal = key / 3; AString = "ABCDEFGHIKKLMNOPQRSTUVWXYSZ"; } + + public override string ToString() => nameof(TestObject); } @@ -67,9 +71,9 @@ public ToDataTableTest() [Test] public void ToDataTableNullMemberExpressionMethod() { - Expression> expression = null; + Expression>? expression = null; - Assert.That(() => _testObjects.ToDataTable(expression), + Assert.That(() => _testObjects.ToDataTable(expression!), Throws.ArgumentException("expressions")); } @@ -177,7 +181,7 @@ public void ToDataTableWithSchema() .Cast() .ToArray(); - vars.Select(e => new { Name = e.Key.ToString(), Value = e.Value.ToString() }) + vars.Select(e => new { Name = e.Key.ToString(), Value = e.Value!.ToString() }) .ToDataTable(dt, e => e.Name, e => e.Value); var rows = dt.Rows.Cast().ToArray(); @@ -201,10 +205,12 @@ public void ToDataTableIgnoresStaticMembers() var points = new[] { new Point(12, 34) }.ToDataTable(); Assert.That(points.Columns.Count, Is.EqualTo(3)); - DataColumn x, y, empty; - Assert.That(x = points.Columns["X"], Is.Not.Null); - Assert.That(y = points.Columns["Y"], Is.Not.Null); - Assert.That(empty = points.Columns["IsEmpty"], Is.Not.Null); + var x = points.Columns["X"]; + var y = points.Columns["Y"]; + var empty = points.Columns["IsEmpty"]; + Assert.That(x, Is.Not.Null); + Assert.That(y, Is.Not.Null); + Assert.That(empty, Is.Not.Null); var row = points.Rows.Cast().Single(); Assert.That(row[x], Is.EqualTo(12)); Assert.That(row[y], Is.EqualTo(34)); diff --git a/MoreLinq/Extensions.ToDataTable.g.cs b/MoreLinq/Extensions.ToDataTable.g.cs index 66c6f8931..b9165678e 100644 --- a/MoreLinq/Extensions.ToDataTable.g.cs +++ b/MoreLinq/Extensions.ToDataTable.g.cs @@ -68,7 +68,7 @@ public static DataTable ToDataTable(this IEnumerable source) /// /// This operator uses immediate execution. - public static DataTable ToDataTable(this IEnumerable source, params Expression>[] expressions) + public static DataTable ToDataTable(this IEnumerable source, params Expression>[] expressions) => MoreEnumerable.ToDataTable(source, expressions); /// /// Appends elements in the sequence as rows of a given object. @@ -101,7 +101,7 @@ public static TTable ToDataTable(this IEnumerable source, TTable t /// /// This operator uses immediate execution. - public static TTable ToDataTable(this IEnumerable source, TTable table, params Expression>[] expressions) + public static TTable ToDataTable(this IEnumerable source, TTable table, params Expression>[] expressions) where TTable : DataTable => MoreEnumerable.ToDataTable(source, table, expressions); diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index d04c66d3c..793eb6bc5 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -41,7 +41,7 @@ static partial class MoreEnumerable public static TTable ToDataTable(this IEnumerable source, TTable table) where TTable : DataTable { - return ToDataTable(source, table, EmptyArray>>.Value); + return ToDataTable(source, table, EmptyArray>>.Value); } /// @@ -57,7 +57,7 @@ public static TTable ToDataTable(this IEnumerable source, TTable t /// /// This operator uses immediate execution. - public static DataTable ToDataTable(this IEnumerable source, params Expression>[] expressions) + public static DataTable ToDataTable(this IEnumerable source, params Expression>[] expressions) { return ToDataTable(source, new DataTable(), expressions); } @@ -92,7 +92,7 @@ public static DataTable ToDataTable(this IEnumerable source) /// /// This operator uses immediate execution. - public static TTable ToDataTable(this IEnumerable source, TTable table, params Expression>[] expressions) + public static TTable ToDataTable(this IEnumerable source, TTable table, params Expression>[] expressions) where TTable : DataTable { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -100,7 +100,7 @@ public static TTable ToDataTable(this IEnumerable source, TTable t // TODO disallow null for "expressions" in next major update - expressions ??= EmptyArray>>.Value; + expressions ??= EmptyArray>>.Value; var members = PrepareMemberInfos(expressions).ToArray(); members = BuildOrBindSchema(table, members); @@ -130,7 +130,7 @@ public static TTable ToDataTable(this IEnumerable source, TTable t return table; } - static IEnumerable PrepareMemberInfos(ICollection>> expressions) + static IEnumerable PrepareMemberInfos(ICollection>> expressions) { // // If no lambda expressions supplied then reflect them off the source element type. From a8e13e0416026ea072b48ac7dba9e18a7b27e814 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 3 Jan 2023 18:19:46 +0100 Subject: [PATCH 147/157] Enable nullable context for entire solution This is a squashed merge of PR #915 that adds to #803. --- Directory.Build.props | 1 + MoreLinq.Test/AcquireTest.cs | 12 +-- MoreLinq.Test/AggregateTest.cs | 11 +-- MoreLinq.Test/AppendTest.cs | 2 +- MoreLinq.Test/AssertCountTest.cs | 2 +- MoreLinq.Test/AssertTest.cs | 2 +- MoreLinq.Test/Async/AsyncEnumerable.cs | 26 ++++--- MoreLinq.Test/BatchTest.cs | 12 ++- MoreLinq.Test/BreakingCollection.cs | 2 - MoreLinq.Test/BreakingSequence.cs | 9 ++- MoreLinq.Test/ChooseTest.cs | 2 +- MoreLinq.Test/CompareCountTest.cs | 8 +- MoreLinq.Test/Comparer.cs | 8 +- MoreLinq.Test/CountByTest.cs | 16 ++-- MoreLinq.Test/CountDownTest.cs | 10 +-- MoreLinq.Test/Enumerable.cs | 36 +++++---- MoreLinq.Test/EqualityComparer.cs | 10 +-- MoreLinq.Test/EquiZipTest.cs | 4 +- MoreLinq.Test/FlattenTest.cs | 2 - MoreLinq.Test/FullGroupJoinTest.cs | 2 - MoreLinq.Test/IndexByTest.cs | 21 +++-- MoreLinq.Test/InterleaveTest.cs | 2 +- MoreLinq.Test/LagTest.cs | 2 - MoreLinq.Test/LeadTest.cs | 2 - MoreLinq.Test/MaxByTest.cs | 2 - MoreLinq.Test/MemoizeTest.cs | 4 +- MoreLinq.Test/MinByTest.cs | 2 - MoreLinq.Test/MoreLinq.Test.csproj | 1 + MoreLinq.Test/NullArgumentTest.cs | 78 ++++++++++++------- MoreLinq.Test/OrderByTest.cs | 15 +++- MoreLinq.Test/PadStartTest.cs | 2 - MoreLinq.Test/PadTest.cs | 2 - MoreLinq.Test/PartialSortByTest.cs | 2 - MoreLinq.Test/PartialSortTest.cs | 2 - MoreLinq.Test/PartitionTest.cs | 2 - MoreLinq.Test/PermutationsTest.cs | 10 ++- MoreLinq.Test/PrependTest.cs | 2 +- MoreLinq.Test/RankTest.cs | 2 - MoreLinq.Test/ReturnTest.cs | 8 +- MoreLinq.Test/ScanByTest.cs | 34 ++++---- MoreLinq.Test/ScanTest.cs | 6 +- MoreLinq.Test/SequenceReader.cs | 70 ++++------------- MoreLinq.Test/SortedMergeTest.cs | 4 +- MoreLinq.Test/SubjectTest.cs | 22 +++--- MoreLinq.Test/TestingSequence.cs | 4 +- MoreLinq.Test/Throws.cs | 5 +- MoreLinq.Test/ToDelimitedStringTest.cs | 4 +- MoreLinq.Test/TraceTest.cs | 1 - MoreLinq.Test/TransposeTest.cs | 2 +- MoreLinq.Test/WatchableEnumerator.cs | 6 +- MoreLinq.Test/ZipLongestTest.cs | 4 +- MoreLinq.Test/ZipShortestTest.cs | 4 +- MoreLinq/MoreLinq.csproj | 1 - MoreLinq/Reactive/Subject.cs | 2 - .../MoreLinq.ExtensionsGenerator.csproj | 1 - 55 files changed, 245 insertions(+), 263 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f958a257d..11286bf3d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,7 @@ 11 + enable true diff --git a/MoreLinq.Test/AcquireTest.cs b/MoreLinq.Test/AcquireTest.cs index c29d09933..14025320b 100644 --- a/MoreLinq.Test/AcquireTest.cs +++ b/MoreLinq.Test/AcquireTest.cs @@ -26,9 +26,9 @@ public class AcquireTest [Test] public void AcquireAll() { - Disposable a = null; - Disposable b = null; - Disposable c = null; + Disposable? a = null; + Disposable? b = null; + Disposable? c = null; var allocators = MoreEnumerable.From(() => a = new Disposable(), () => b = new Disposable(), @@ -48,9 +48,9 @@ public void AcquireAll() [Test] public void AcquireSome() { - Disposable a = null; - Disposable b = null; - Disposable c = null; + Disposable? a = null; + Disposable? b = null; + Disposable? c = null; var allocators = MoreEnumerable.From(() => a = new Disposable(), () => b = new Disposable(), diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 94b6c4ade..690207c5b 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -65,10 +65,11 @@ into m AccumulatorCount = (m.Instantiation.GetParameters().Length - 2 /* source + resultSelector */) / 2 /* seed + accumulator */, ResultSelectorType = rst, Parameters = - rst.GetMethod("Invoke") - .GetParameters() - .Select(p => Expression.Parameter(p.ParameterType)) - .ToArray(), + rst.GetMethod("Invoke") is { } invoke + ? invoke.GetParameters() + .Select(p => Expression.Parameter(p.ParameterType)) + .ToArray() + : throw new Exception("""Method "Invoke" not found."""), } into m let resultSelector = @@ -96,7 +97,7 @@ into t select new TestCaseData(t.Method, t.Args).SetName(t.Name).Returns(t.Expectation); [TestCaseSource(nameof(AccumulatorsTestSource), new object[] { nameof(Accumulators), 10 })] - public object Accumulators(MethodInfo method, object[] args) => + public object? Accumulators(MethodInfo method, object[] args) => method.Invoke(null, args); [Test] diff --git a/MoreLinq.Test/AppendTest.cs b/MoreLinq.Test/AppendTest.cs index df46b13c7..580a74875 100644 --- a/MoreLinq.Test/AppendTest.cs +++ b/MoreLinq.Test/AppendTest.cs @@ -46,7 +46,7 @@ public void AppendWithEmptyHeadSequence() public void AppendWithNullTail() { var head = new[] { "first", "second" }; - string tail = null; + string? tail = null; var whole = head.Append(tail); whole.AssertSequenceEqual("first", "second", null); } diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index a9bf9ca53..b2b165564 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -106,7 +106,7 @@ public void AssertCountIsLazy() [Test] public void AssertCountWithCollectionIsLazy() { - new BreakingCollection(5).AssertCount(0); + new BreakingCollection(new int[5]).AssertCount(0); } [Test] diff --git a/MoreLinq.Test/AssertTest.cs b/MoreLinq.Test/AssertTest.cs index ff77f609f..e5bd40d9e 100644 --- a/MoreLinq.Test/AssertTest.cs +++ b/MoreLinq.Test/AssertTest.cs @@ -49,7 +49,7 @@ public void AssertSequenceWithValidSomeInvalidElements() public void AssertSequenceWithInvalidElementsAndCustomErrorReturningNull() { var source = new[] { 2, 4, 6, 7, 8, 9 }; - Assert.That(() => source.Assert(n => n % 2 == 0, _ => null).Consume(), + Assert.That(() => source.Assert(n => n % 2 == 0, _ => null!).Consume(), Throws.InvalidOperationException); } diff --git a/MoreLinq.Test/Async/AsyncEnumerable.cs b/MoreLinq.Test/Async/AsyncEnumerable.cs index 6fe745988..9e34f492a 100644 --- a/MoreLinq.Test/Async/AsyncEnumerable.cs +++ b/MoreLinq.Test/Async/AsyncEnumerable.cs @@ -141,7 +141,7 @@ public static IAsyncEnumerable Distinct(this IAsyncEnumerable< public static ValueTask ElementAtAsync(this IAsyncEnumerable source, int index) => LinqEnumerable.ElementAtAsync(source, index); - public static ValueTask ElementAtOrDefaultAsync(this IAsyncEnumerable source, int index) => + public static ValueTask ElementAtOrDefaultAsync(this IAsyncEnumerable source, int index) => LinqEnumerable.ElementAtOrDefaultAsync(source, index); public static IAsyncEnumerable Empty() => @@ -159,10 +159,10 @@ public static ValueTask FirstAsync(this IAsyncEnumerable FirstAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.FirstAsync(source, predicate); - public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source) => + public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source) => LinqEnumerable.FirstOrDefaultAsync(source); - public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => + public static ValueTask FirstOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.FirstOrDefaultAsync(source, predicate); public static IAsyncEnumerable GroupBy(this IAsyncEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) => @@ -213,10 +213,10 @@ public static ValueTask LastAsync(this IAsyncEnumerable LastAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.LastAsync(source, predicate); - public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source) => + public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source) => LinqEnumerable.LastOrDefaultAsync(source); - public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => + public static ValueTask LastOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.LastOrDefaultAsync(source, predicate); public static ValueTask LongCountAsync(this IAsyncEnumerable source) => @@ -411,10 +411,10 @@ public static ValueTask SingleAsync(this IAsyncEnumerable SingleAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.SingleAsync(source, predicate); - public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source) => + public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source) => LinqEnumerable.SingleOrDefaultAsync(source); - public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => + public static ValueTask SingleOrDefaultAsync(this IAsyncEnumerable source, Func predicate) => LinqEnumerable.SingleOrDefaultAsync(source, predicate); public static IAsyncEnumerable Skip(this IAsyncEnumerable source, int count) => @@ -510,16 +510,20 @@ public static IOrderedAsyncEnumerable ThenByDescending(t public static ValueTask ToArrayAsync(this IAsyncEnumerable source) => LinqEnumerable.ToArrayAsync(source); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, IEqualityComparer comparer) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, IEqualityComparer comparer) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector, comparer); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector, elementSelector); - public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) => + public static ValueTask> ToDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + where TKey: notnull => LinqEnumerable.ToDictionaryAsync(source, keySelector, elementSelector, comparer); public static ValueTask> ToListAsync(this IAsyncEnumerable source) => diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index ef923b9dd..3b50d7426 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -339,7 +339,7 @@ public void BatchBucketSelectorCurrentList() { var input = TestingSequence.Of(1, 2, 3, 4, 5, 6, 7, 8, 9); using var pool = new TestArrayPool(); - int[] bucketSelectorItems = null; + int[]? bucketSelectorItems = null; var result = input.Batch(4, pool, current => bucketSelectorItems = current.ToArray(), _ => 0); @@ -356,18 +356,16 @@ public void BatchBucketSelectorCurrentList() sealed class TestArrayPool : ArrayPool, IDisposable { - T[] _pooledArray; - T[] _rentedArray; + T[]? _pooledArray; + T[]? _rentedArray; public override T[] Rent(int minimumLength) { if (_pooledArray is null && _rentedArray is null) _pooledArray = new T[minimumLength * 2]; - if (_pooledArray is null) - throw new InvalidOperationException("The pool is exhausted."); - - (_pooledArray, _rentedArray) = (null, _pooledArray); + (_pooledArray, _rentedArray) = + (null, _pooledArray ?? throw new InvalidOperationException("The pool is exhausted.")); return _rentedArray; } diff --git a/MoreLinq.Test/BreakingCollection.cs b/MoreLinq.Test/BreakingCollection.cs index a628424cf..7cb34e6b9 100644 --- a/MoreLinq.Test/BreakingCollection.cs +++ b/MoreLinq.Test/BreakingCollection.cs @@ -26,8 +26,6 @@ class BreakingCollection : BreakingSequence, ICollection public BreakingCollection(params T[] values) : this ((IList) values) {} public BreakingCollection(IList list) => List = list; - public BreakingCollection(int count) : - this(Enumerable.Repeat(default(T), count).ToList()) {} public int Count => List.Count; diff --git a/MoreLinq.Test/BreakingSequence.cs b/MoreLinq.Test/BreakingSequence.cs index 9ed0ccaae..c3c223311 100644 --- a/MoreLinq.Test/BreakingSequence.cs +++ b/MoreLinq.Test/BreakingSequence.cs @@ -27,7 +27,14 @@ namespace MoreLinq.Test /// class BreakingSequence : IEnumerable { - public IEnumerator GetEnumerator() => throw new InvalidOperationException(); + public IEnumerator GetEnumerator() => throw new BreakException(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } + + sealed class BreakException : Exception + { + public BreakException() { } + public BreakException(string message) : base(message) { } + public BreakException(string message, Exception inner) : base(message, inner) { } + } } diff --git a/MoreLinq.Test/ChooseTest.cs b/MoreLinq.Test/ChooseTest.cs index acc4dd112..3f6baf592 100644 --- a/MoreLinq.Test/ChooseTest.cs +++ b/MoreLinq.Test/ChooseTest.cs @@ -67,7 +67,7 @@ static class Option static class Option { - public static readonly (bool IsSome, T Value) None = (false, default); + public static readonly (bool IsSome, T Value) None = default; } [Test] diff --git a/MoreLinq.Test/CompareCountTest.cs b/MoreLinq.Test/CompareCountTest.cs index bc93e08de..906749bf6 100644 --- a/MoreLinq.Test/CompareCountTest.cs +++ b/MoreLinq.Test/CompareCountTest.cs @@ -53,7 +53,7 @@ public void CompareCountWithCollectionAndSequence(int collectionCount, int expectedCompareCount, int expectedMoveNextCallCount) { - var collection = new BreakingCollection(collectionCount); + var collection = new BreakingCollection(new int[collectionCount]); using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); @@ -70,7 +70,7 @@ public void CompareCountWithSequenceAndCollection(int sequenceCount, int expectedCompareCount, int expectedMoveNextCallCount) { - var collection = new BreakingCollection(collectionCount); + var collection = new BreakingCollection(new int[collectionCount]); using var seq = Enumerable.Range(0, sequenceCount).AsTestingSequence(); @@ -107,7 +107,7 @@ public void CompareCountDisposesSequenceEnumerators() [Test] public void CompareCountDisposesFirstEnumerator() { - var collection = new BreakingCollection(0); + var collection = new BreakingCollection(); using var seq = TestingSequence.Of(); @@ -117,7 +117,7 @@ public void CompareCountDisposesFirstEnumerator() [Test] public void CompareCountDisposesSecondEnumerator() { - var collection = new BreakingCollection(0); + var collection = new BreakingCollection(); using var seq = TestingSequence.Of(); diff --git a/MoreLinq.Test/Comparer.cs b/MoreLinq.Test/Comparer.cs index 75983a3d2..7f9f26c29 100644 --- a/MoreLinq.Test/Comparer.cs +++ b/MoreLinq.Test/Comparer.cs @@ -27,19 +27,19 @@ sealed class Comparer /// . /// - public static IComparer Create(Func compare) => + public static IComparer Create(Func compare) => new DelegatingComparer(compare); sealed class DelegatingComparer : IComparer { - readonly Func _comparer; + readonly Func _comparer; - public DelegatingComparer(Func comparer) + public DelegatingComparer(Func comparer) { _comparer = comparer ?? throw new ArgumentNullException(nameof(comparer)); } - public int Compare(T x, T y) => _comparer(x, y); + public int Compare(T? x, T? y) => _comparer(x, y); } } } diff --git a/MoreLinq.Test/CountByTest.cs b/MoreLinq.Test/CountByTest.cs index ea0e7e8a1..f0732a7ba 100644 --- a/MoreLinq.Test/CountByTest.cs +++ b/MoreLinq.Test/CountByTest.cs @@ -98,10 +98,10 @@ public void CountByWithSomeNullKeys() var result = ss.CountBy(s => s); result.AssertSequenceEqual( - KeyValuePair.Create("foo", 2), - KeyValuePair.Create((string) null, 4), - KeyValuePair.Create("bar", 2), - KeyValuePair.Create("baz", 2)); + KeyValuePair.Create((string?)"foo", 2), + KeyValuePair.Create((string?)null, 4), + KeyValuePair.Create((string?)"bar", 2), + KeyValuePair.Create((string?)"baz", 2)); } [Test] @@ -110,10 +110,10 @@ public void CountByWithSomeNullKeysAndEqualityComparer() var result = new[] { "a", "B", null, "c", "A", null, "b", "A" }.CountBy(c => c, StringComparer.OrdinalIgnoreCase); result.AssertSequenceEqual( - KeyValuePair.Create("a", 3), - KeyValuePair.Create("B", 2), - KeyValuePair.Create((string)null, 2), - KeyValuePair.Create("c", 1)); + KeyValuePair.Create((string?)"a", 3), + KeyValuePair.Create((string?)"B", 2), + KeyValuePair.Create((string?)null, 2), + KeyValuePair.Create((string?)"c", 1)); } } } diff --git a/MoreLinq.Test/CountDownTest.cs b/MoreLinq.Test/CountDownTest.cs index 1e42f40ea..a1862116e 100644 --- a/MoreLinq.Test/CountDownTest.cs +++ b/MoreLinq.Test/CountDownTest.cs @@ -133,14 +133,14 @@ static class TestCollection { public static ICollection Create(ICollection collection, - Func, IEnumerator> em = null) + Func, IEnumerator>? em = null) { return new Collection(collection, em); } public static IReadOnlyCollection CreateReadOnly(ICollection collection, - Func, IEnumerator> em = null) + Func, IEnumerator>? em = null) { return new ReadOnlyCollection(collection, em); } @@ -154,7 +154,7 @@ abstract class Sequence : IEnumerable { readonly Func, IEnumerator> _em; - protected Sequence(Func, IEnumerator> em) => + protected Sequence(Func, IEnumerator>? em) => _em = em ?? (e => e); public IEnumerator GetEnumerator() => @@ -175,7 +175,7 @@ sealed class Collection : Sequence, ICollection readonly ICollection _collection; public Collection(ICollection collection, - Func, IEnumerator> em = null) : + Func, IEnumerator>? em = null) : base(em) => _collection = collection ?? throw new ArgumentNullException(nameof(collection)); @@ -202,7 +202,7 @@ sealed class ReadOnlyCollection : Sequence, IReadOnlyCollection readonly ICollection _collection; public ReadOnlyCollection(ICollection collection, - Func, IEnumerator> em = null) : + Func, IEnumerator>? em = null) : base(em) => _collection = collection ?? throw new ArgumentNullException(nameof(collection)); diff --git a/MoreLinq.Test/Enumerable.cs b/MoreLinq.Test/Enumerable.cs index 0d6f188d3..f7795af4e 100644 --- a/MoreLinq.Test/Enumerable.cs +++ b/MoreLinq.Test/Enumerable.cs @@ -126,7 +126,7 @@ public static int Count(this IEnumerable source, Func(this IEnumerable source) => LinqEnumerable.Count(source); - public static IEnumerable DefaultIfEmpty(this IEnumerable source) => + public static IEnumerable DefaultIfEmpty(this IEnumerable source) => LinqEnumerable.DefaultIfEmpty(source); public static IEnumerable DefaultIfEmpty(this IEnumerable source, TSource defaultValue) => @@ -141,7 +141,7 @@ public static IEnumerable Distinct(this IEnumerable s public static TSource ElementAt(this IEnumerable source, int index) => LinqEnumerable.ElementAt(source, index); - public static TSource ElementAtOrDefault(this IEnumerable source, int index) => + public static TSource? ElementAtOrDefault(this IEnumerable source, int index) => LinqEnumerable.ElementAtOrDefault(source, index); public static IEnumerable Empty() => @@ -159,10 +159,10 @@ public static TSource First(this IEnumerable source) => public static TSource First(this IEnumerable source, Func predicate) => LinqEnumerable.First(source, predicate); - public static TSource FirstOrDefault(this IEnumerable source) => + public static TSource? FirstOrDefault(this IEnumerable source) => LinqEnumerable.FirstOrDefault(source); - public static TSource FirstOrDefault(this IEnumerable source, Func predicate) => + public static TSource? FirstOrDefault(this IEnumerable source, Func predicate) => LinqEnumerable.FirstOrDefault(source, predicate); public static IEnumerable GroupBy(this IEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer comparer) => @@ -213,10 +213,10 @@ public static TSource Last(this IEnumerable source) => public static TSource Last(this IEnumerable source, Func predicate) => LinqEnumerable.Last(source, predicate); - public static TSource LastOrDefault(this IEnumerable source) => + public static TSource? LastOrDefault(this IEnumerable source) => LinqEnumerable.LastOrDefault(source); - public static TSource LastOrDefault(this IEnumerable source, Func predicate) => + public static TSource? LastOrDefault(this IEnumerable source, Func predicate) => LinqEnumerable.LastOrDefault(source, predicate); public static long LongCount(this IEnumerable source) => @@ -249,13 +249,13 @@ public static decimal Max(this IEnumerable source, Func(this IEnumerable source, Func selector) => LinqEnumerable.Max(source, selector); - public static TResult Max(this IEnumerable source, Func selector) => + public static TResult? Max(this IEnumerable source, Func selector) => LinqEnumerable.Max(source, selector); public static double? Max(this IEnumerable source, Func selector) => LinqEnumerable.Max(source, selector); - public static TSource Max(this IEnumerable source) => + public static TSource? Max(this IEnumerable source) => LinqEnumerable.Max(source); public static float Max(this IEnumerable source, Func selector) => @@ -303,7 +303,7 @@ public static long Min(this IEnumerable source, Func(this IEnumerable source, Func selector) => LinqEnumerable.Min(source, selector); - public static TResult Min(this IEnumerable source, Func selector) => + public static TResult? Min(this IEnumerable source, Func selector) => LinqEnumerable.Min(source, selector); public static long? Min(this IEnumerable source, Func selector) => @@ -321,7 +321,7 @@ public static decimal Min(this IEnumerable source, Func(this IEnumerable source, Func selector) => LinqEnumerable.Min(source, selector); - public static TSource Min(this IEnumerable source) => + public static TSource? Min(this IEnumerable source) => LinqEnumerable.Min(source); public static double Min(this IEnumerable source, Func selector) => @@ -411,10 +411,10 @@ public static TSource Single(this IEnumerable source) => public static TSource Single(this IEnumerable source, Func predicate) => LinqEnumerable.Single(source, predicate); - public static TSource SingleOrDefault(this IEnumerable source) => + public static TSource? SingleOrDefault(this IEnumerable source) => LinqEnumerable.SingleOrDefault(source); - public static TSource SingleOrDefault(this IEnumerable source, Func predicate) => + public static TSource? SingleOrDefault(this IEnumerable source, Func predicate) => LinqEnumerable.SingleOrDefault(source, predicate); public static IEnumerable Skip(this IEnumerable source, int count) => @@ -510,16 +510,20 @@ public static IOrderedEnumerable ThenByDescending(this I public static TSource[] ToArray(this IEnumerable source) => LinqEnumerable.ToArray(source); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, IEqualityComparer comparer) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, IEqualityComparer comparer) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector, comparer); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector, elementSelector); - public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) => + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + where TKey : notnull => LinqEnumerable.ToDictionary(source, keySelector, elementSelector, comparer); public static List ToList(this IEnumerable source) => diff --git a/MoreLinq.Test/EqualityComparer.cs b/MoreLinq.Test/EqualityComparer.cs index f377a819c..655999130 100644 --- a/MoreLinq.Test/EqualityComparer.cs +++ b/MoreLinq.Test/EqualityComparer.cs @@ -27,24 +27,24 @@ static class EqualityComparer /// . /// - public static IEqualityComparer Create(Func comparer) => + public static IEqualityComparer Create(Func comparer) => new DelegatingComparer(comparer); sealed class DelegatingComparer : IEqualityComparer { - readonly Func _comparer; + readonly Func _comparer; readonly Func _hasher; - public DelegatingComparer(Func comparer) + public DelegatingComparer(Func comparer) : this(comparer, x => x == null ? 0 : x.GetHashCode()) {} - DelegatingComparer(Func comparer, Func hasher) + DelegatingComparer(Func comparer, Func hasher) { _comparer = comparer ?? throw new ArgumentNullException(nameof(comparer)); _hasher = hasher ?? throw new ArgumentNullException(nameof(hasher)); } - public bool Equals(T x, T y) => _comparer(x, y); + public bool Equals(T? x, T? y) => _comparer(x, y); public int GetHashCode(T obj) => _hasher(obj); } } diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index c0d18b0af..477a86384 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; @@ -98,7 +96,7 @@ public void ZipDisposesInnerSequencesCaseGetEnumeratorThrows() using var s1 = TestingSequence.Of(1, 2); Assert.That(() => s1.EquiZip(new BreakingSequence(), Tuple.Create).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } } } diff --git a/MoreLinq.Test/FlattenTest.cs b/MoreLinq.Test/FlattenTest.cs index 2cbbdb940..e1a8a5419 100644 --- a/MoreLinq.Test/FlattenTest.cs +++ b/MoreLinq.Test/FlattenTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System.Collections.Generic; diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index 9a73c6ba9..f5435cfa5 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/IndexByTest.cs b/MoreLinq.Test/IndexByTest.cs index da62a738a..8b71ea268 100644 --- a/MoreLinq.Test/IndexByTest.cs +++ b/MoreLinq.Test/IndexByTest.cs @@ -81,18 +81,17 @@ public void IndexByWithSomeNullKeys() var source = new[] { "foo", null, "bar", "baz", null, null, "baz", "bar", null, "foo" }; var result = source.IndexBy(c => c); - const string @null = null; // type inference happiness result.AssertSequenceEqual( - KeyValuePair.Create(0, "foo"), - KeyValuePair.Create(0, @null), - KeyValuePair.Create(0, "bar"), - KeyValuePair.Create(0, "baz"), - KeyValuePair.Create(1, @null), - KeyValuePair.Create(2, @null), - KeyValuePair.Create(1, "baz"), - KeyValuePair.Create(1, "bar"), - KeyValuePair.Create(3, @null), - KeyValuePair.Create(1, "foo")); + KeyValuePair.Create(0, (string?)"foo"), + KeyValuePair.Create(0, (string?)null), + KeyValuePair.Create(0, (string?)"bar"), + KeyValuePair.Create(0, (string?)"baz"), + KeyValuePair.Create(1, (string?)null), + KeyValuePair.Create(2, (string?)null), + KeyValuePair.Create(1, (string?)"baz"), + KeyValuePair.Create(1, (string?)"bar"), + KeyValuePair.Create(3, (string?)null), + KeyValuePair.Create(1, (string?)"foo")); } [Test] diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index ad9b02c6a..8eecc16a3 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -46,7 +46,7 @@ public void TestInterleaveDisposesOnErrorAtGetEnumerator() // Expected and thrown by BreakingSequence Assert.That(() => sequenceA.Interleave(sequenceB).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } /// diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index fbd0262ff..3a1c0fdfd 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index ccff7fb03..39daf7228 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxByTest.cs index 59c1bc712..0abc1beb6 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxByTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/MemoizeTest.cs b/MoreLinq.Test/MemoizeTest.cs index 2d94bdf42..d600afc68 100644 --- a/MoreLinq.Test/MemoizeTest.cs +++ b/MoreLinq.Test/MemoizeTest.cs @@ -343,13 +343,13 @@ sealed class DisposeTestingSequenceEnumerator : IEnumerator { readonly IEnumerator _sequence; - public event EventHandler Disposed; + public event EventHandler? Disposed; public DisposeTestingSequenceEnumerator(IEnumerator sequence) => _sequence = sequence; public T Current => _sequence.Current; - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Reset() => _sequence.Reset(); public bool MoveNext() => _sequence.MoveNext(); diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinByTest.cs index d8745c1a6..49c8565a7 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinByTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index c27c04025..ac5c2885c 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -56,6 +56,7 @@ + diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index 8e8d0f36d..dc65ad5ad 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -20,12 +20,12 @@ namespace MoreLinq.Test using System; using System.Collections; using System.Collections.Generic; - using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using NUnit.Framework; using NUnit.Framework.Interfaces; + using StackTrace = System.Diagnostics.StackTrace; [TestFixture] public class NullArgumentTest @@ -41,7 +41,7 @@ public void CanBeNull(Action testCase) => static IEnumerable GetNotNullTestCases() => GetTestCases(canBeNull: false, testCaseFactory: (method, args, paramName) => () => { - Exception e = null; + Exception? e = null; try { @@ -53,24 +53,29 @@ static IEnumerable GetNotNullTestCases() => } Assert.That(e, Is.Not.Null, $"No exception was thrown when {nameof(ArgumentNullException)} was expected."); - Assert.That(e, Is.InstanceOf()); - var ane = (ArgumentNullException) e; - Assert.That(ane.ParamName, Is.EqualTo(paramName)); - var stackTrace = new StackTrace(ane, false); + Assert.That(e, Is.InstanceOf().With.Property(nameof(ArgumentNullException.ParamName)).EqualTo(paramName)); + Debug.Assert(e is not null); + var stackTrace = new StackTrace(e, false); var stackFrame = stackTrace.GetFrames().First(); - var actualType = stackFrame.GetMethod().DeclaringType; +#if NETCOREAPP3_1 + // Under .NET Core 3.1, "StackTrace.GetFrames()" was defined to return an array + // of nullable frame elements. See: + // https://github.com/dotnet/corefx/blob/v3.1.32/src/Common/src/CoreLib/System/Diagnostics/StackTrace.cs#L162 + Debug.Assert(stackFrame is not null); +#endif + var actualType = stackFrame.GetMethod()?.DeclaringType; Assert.That(actualType, Is.SameAs(typeof(MoreEnumerable))); }); static IEnumerable GetCanBeNullTestCases() => GetTestCases(canBeNull: true, testCaseFactory: (method, args, _) => () => method.Invoke(null, args)); - static IEnumerable GetTestCases(bool canBeNull, Func testCaseFactory) => + static IEnumerable GetTestCases(bool canBeNull, Func testCaseFactory) => from m in typeof (MoreEnumerable).GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) from t in CreateTestCases(m, canBeNull, testCaseFactory) select t; - static IEnumerable CreateTestCases(MethodInfo methodDefinition, bool canBeNull, Func testCaseFactory) + static IEnumerable CreateTestCases(MethodInfo methodDefinition, bool canBeNull, Func testCaseFactory) { var method = InstantiateMethod(methodDefinition); var parameters = method.GetParameters().ToList(); @@ -78,7 +83,7 @@ static IEnumerable CreateTestCases(MethodInfo methodDefinition, b return from param in parameters where IsReferenceType(param) && CanBeNull(param) == canBeNull let arguments = parameters.Select(p => p == param ? null : CreateInstance(p.ParameterType)).ToArray() - let testCase = testCaseFactory(method, arguments, param.Name) + let testCase = testCaseFactory(method, arguments, param.Name ?? throw new NullReferenceException()) let testName = GetTestName(methodDefinition, param) select (ITestCaseData) new TestCaseData(testCase).SetName(testName); } @@ -139,15 +144,25 @@ static object CreateInstance(Type type) if (type == typeof (string)) return ""; if (type == typeof(TaskScheduler)) return TaskScheduler.Default; if (type == typeof(IEnumerable)) return new[] { 1, 2, 3 }; // Provide non-empty sequence for MinBy/MaxBy. - if (type.IsArray) return Array.CreateInstance(type.GetElementType(), 0); - if (type.GetTypeInfo().IsValueType || HasDefaultConstructor(type)) return Activator.CreateInstance(type); if (typeof(Delegate).IsAssignableFrom(type)) return CreateDelegateInstance(type); - var typeInfo = type.GetTypeInfo(); + if (type.IsArray) + { + var elementType = type.GetElementType(); + Debug.Assert(elementType is not null); + return Array.CreateInstance(elementType, 0); + } + + if (type.GetTypeInfo().IsValueType || HasDefaultConstructor(type)) + { + var instance = Activator.CreateInstance(type); + Debug.Assert(instance is not null); + return instance; + } - return typeInfo.IsGenericType - ? CreateGenericInterfaceInstance(typeInfo) - : EmptyEnumerable.Instance; + return type.GetTypeInfo() is { IsGenericType: true } typeInfo + ? CreateGenericInterfaceInstance(typeInfo) + : EmptyEnumerable.Instance; } static bool HasDefaultConstructor(Type type) => @@ -156,6 +171,7 @@ static bool HasDefaultConstructor(Type type) => static Delegate CreateDelegateInstance(Type type) { var invoke = type.GetMethod("Invoke"); + Debug.Assert(invoke is not null); var parameters = invoke.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)); var body = Expression.Default(invoke.ReturnType); // requires >= .NET 4.0 var lambda = Expression.Lambda(type, body, parameters); @@ -167,8 +183,10 @@ static object CreateGenericInterfaceInstance(TypeInfo type) Debug.Assert(type.IsGenericType && type.IsInterface); var name = type.Name.Substring(1); // Delete first character, i.e. the 'I' in IEnumerable var definition = typeof (GenericArgs).GetTypeInfo().GetNestedType(name); - var instantiation = definition.MakeGenericType(type.GetGenericArguments()); - return Activator.CreateInstance(instantiation); + Debug.Assert(definition is not null); + var instance = Activator.CreateInstance(definition.MakeGenericType(type.GetGenericArguments())); + Debug.Assert(instance is not null); + return instance; } static class EmptyEnumerable @@ -191,45 +209,45 @@ public void Reset() { } // ReSharper disable UnusedMember.Local, UnusedAutoPropertyAccessor.Local static class GenericArgs { - class Enumerator : IEnumerator + class Enumerator : IEnumerator { public bool MoveNext() => false; - public T Current { get; private set; } - object IEnumerator.Current => Current; + public T? Current { get; private set; } + object? IEnumerator.Current => Current; public void Reset() { } public void Dispose() { } } - public class Enumerable : IEnumerable + public class Enumerable : IEnumerable { - public IEnumerator GetEnumerator() => new Enumerator(); + public IEnumerator GetEnumerator() => new Enumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - public class OrderedEnumerable : Enumerable, System.Linq.IOrderedEnumerable + public class OrderedEnumerable : Enumerable, System.Linq.IOrderedEnumerable { - public System.Linq.IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending) + public System.Linq.IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer? comparer, bool descending) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); return this; } } - public class AwaitQuery : Enumerable, - Experimental.IAwaitQuery + public class AwaitQuery : Enumerable, + Experimental.IAwaitQuery { public Experimental.AwaitQueryOptions Options => Experimental.AwaitQueryOptions.Default; - public Experimental.IAwaitQuery WithOptions(Experimental.AwaitQueryOptions options) => this; + public Experimental.IAwaitQuery WithOptions(Experimental.AwaitQueryOptions options) => this; } public class Comparer : IComparer { - public int Compare(T x, T y) => -1; + public int Compare(T? x, T? y) => -1; } public class EqualityComparer : IEqualityComparer { - public bool Equals(T x, T y) => false; + public bool Equals(T? x, T? y) => false; public int GetHashCode(T obj) => 0; } } diff --git a/MoreLinq.Test/OrderByTest.cs b/MoreLinq.Test/OrderByTest.cs index 7cc944c25..cb6f8bf1d 100644 --- a/MoreLinq.Test/OrderByTest.cs +++ b/MoreLinq.Test/OrderByTest.cs @@ -17,6 +17,7 @@ namespace MoreLinq.Test { + using System.Collections.Generic; using NUnit.Framework; /// @@ -45,6 +46,16 @@ public void TestOrderBySelectorPreserved() Assert.That(resultDes1, Is.EqualTo(resultDes2)); } + static readonly IComparer NumericStringComparer = + Comparer.Create((string? a, string? b) => + (a, b) switch + { + (null, null) => 0, + (null, _) => -1, + (_, null) => 1, + var (sa, sb) => int.Parse(sa).CompareTo(int.Parse(sb)) + }); + /// /// Verify that OrderBy preserves the comparer /// @@ -55,7 +66,7 @@ public void TestOrderByComparerPreserved() var sequenceAscending = sequence.Select(x => x.ToString()); var sequenceDescending = sequenceAscending.Reverse(); - var comparer = Comparer.Create((a, b) => int.Parse(a).CompareTo(int.Parse(b))); + var comparer = NumericStringComparer; var resultAsc1 = sequenceAscending.OrderBy(x => x, comparer, OrderByDirection.Descending); var resultAsc2 = sequenceAscending.OrderByDescending(x => x, comparer); @@ -119,7 +130,7 @@ public void TestThenByComparerPreserved() new {A = "2", B = "1"}, }; - var comparer = Comparer.Create((a, b) => int.Parse(a).CompareTo(int.Parse(b))); + var comparer = NumericStringComparer; var resultA1 = sequence.OrderBy(x => x.A, comparer, OrderByDirection.Ascending) .ThenBy(y => y.B, comparer, OrderByDirection.Ascending); diff --git a/MoreLinq.Test/PadStartTest.cs b/MoreLinq.Test/PadStartTest.cs index 2282a28ba..a65e3bd2b 100644 --- a/MoreLinq.Test/PadStartTest.cs +++ b/MoreLinq.Test/PadStartTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/PadTest.cs b/MoreLinq.Test/PadTest.cs index f9677a22d..4537bdca0 100644 --- a/MoreLinq.Test/PadTest.cs +++ b/MoreLinq.Test/PadTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; diff --git a/MoreLinq.Test/PartialSortByTest.cs b/MoreLinq.Test/PartialSortByTest.cs index 7330a151c..9c83dcd7c 100644 --- a/MoreLinq.Test/PartialSortByTest.cs +++ b/MoreLinq.Test/PartialSortByTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PartialSortTest.cs b/MoreLinq.Test/PartialSortTest.cs index 53ad4d729..04257a737 100644 --- a/MoreLinq.Test/PartialSortTest.cs +++ b/MoreLinq.Test/PartialSortTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PartitionTest.cs b/MoreLinq.Test/PartitionTest.cs index 2883885c1..cdc4ac19c 100644 --- a/MoreLinq.Test/PartitionTest.cs +++ b/MoreLinq.Test/PartitionTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index 67e5c4446..11d7d3092 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -90,7 +90,7 @@ public void TestCardinalityThreePermutation() // should contain six permutations (as defined above) Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); - Assert.That(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y)))), Is.True); + Assert.That(permutations.All(p => expectedPermutations.Contains(p, SequenceEqualityComparer.Instance)), Is.True); } /// @@ -133,7 +133,7 @@ public void TestCardinalityFourPermutation() // should contain six permutations (as defined above) Assert.That(permutations.Count(), Is.EqualTo(expectedPermutations.Length)); - Assert.That(permutations.All(p => expectedPermutations.Contains(p, EqualityComparer.Create>((x, y) => x.SequenceEqual(y)))), Is.True); + Assert.That(permutations.All(p => expectedPermutations.Contains(p, SequenceEqualityComparer.Instance)), Is.True); } /// @@ -196,5 +196,11 @@ public void TestPermutationsAreIndependent() } } } + + static class SequenceEqualityComparer + { + public static readonly IEqualityComparer> Instance = + EqualityComparer.Create>((x, y) => x is { } sx && y is { } sy && sx.SequenceEqual(sy)); + } } } diff --git a/MoreLinq.Test/PrependTest.cs b/MoreLinq.Test/PrependTest.cs index 381e1deba..a13e3fb77 100644 --- a/MoreLinq.Test/PrependTest.cs +++ b/MoreLinq.Test/PrependTest.cs @@ -46,7 +46,7 @@ public void PrependWithEmptyTailSequence() public void PrependWithNullHead() { string[] tail = { "second", "third" }; - string head = null; + string? head = null; var whole = tail.Prepend(head); whole.AssertSequenceEqual(null, "second", "third"); } diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index 5fd0a78db..1187e408e 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; diff --git a/MoreLinq.Test/ReturnTest.cs b/MoreLinq.Test/ReturnTest.cs index 0cf3f3ee9..29d252632 100644 --- a/MoreLinq.Test/ReturnTest.cs +++ b/MoreLinq.Test/ReturnTest.cs @@ -34,8 +34,8 @@ static class SomeSingleton static class NullSingleton { - public static readonly IEnumerable Sequence = MoreEnumerable.Return(null); - public static IList List => (IList)Sequence; + public static readonly IEnumerable Sequence = MoreEnumerable.Return(null); + public static IList List => (IList)Sequence; } [Test] @@ -77,13 +77,13 @@ public void TestContainsDoesNotThrowWhenTheItemProvidedIsNull() [Test] public void TestIndexOfDoesNotThrowWhenTheItemProvidedIsNull() { - Assert.That(() => NullSingleton.List.IndexOf(new object()), Throws.Nothing); + Assert.That(() => SomeSingleton.List.IndexOf(new object()), Throws.Nothing); } [Test] public void TestIndexOfDoesNotThrowWhenTheItemContainedIsNull() { - Assert.That(() => SomeSingleton.List.IndexOf(null), Throws.Nothing); + Assert.That(() => NullSingleton.List.IndexOf(null), Throws.Nothing); } [Test] diff --git a/MoreLinq.Test/ScanByTest.cs b/MoreLinq.Test/ScanByTest.cs index 343b7bd9d..40414fd3c 100644 --- a/MoreLinq.Test/ScanByTest.cs +++ b/MoreLinq.Test/ScanByTest.cs @@ -50,7 +50,7 @@ public void ScanBy() var result = source.ScanBy( item => item.First(), - key => (Element: default(string), Key: key, State: key - 1), + key => (Element: string.Empty, Key: key, State: key - 1), (state, key, item) => (item, char.ToUpperInvariant(key), state.State + 1)); result.AssertSequenceEqual( @@ -103,31 +103,31 @@ public void ScanByWithSomeNullKeys() var result = source.ScanBy(c => c, _ => -1, (i, _, _) => i + 1); result.AssertSequenceEqual( - KeyValuePair.Create("foo" , 0), - KeyValuePair.Create((string)null, 0), - KeyValuePair.Create("bar" , 0), - KeyValuePair.Create("baz" , 0), - KeyValuePair.Create((string)null, 1), - KeyValuePair.Create((string)null, 2), - KeyValuePair.Create("baz" , 1), - KeyValuePair.Create("bar" , 1), - KeyValuePair.Create((string)null, 3), - KeyValuePair.Create("foo" , 1)); + KeyValuePair.Create((string?)"foo", 0), + KeyValuePair.Create((string?)null , 0), + KeyValuePair.Create((string?)"bar", 0), + KeyValuePair.Create((string?)"baz", 0), + KeyValuePair.Create((string?)null , 1), + KeyValuePair.Create((string?)null , 2), + KeyValuePair.Create((string?)"baz", 1), + KeyValuePair.Create((string?)"bar", 1), + KeyValuePair.Create((string?)null , 3), + KeyValuePair.Create((string?)"foo", 1)); } [Test] public void ScanByWithNullSeed() { - var nil = (object)null; + var nil = (object?)null; var source = new[] { "foo", null, "bar", null, "baz" }; var result = source.ScanBy(c => c, _ => nil, (_, _, _) => nil); result.AssertSequenceEqual( - KeyValuePair.Create("foo" , nil), - KeyValuePair.Create((string)null, nil), - KeyValuePair.Create("bar" , nil), - KeyValuePair.Create((string)null, nil), - KeyValuePair.Create("baz" , nil)); + KeyValuePair.Create((string?)"foo", nil), + KeyValuePair.Create((string?)null , nil), + KeyValuePair.Create((string?)"bar", nil), + KeyValuePair.Create((string?)null , nil), + KeyValuePair.Create((string?)"baz", nil)); } [Test] diff --git a/MoreLinq.Test/ScanTest.cs b/MoreLinq.Test/ScanTest.cs index 15ec08a83..65e6eed10 100644 --- a/MoreLinq.Test/ScanTest.cs +++ b/MoreLinq.Test/ScanTest.cs @@ -47,7 +47,7 @@ public void ScanDoesNotIterateExtra() { var sequence = Enumerable.Range(1, 3).Concat(new BreakingSequence()).Scan(SampleData.Plus); var gold = new[] {1, 3, 6}; - Assert.That(sequence.Consume, Throws.InvalidOperationException); + Assert.That(sequence.Consume, Throws.BreakException); sequence.Take(3).AssertSequenceEqual(gold); } @@ -68,7 +68,7 @@ public void SeededScanSum() [Test] public void SeededScanIsLazy() { - new BreakingSequence().Scan(null, BreakingFunc.Of()); + new BreakingSequence().Scan(null, BreakingFunc.Of()); } [Test] @@ -76,7 +76,7 @@ public void SeededScanDoesNotIterateExtra() { var sequence = Enumerable.Range(1, 3).Concat(new BreakingSequence()).Scan(0, SampleData.Plus); var gold = new[] { 0, 1, 3, 6 }; - Assert.That(sequence.Consume, Throws.InvalidOperationException); + Assert.That(sequence.Consume, Throws.BreakException); sequence.Take(4).AssertSequenceEqual(gold); } diff --git a/MoreLinq.Test/SequenceReader.cs b/MoreLinq.Test/SequenceReader.cs index 1ff809035..5a2a0cba6 100644 --- a/MoreLinq.Test/SequenceReader.cs +++ b/MoreLinq.Test/SequenceReader.cs @@ -35,9 +35,9 @@ public static SequenceReader Read(this IEnumerable source) /// "read" operation. /// /// Type of elements to read. - class SequenceReader : IDisposable + sealed class SequenceReader : IDisposable { - IEnumerator _enumerator; + IEnumerator? _enumerator; /// /// Initializes a instance @@ -63,84 +63,46 @@ static IEnumerator GetEnumerator(IEnumerable source) return source.GetEnumerator(); } + IEnumerator Enumerator => + _enumerator ?? throw new ObjectDisposedException(GetType().FullName); + /// - /// Tires to read the next value. + /// Reads a value otherwise throws + /// if no more values are available. /// - /// - /// When this method returns, contains the value read on success. - /// /// - /// Returns true if a value was successfully read; otherwise, false. + /// Returns the read value; /// - public virtual bool TryRead(out T value) + public T Read() { - EnsureNotDisposed(); + var e = Enumerator; - value = default; - - var e = _enumerator; if (!e.MoveNext()) - return false; + throw new InvalidOperationException(); - value = e.Current; - return true; + return e.Current; } - /// - /// Tires to read the next value otherwise return the default. - /// - - public T TryRead() => TryRead(default); - - /// - /// Tires to read the next value otherwise return a given default. - /// - - public T TryRead(T defaultValue) => - TryRead(out var result) ? result : defaultValue; - - /// - /// Reads a value otherwise throws - /// if no more values are available. - /// - /// - /// Returns the read value; - /// - - public T Read() => - TryRead(out var result) ? result : throw new InvalidOperationException(); - /// /// Reads the end. If the end has not been reached then it /// throws . /// - public virtual void ReadEnd() + public void ReadEnd() { - EnsureNotDisposed(); + var enumerator = Enumerator; - if (_enumerator.MoveNext()) + if (enumerator.MoveNext()) throw new InvalidOperationException(); } - /// - /// Ensures that this object has not been disposed, that - /// has not been previously called. - /// - - protected void EnsureNotDisposed() - { - if (_enumerator == null) - throw new ObjectDisposedException(GetType().FullName); - } - /// /// Disposes this object and enumerator with which is was /// initialized. /// - public virtual void Dispose() + public void Dispose() { var e = _enumerator; if (e == null) return; diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index 3ef82578d..4428e4c5b 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -51,7 +51,7 @@ public void TestSortedMergeDisposesOnError() // Expected and thrown by BreakingSequence Assert.That(() => sequenceA.SortedMerge(OrderByDirection.Ascending, new BreakingSequence()) .Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } /// @@ -62,7 +62,7 @@ public void TestSortedMergeComparerNull() { var sequenceA = Enumerable.Range(1, 3); var sequenceB = Enumerable.Range(4, 3); - var result = sequenceA.SortedMerge(OrderByDirection.Ascending, (IComparer)null, sequenceB); + var result = sequenceA.SortedMerge(OrderByDirection.Ascending, (IComparer?)null, sequenceB); Assert.That(result, Is.EqualTo(sequenceA.Concat(sequenceB))); } diff --git a/MoreLinq.Test/SubjectTest.cs b/MoreLinq.Test/SubjectTest.cs index 334c82730..fe44b2485 100644 --- a/MoreLinq.Test/SubjectTest.cs +++ b/MoreLinq.Test/SubjectTest.cs @@ -25,9 +25,9 @@ namespace MoreLinq.Test public class SubjectTest { static IDisposable Subscribe(IObservable subject, - Action onNext = null, - Action onError = null, - Action onCompleted = null) => + Action? onNext = null, + Action? onError = null, + Action? onCompleted = null) => subject.Subscribe(onNext ?? BreakingAction.Of(), onError ?? BreakingAction.Of(), onCompleted ?? BreakingAction.WithoutArguments); @@ -36,7 +36,7 @@ static IDisposable Subscribe(IObservable subject, public void SubscribeWithNullObserverThrows() { var subject = new Subject(); - Assert.That(() => subject.Subscribe(null), + Assert.That(() => subject.Subscribe(null!), Throws.ArgumentNullException("observer")); } @@ -60,8 +60,8 @@ public void OnNextObservations() [Test] public void OnErrorObservations() { - Exception error1 = null; - Exception error2 = null; + Exception? error1 = null; + Exception? error2 = null; var subject = new Subject(); @@ -139,7 +139,7 @@ public void SubscriptionPostCompletion() [Test] public void SubscriptionPostError() { - Exception observedError = null; + Exception? observedError = null; var subject = new Subject(); var error = new TestException(); subject.OnError(error); @@ -215,8 +215,12 @@ public void ErrorsOnce() public void SafeToDisposeDuringOnNext() { var subject = new Subject(); - IDisposable subscription = null; - var action = new Action(() => subscription.Dispose()); + IDisposable? subscription = null; + var action = new Action(() => + { + Debug.Assert(subscription is not null); + subscription.Dispose(); + }); subscription = subject.Subscribe(_ => action()); subject.OnNext(42); action = BreakingAction.WithoutArguments; diff --git a/MoreLinq.Test/TestingSequence.cs b/MoreLinq.Test/TestingSequence.cs index aa8bf84ee..0a90139d2 100644 --- a/MoreLinq.Test/TestingSequence.cs +++ b/MoreLinq.Test/TestingSequence.cs @@ -41,7 +41,7 @@ internal static TestingSequence AsTestingSequence(this IEnumerable sour sealed class TestingSequence : IEnumerable, IDisposable { bool? _disposed; - IEnumerable _sequence; + IEnumerable? _sequence; internal TestingSequence(IEnumerable sequence) => _sequence = sequence; @@ -65,6 +65,8 @@ void AssertDisposed() public IEnumerator GetEnumerator() { Assert.That(_sequence, Is.Not.Null, "LINQ operators should not enumerate a sequence more than once."); + Debug.Assert(_sequence is not null); + var enumerator = _sequence.GetEnumerator().AsWatchable(); _disposed = false; enumerator.Disposed += delegate diff --git a/MoreLinq.Test/Throws.cs b/MoreLinq.Test/Throws.cs index ef4326408..672234e3c 100644 --- a/MoreLinq.Test/Throws.cs +++ b/MoreLinq.Test/Throws.cs @@ -24,7 +24,8 @@ static class Throws { public static ThrowsNothingConstraint Nothing => NUnit.Framework.Throws.Nothing; public static ExactTypeConstraint InvalidOperationException => NUnit.Framework.Throws.InvalidOperationException; - public static ExactTypeConstraint ObjectDisposedException => NUnit.Framework.Throws.TypeOf(); + public static ExactTypeConstraint ObjectDisposedException => TypeOf(); + public static ExactTypeConstraint BreakException => TypeOf(); public static InstanceOfTypeConstraint InstanceOf() where T : Exception => @@ -41,7 +42,7 @@ public static EqualConstraint ArgumentNullException(string expectedParamName) => NUnit.Framework.Throws.ArgumentNullException.With.ParamName().EqualTo(expectedParamName); public static ExactTypeConstraint ArgumentOutOfRangeException() => - NUnit.Framework.Throws.TypeOf(); + TypeOf(); public static EqualConstraint ArgumentOutOfRangeException(string expectedParamName) => ArgumentOutOfRangeException().With.ParamName().EqualTo(expectedParamName); diff --git a/MoreLinq.Test/ToDelimitedStringTest.cs b/MoreLinq.Test/ToDelimitedStringTest.cs index 8d7eb1f0a..61d21e659 100644 --- a/MoreLinq.Test/ToDelimitedStringTest.cs +++ b/MoreLinq.Test/ToDelimitedStringTest.cs @@ -32,7 +32,7 @@ public void ToDelimitedStringWithNonEmptySequenceAndDelimiter() [Test] public void ToDelimitedStringWithNonEmptySequenceContainingNulls() { - var result = new object[] { 1, null, "foo", true }.ToDelimitedString(","); + var result = new object?[] { 1, null, "foo", true }.ToDelimitedString(","); Assert.That(result, Is.EqualTo("1,,foo,True")); } @@ -40,7 +40,7 @@ public void ToDelimitedStringWithNonEmptySequenceContainingNulls() public void ToDelimitedStringWithNonEmptySequenceContainingNullsAtStart() { // See: https://github.com/morelinq/MoreLINQ/issues/43 - var result = new object[] { null, null, "foo" }.ToDelimitedString(","); + var result = new object?[] { null, null, "foo" }.ToDelimitedString(","); Assert.That(result, Is.EqualTo(",,foo")); } } diff --git a/MoreLinq.Test/TraceTest.cs b/MoreLinq.Test/TraceTest.cs index 36a7ca0ed..65dc4f2b3 100644 --- a/MoreLinq.Test/TraceTest.cs +++ b/MoreLinq.Test/TraceTest.cs @@ -84,7 +84,6 @@ static IEnumerable Lines(string str) IEnumerator _(TextReader reader) { - Debug.Assert(reader != null); while (reader.ReadLine() is { } line) yield return line; } diff --git a/MoreLinq.Test/TransposeTest.cs b/MoreLinq.Test/TransposeTest.cs index f400a0c7c..70f3c6772 100644 --- a/MoreLinq.Test/TransposeTest.cs +++ b/MoreLinq.Test/TransposeTest.cs @@ -36,7 +36,7 @@ public void TransposeWithOneNullRow() using var seq1 = TestingSequence.Of(10, 11); using var seq2 = TestingSequence.Of(); using var seq3 = TestingSequence.Of(30, 31, 32); - using var matrix = TestingSequence.Of(seq1, seq2, seq3, null); + using var matrix = TestingSequence.Of>(seq1, seq2, seq3, null!); Assert.That(() => matrix.Transpose().FirstOrDefault(), Throws.TypeOf()); diff --git a/MoreLinq.Test/WatchableEnumerator.cs b/MoreLinq.Test/WatchableEnumerator.cs index ca22e87be..2756df448 100644 --- a/MoreLinq.Test/WatchableEnumerator.cs +++ b/MoreLinq.Test/WatchableEnumerator.cs @@ -31,14 +31,14 @@ sealed class WatchableEnumerator : IEnumerator { readonly IEnumerator _source; - public event EventHandler Disposed; - public event EventHandler MoveNextCalled; + public event EventHandler? Disposed; + public event EventHandler? MoveNextCalled; public WatchableEnumerator(IEnumerator source) => _source = source ?? throw new ArgumentNullException(nameof(source)); public T Current => _source.Current; - object IEnumerator.Current => Current; + object? IEnumerator.Current => Current; public void Reset() => _source.Reset(); public bool MoveNext() diff --git a/MoreLinq.Test/ZipLongestTest.cs b/MoreLinq.Test/ZipLongestTest.cs index 05fbd663c..24bd03a59 100644 --- a/MoreLinq.Test/ZipLongestTest.cs +++ b/MoreLinq.Test/ZipLongestTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using System; @@ -81,7 +79,7 @@ public void ZipLongestDisposesInnerSequencesCaseGetEnumeratorThrows() using var s1 = TestingSequence.Of(1, 2); Assert.That(() => s1.ZipLongest(new BreakingSequence(), Tuple.Create).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } } } diff --git a/MoreLinq.Test/ZipShortestTest.cs b/MoreLinq.Test/ZipShortestTest.cs index 9c6c59f04..3a63b3660 100644 --- a/MoreLinq.Test/ZipShortestTest.cs +++ b/MoreLinq.Test/ZipShortestTest.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Test { using NUnit.Framework; @@ -111,7 +109,7 @@ public void ZipShortestDisposesInnerSequencesCaseGetEnumeratorThrows() using var s1 = TestingSequence.Of(1, 2); Assert.That(() => s1.ZipShortest(new BreakingSequence(), Tuple.Create).Consume(), - Throws.InvalidOperationException); + Throws.BreakException); } } } diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index f428cf3e3..dccb0afc0 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -120,7 +120,6 @@ 3.3.2 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0;netstandard2.1;net6.0 - enable false diff --git a/MoreLinq/Reactive/Subject.cs b/MoreLinq/Reactive/Subject.cs index f1fc91a6a..bef962abb 100644 --- a/MoreLinq/Reactive/Subject.cs +++ b/MoreLinq/Reactive/Subject.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -#nullable enable - namespace MoreLinq.Reactive { using System; diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index ee274ddfb..e1c9c8e49 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -3,7 +3,6 @@ Exe net7.0 false - enable From 52f705d2390a26aac4919099621a86e6a6110176 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 12 Jan 2023 21:46:34 +0100 Subject: [PATCH 148/157] Seal (private) enumerators --- MoreLinq/Permutations.cs | 2 +- MoreLinq/Subsets.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Permutations.cs b/MoreLinq/Permutations.cs index 0fac1001b..b700e46fc 100644 --- a/MoreLinq/Permutations.cs +++ b/MoreLinq/Permutations.cs @@ -29,7 +29,7 @@ public static partial class MoreEnumerable /// The private implementation class that produces permutations of a sequence. /// - class PermutationEnumerator : IEnumerator> + sealed class PermutationEnumerator : IEnumerator> { // NOTE: The algorithm used to generate permutations uses the fact that any set // can be put into 1-to-1 correspondence with the set of ordinals number (0..n). diff --git a/MoreLinq/Subsets.cs b/MoreLinq/Subsets.cs index 357a60f73..3ddf9f49a 100644 --- a/MoreLinq/Subsets.cs +++ b/MoreLinq/Subsets.cs @@ -123,7 +123,7 @@ sealed class SubsetGenerator : IEnumerable> /// predetermined size less than or equal to the original set size. /// - class SubsetEnumerator : IEnumerator> + sealed class SubsetEnumerator : IEnumerator> { readonly IList _set; // the original set of elements readonly T[] _subset; // the current subset to return From a53d8973e6c54ac81d251373dc71b0616e258a6f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 12 Jan 2023 21:50:19 +0100 Subject: [PATCH 149/157] Replace "!" with debug assertion in "Permutations" This adds to commit b31c7fdd6b02a77f37e57c0684e6bdcb8b51e5ba, "Replace null-forgiving operator uses with debug assertions (#890)". --- MoreLinq/Permutations.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MoreLinq/Permutations.cs b/MoreLinq/Permutations.cs index b700e46fc..fd9698468 100644 --- a/MoreLinq/Permutations.cs +++ b/MoreLinq/Permutations.cs @@ -103,7 +103,14 @@ public void Reset() _hasMoreResults = true; // there's always at least one permutation: the original set itself } - public IList Current => _current!; + public IList Current + { + get + { + Debug.Assert(_current is not null); + return _current; + } + } object IEnumerator.Current => Current; From 1fc1e312e8478462cad0e90bde7dc572b344129f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 13:50:25 +0100 Subject: [PATCH 150/157] Fix typos in comments --- MoreLinq.Test/LeadTest.cs | 2 +- MoreLinq.Test/PartialSortByTest.cs | 2 +- MoreLinq.Test/PartialSortTest.cs | 2 +- MoreLinq.Test/PermutationsTest.cs | 2 +- MoreLinq.Test/RankTest.cs | 2 +- MoreLinq/CountDown.cs | 2 +- MoreLinq/CountMethods.cs | 4 ++-- MoreLinq/Experimental/Await.cs | 8 ++++---- MoreLinq/Experimental/Memoize.cs | 2 +- MoreLinq/Extensions.g.cs | 18 +++++++++--------- MoreLinq/FillBackward.cs | 2 +- MoreLinq/FillForward.cs | 2 +- MoreLinq/FullJoin.cs | 2 +- MoreLinq/LeftJoin.cs | 2 +- MoreLinq/Permutations.cs | 8 ++++---- MoreLinq/Random.cs | 2 +- MoreLinq/RandomSubset.cs | 4 ++-- MoreLinq/RightJoin.cs | 2 +- MoreLinq/ScanRight.cs | 4 ++-- MoreLinq/Subsets.cs | 8 ++++---- MoreLinq/Window.cs | 8 ++++---- 21 files changed, 44 insertions(+), 44 deletions(-) diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index 39daf7228..8b2e245b8 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -72,7 +72,7 @@ public void TestLeadExplicitDefaultValue() } /// - /// Verify that Lead() willuse default(T) if a specific default value is not supplied for the lead value. + /// Verify that Lead() will use default(T) if a specific default value is not supplied for the lead value. /// [Test] public void TestLeadImplicitDefaultValue() diff --git a/MoreLinq.Test/PartialSortByTest.cs b/MoreLinq.Test/PartialSortByTest.cs index 9c83dcd7c..e192e60fe 100644 --- a/MoreLinq.Test/PartialSortByTest.cs +++ b/MoreLinq.Test/PartialSortByTest.cs @@ -87,7 +87,7 @@ public void PartialSortByIsStable() var sorted = foobars.PartialSort(5); - // Pair expected and actuals by index and then check + // Pair expected and actual by index and then check // reference equality, finding the first mismatch. var mismatchIndex = diff --git a/MoreLinq.Test/PartialSortTest.cs b/MoreLinq.Test/PartialSortTest.cs index 04257a737..3c644e6ae 100644 --- a/MoreLinq.Test/PartialSortTest.cs +++ b/MoreLinq.Test/PartialSortTest.cs @@ -92,7 +92,7 @@ public void PartialSortByIsStable() var sorted = foobars.PartialSortBy(5, s => s.Length); - // Pair expected and actuals by index and then check + // Pair expected and actual by index and then check // reference equality, finding the first mismatch. var mismatchIndex = diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index 11d7d3092..231e4c601 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -144,7 +144,7 @@ public void TestCardinalityFourPermutation() public void TestHigherCardinalityPermutations() { // NOTE: Testing higher cardinality permutations by exhaustive comparison becomes tedious - // above cardiality 4 sets, as the number of permutations is N! (factorial). To provide + // above cardinality 4 sets, as the number of permutations is N! (factorial). To provide // some level of verification, though, we will simply test the count of items in the // permuted sets, and verify they are equal to the expected number (count!). diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index 1187e408e..6965c1226 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -171,7 +171,7 @@ public void TestRankCustomComparer() const int count = 10; var ordinals = Enumerable.Range(1, count); var sequence = ordinals.Select( x => new DateTime(2010,x,20-x) ); - // invert the CompareTo operation to Rank in reverse order (ascening to descending) + // invert the CompareTo operation to Rank in reverse order (ascending to descending) var resultA = sequence.AsTestingSequence().Rank(Comparer.Create((a, b) => -a.CompareTo(b))); var resultB = sequence.AsTestingSequence().RankBy(x => x.Day, Comparer.Create((a, b) => -a.CompareTo(b))); diff --git a/MoreLinq/CountDown.cs b/MoreLinq/CountDown.cs index f95985898..a46186a89 100644 --- a/MoreLinq/CountDown.cs +++ b/MoreLinq/CountDown.cs @@ -39,7 +39,7 @@ static partial class MoreEnumerable /// A function that receives the element and the current countdown /// value for the element and which returns those mapped to a /// result returned in the resulting sequence. For elements before - /// the last , the coundown value is + /// the last , the countdown value is /// null. /// /// A sequence of results returned by diff --git a/MoreLinq/CountMethods.cs b/MoreLinq/CountMethods.cs index fd2cb640e..d253dc7c8 100644 --- a/MoreLinq/CountMethods.cs +++ b/MoreLinq/CountMethods.cs @@ -55,7 +55,7 @@ public static bool AtLeast(this IEnumerable source, int count) /// /// Element type of sequence /// The source sequence - /// The maximun number of items a sequence must have for this + /// The maximum number of items a sequence must have for this /// function to return true /// is null /// is negative @@ -110,7 +110,7 @@ public static bool Exactly(this IEnumerable source, int count) /// The source sequence /// The minimum number of items a sequence must have for this /// function to return true - /// The maximun number of items a sequence must have for this + /// The maximum number of items a sequence must have for this /// function to return true /// is null /// is negative or is less than min diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index 3873754ea..779a5ed29 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -363,14 +363,14 @@ select e.Task.IsFaulted /// completed task. /// /// The type of the source elements. - /// The type of the tasks's result. + /// The type of the task's result. /// The type of the result elements. /// The source sequence. /// A function to begin the asynchronous /// evaluation of each element, the second parameter of which is a /// that can be used to abort /// asynchronous operations. - /// A fucntion that projects the final + /// A function that projects the final /// result given the source item and its asynchronous completion /// result. /// @@ -483,7 +483,7 @@ void PostNotice(Notice notice, // Note that only the "last" critical error is reported // as maintaining a list would incur allocations. The idea // here is to make a best effort attempt to report any of - // the error conditions that may be occuring, which is still + // the error conditions that may be occurring, which is still // better than nothing. try @@ -656,7 +656,7 @@ void OnPendingCompleted() var item = enumerator.Current; var task = starter(item); - // Add a continutation that notifies completion of the task, + // Add a continuation that notifies completion of the task, // along with the necessary housekeeping, in case it // completes before maximum concurrency is reached. diff --git a/MoreLinq/Experimental/Memoize.cs b/MoreLinq/Experimental/Memoize.cs index cc5e2a09a..258037e9c 100644 --- a/MoreLinq/Experimental/Memoize.cs +++ b/MoreLinq/Experimental/Memoize.cs @@ -44,7 +44,7 @@ static partial class ExperimentalEnumerable /// The returned will cache items from /// in a thread-safe manner. Each thread can /// call its to acquire an - /// iterator but the same iterator should not be used simultanesouly + /// iterator but the same iterator should not be used simultaneously /// from multiple threads. The sequence supplied in is not expected to be thread-safe but it is required /// to be thread-agnostic because different threads (though never diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 52ed15837..e01aee1d9 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -586,7 +586,7 @@ public static partial class AtMostExtension /// /// Element type of sequence /// The source sequence - /// The maximun number of items a sequence must have for this + /// The maximum number of items a sequence must have for this /// function to return true /// is null /// is negative @@ -1137,7 +1137,7 @@ public static partial class CountBetweenExtension /// The source sequence /// The minimum number of items a sequence must have for this /// function to return true - /// The maximun number of items a sequence must have for this + /// The maximum number of items a sequence must have for this /// function to return true /// is null /// is negative or is less than min @@ -1214,7 +1214,7 @@ public static partial class CountDownExtension /// A function that receives the element and the current countdown /// value for the element and which returns those mapped to a /// result returned in the resulting sequence. For elements before - /// the last , the coundown value is + /// the last , the countdown value is /// null. /// /// A sequence of results returned by @@ -1778,7 +1778,7 @@ public static IEnumerable FillBackward(this IEnumerable source, Func /// Returns a sequence with each missing element in the source replaced /// with the following non-missing element in that sequence. Additional - /// parameters specifiy two functions, one used to determine if an + /// parameters specify two functions, one used to determine if an /// element is considered missing or not and another to provide the /// replacement for the missing element. /// @@ -1855,7 +1855,7 @@ public static IEnumerable FillForward(this IEnumerable source, Func /// Returns a sequence with each missing element in the source replaced /// with one based on the previous non-missing element seen in that - /// sequence. Additional parameters specifiy two functions, one used to + /// sequence. Additional parameters specify two functions, one used to /// determine if an element is considered missing or not and another /// to provide the replacement for the missing element. /// @@ -5034,7 +5034,7 @@ public static IEnumerable> ScanBy - /// Peforms a right-associative scan (inclusive prefix) on a sequence of elements. + /// Performs a right-associative scan (inclusive prefix) on a sequence of elements. /// This operator is the right-associative version of the /// LINQ operator. /// @@ -5060,7 +5060,7 @@ public static IEnumerable ScanRight(this IEnumerable => MoreEnumerable.ScanRight(source, func); /// - /// Peforms a right-associative scan (inclusive prefix) on a sequence of elements. + /// Performs a right-associative scan (inclusive prefix) on a sequence of elements. /// The specified seed value is used as the initial accumulator value. /// This operator is the right-associative version of the /// LINQ operator. @@ -6688,11 +6688,11 @@ public static IEnumerable> Transpose(this IEnumerable - /// Processes a sequence into a series of subsequences representing a windowed subset of the original + /// Processes a sequence into a series of sub-sequences representing a windowed subset of the original /// /// /// The number of sequences returned is: Max(0, sequence.Count() - windowSize) + 1
- /// Returned subsequences are buffered, but the overall operation is streamed.
+ /// Returned sub-sequences are buffered, but the overall operation is streamed.
///
/// The type of the elements of the source sequence /// The sequence to evaluate a sliding window over diff --git a/MoreLinq/FillBackward.cs b/MoreLinq/FillBackward.cs index b7edd86b6..c07c8a81b 100644 --- a/MoreLinq/FillBackward.cs +++ b/MoreLinq/FillBackward.cs @@ -74,7 +74,7 @@ public static IEnumerable FillBackward(this IEnumerable source, Func /// Returns a sequence with each missing element in the source replaced /// with the following non-missing element in that sequence. Additional - /// parameters specifiy two functions, one used to determine if an + /// parameters specify two functions, one used to determine if an /// element is considered missing or not and another to provide the /// replacement for the missing element. /// diff --git a/MoreLinq/FillForward.cs b/MoreLinq/FillForward.cs index 0577b8053..dd4b52aad 100644 --- a/MoreLinq/FillForward.cs +++ b/MoreLinq/FillForward.cs @@ -74,7 +74,7 @@ public static IEnumerable FillForward(this IEnumerable source, Func /// Returns a sequence with each missing element in the source replaced /// with one based on the previous non-missing element seen in that - /// sequence. Additional parameters specifiy two functions, one used to + /// sequence. Additional parameters specify two functions, one used to /// determine if an element is considered missing or not and another /// to provide the replacement for the missing element. /// diff --git a/MoreLinq/FullJoin.cs b/MoreLinq/FullJoin.cs index 251cd6ec5..877e35799 100644 --- a/MoreLinq/FullJoin.cs +++ b/MoreLinq/FullJoin.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copysecond (c) 2017 Atif Aziz. All seconds reserved. +// Copyright (c) 2017 Atif Aziz. All seconds reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/MoreLinq/LeftJoin.cs b/MoreLinq/LeftJoin.cs index 6648dcefc..b20fe47a5 100644 --- a/MoreLinq/LeftJoin.cs +++ b/MoreLinq/LeftJoin.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copysecond (c) 2017 Atif Aziz. All seconds reserved. +// Copyright (c) 2017 Atif Aziz. All seconds reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/MoreLinq/Permutations.cs b/MoreLinq/Permutations.cs index fd9698468..0236c5b06 100644 --- a/MoreLinq/Permutations.cs +++ b/MoreLinq/Permutations.cs @@ -51,7 +51,7 @@ sealed class PermutationEnumerator : IEnumerator> // However, there's a fly in the ointment. The factorial function grows VERY rapidly. // 13! overflows the range of a Int32; while 28! overflows the range of decimal. // To overcome these limitations, the algorithm relies on the fact that the factorial - // of N is equivalent to the evaluation of N-1 nested loops. Unfortunatley, you can't + // of N is equivalent to the evaluation of N-1 nested loops. Unfortunately, you can't // just code up a variable number of nested loops ... this is where .NET generators // with their elegant 'yield return' syntax come to the rescue. // @@ -95,9 +95,9 @@ public void Reset() // restore lexographic ordering of the permutation indexes for (var i = 0; i < _permutation.Length; i++) _permutation[i] = i; - // start a newiteration over the nested loop generator + // start a new iteration over the nested loop generator _generatorIterator = _generator.GetEnumerator(); - // we must advance the nestedloop iterator to the initial element, + // we must advance the nested loop iterator to the initial element, // this ensures that we only ever produce N!-1 calls to NextPermutation() _generatorIterator.MoveNext(); _hasMoreResults = true; // there's always at least one permutation: the original set itself @@ -123,7 +123,7 @@ public bool MoveNext() if (_hasMoreResults) _generatorIterator.Current(); // produce the next permutation ordering // we return prevResult rather than m_HasMoreResults because there is always - // at least one permtuation: the original set. Also, this provides a simple way + // at least one permutation: the original set. Also, this provides a simple way // to deal with the disparity between sets that have only one loop level (size 0-2) // and those that have two or more (size > 2). return prevResult; diff --git a/MoreLinq/Random.cs b/MoreLinq/Random.cs index 6d9b82700..684fddef8 100644 --- a/MoreLinq/Random.cs +++ b/MoreLinq/Random.cs @@ -148,7 +148,7 @@ public static IEnumerable Random(int minValue, int maxValue) /// /// Returns an infinite sequence of random integers between a given - /// minumum and a maximum using the supplied random number generator. + /// minimum and a maximum using the supplied random number generator. /// /// Generator used to produce random numbers /// Inclusive lower bound of the values returned diff --git a/MoreLinq/RandomSubset.cs b/MoreLinq/RandomSubset.cs index 6aafdc5a1..46a74aab5 100644 --- a/MoreLinq/RandomSubset.cs +++ b/MoreLinq/RandomSubset.cs @@ -66,7 +66,7 @@ public static IEnumerable RandomSubset(this IEnumerable source, int sub static IEnumerable RandomSubsetImpl(IEnumerable source, Random rand, Func, (T[], int)> seeder) { - // The simplest and most efficient way to return a random subet is to perform + // The simplest and most efficient way to return a random subset is to perform // an in-place, partial Fisher-Yates shuffle of the sequence. While we could do // a full shuffle, it would be wasteful in the cases where subsetSize is shorter // than the length of the sequence. @@ -93,7 +93,7 @@ static IEnumerable RandomSubsetImpl(IEnumerable source, Random rand, Fu --w; } - // yield the random subet as a new sequence + // yield the random subset as a new sequence for (var i = 0; i < subsetSize; i++) yield return array[i]; } diff --git a/MoreLinq/RightJoin.cs b/MoreLinq/RightJoin.cs index babbe28c1..7d19fbc39 100644 --- a/MoreLinq/RightJoin.cs +++ b/MoreLinq/RightJoin.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copysecond (c) 2017 Atif Aziz. All seconds reserved. +// Copyright (c) 2017 Atif Aziz. All seconds reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/MoreLinq/ScanRight.cs b/MoreLinq/ScanRight.cs index d7c7b84a5..8afefcece 100644 --- a/MoreLinq/ScanRight.cs +++ b/MoreLinq/ScanRight.cs @@ -23,7 +23,7 @@ namespace MoreLinq static partial class MoreEnumerable { /// - /// Peforms a right-associative scan (inclusive prefix) on a sequence of elements. + /// Performs a right-associative scan (inclusive prefix) on a sequence of elements. /// This operator is the right-associative version of the /// LINQ operator. /// @@ -54,7 +54,7 @@ public static IEnumerable ScanRight(this IEnumerable } /// - /// Peforms a right-associative scan (inclusive prefix) on a sequence of elements. + /// Performs a right-associative scan (inclusive prefix) on a sequence of elements. /// The specified seed value is used as the initial accumulator value. /// This operator is the right-associative version of the /// LINQ operator. diff --git a/MoreLinq/Subsets.cs b/MoreLinq/Subsets.cs index 3ddf9f49a..1443f97f1 100644 --- a/MoreLinq/Subsets.cs +++ b/MoreLinq/Subsets.cs @@ -66,7 +66,7 @@ public static IEnumerable> Subsets(this IEnumerable sequence) yield return subset; } - yield return sequenceAsList; // the last subet is the original set itself + yield return sequenceAsList; // the last subset is the original set itself } } } @@ -95,9 +95,9 @@ public static IEnumerable> Subsets(this IEnumerable sequence, int if (subsetSize < 0) throw new ArgumentOutOfRangeException(nameof(subsetSize), "Subset size must be >= 0"); - // NOTE: Theres an interesting trade-off that we have to make in this operator. + // NOTE: There's an interesting trade-off that we have to make in this operator. // Ideally, we would throw an exception here if the {subsetSize} parameter is - // greater than the sequence length. Unforunately, determining the length of a + // greater than the sequence length. Unfortunately, determining the length of a // sequence is not always possible without enumerating it. Herein lies the rub. // We want Subsets() to be a deferred operation that only iterates the sequence // when the caller is ready to consume the results. However, this forces us to @@ -136,7 +136,7 @@ sealed class SubsetEnumerator : IEnumerator> int _m2; // current swap index (lower index) int _k; // size of the subset being produced int _n; // size of the original set (sequence) - int _z; // count of items excluded from the subet + int _z; // count of items excluded from the subset public SubsetEnumerator(IList set, int subsetSize) { diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index a3c5375f6..011793d1f 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -23,11 +23,11 @@ namespace MoreLinq public static partial class MoreEnumerable { /// - /// Processes a sequence into a series of subsequences representing a windowed subset of the original + /// Processes a sequence into a series of sub-sequences representing a windowed subset of the original /// /// /// The number of sequences returned is: Max(0, sequence.Count() - windowSize) + 1
- /// Returned subsequences are buffered, but the overall operation is streamed.
+ /// Returned sub-sequences are buffered, but the overall operation is streamed.
///
/// The type of the elements of the source sequence /// The sequence to evaluate a sliding window over @@ -70,11 +70,11 @@ public static IEnumerable> Window(this IEnumerable - /// Processes a sequence into a series of subsequences representing a windowed subset of the original + /// Processes a sequence into a series of sub-sequences representing a windowed subset of the original ///
/// /// The number of sequences returned is: Max(0, sequence.Count() - windowSize) + 1
- /// Returned subsequences are buffered, but the overall operation is streamed.
+ /// Returned sub-sequences are buffered, but the overall operation is streamed.
///
/// The type of the elements of the source sequence /// The sequence to evaluate a sliding window over From a8f97b0fc787c75ea15544166c44f563360a1647 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 14:21:28 +0100 Subject: [PATCH 151/157] Revert "Interleave" improvements; just keep tests --- MoreLinq/Interleave.cs | 145 +++++++++++++++++++++++++++++------------ 1 file changed, 104 insertions(+), 41 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index 263fbc445..c06a0129c 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; public static partial class MoreEnumerable @@ -44,63 +45,125 @@ public static partial class MoreEnumerable /// A sequence of interleaved elements from all of the source sequences public static IEnumerable Interleave(this IEnumerable sequence, params IEnumerable[] otherSequences) + { + return Interleave(sequence, ImbalancedInterleaveStrategy.Skip, otherSequences); + } + + /// + /// Interleaves the elements of two or more sequences into a single sequence, applying the specified strategy when sequences are of unequal length + /// + /// + /// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed + /// by the second, then the third, and so on. So, for example:
+ /// { 1,2,3,1,2,3,1,2,3 } + /// ]]> + /// This operator behaves in a deferred and streaming manner.
+ /// When sequences are of unequal length, this method will use the imbalance strategy specified to + /// decide how to continue interleaving the remaining sequences. See + /// for more information.
+ /// The sequences are interleaved in the order that they appear in the + /// collection, with as the first sequence. + ///
+ /// The type of the elements of the source sequences + /// The first sequence in the interleave group + /// Defines the behavior of the operator when sequences are of unequal length + /// The other sequences in the interleave group + /// A sequence of interleaved elements from all of the source sequences + + static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInterleaveStrategy imbalanceStrategy, params IEnumerable[] otherSequences) { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); if (otherSequences.Any(s => s == null)) throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); - return InterleaveSkip(otherSequences.Prepend(sequence)); - } + return _(); IEnumerable _() + { + var sequences = new[] { sequence }.Concat(otherSequences); - private static IEnumerable InterleaveSkip(IEnumerable> sequences) - { - var enumerators = new LinkedList>(); + // produce an iterator collection for all IEnumerable instancess passed to us + var iterators = sequences.Select(e => e.GetEnumerator()).Acquire(); + List> iteratorList = null; - try - { - // First pass. create enumerators. - foreach (var sequence in sequences) + try { - var enumerator = sequence.GetEnumerator(); + iteratorList = new List>(iterators); + iterators = null; + var shouldContinue = true; + var consumedIterators = 0; + var iterCount = iteratorList.Count; - if (enumerator.MoveNext()) - { - enumerators.AddLast(enumerator); - yield return enumerator.Current; - } - else + while (shouldContinue) { - // Immediately dispose enumerators of empty sequences. - enumerator.Dispose(); - } - } + // advance every iterator and verify a value exists to be yielded + for (var index = 0; index < iterCount; index++) + { + if (!iteratorList[index].MoveNext()) + { + // check if all iterators have been consumed and we can terminate + // or if the imbalance strategy informs us that we MUST terminate + if (++consumedIterators == iterCount || imbalanceStrategy == ImbalancedInterleaveStrategy.Stop) + { + shouldContinue = false; + break; + } - var node = enumerators.First; - while (node != null) - { - var nextNode = node.Next; + iteratorList[index].Dispose(); // dispose the iterator sice we no longer need it - var enumerator = node.Value; - if (enumerator.MoveNext()) - { - yield return enumerator.Current; - } - else - { - enumerator.Dispose(); - enumerators.Remove(node); - } + // otherwise, apply the imbalance strategy + switch (imbalanceStrategy) + { + case ImbalancedInterleaveStrategy.Pad: + var newIter = iteratorList[index] = Generate(default(T), x => default).GetEnumerator(); + newIter.MoveNext(); + break; + + case ImbalancedInterleaveStrategy.Skip: + iteratorList.RemoveAt(index); // no longer visit this particular iterator + --iterCount; // reduce the expected number of iterators to visit + --index; // decrement iterator index to compensate for index shifting + --consumedIterators; // decrement consumer iterator count to stay in balance + break; + } - // Work on next node or restart from first one. - node = nextNode ?? enumerators.First; + } + } + + if (shouldContinue) // only if all iterators could be advanced + { + // yield the values of each iterator's current position + foreach (var iterator in iteratorList) + yield return iterator.Current; + } + } + } + finally + { + Debug.Assert(iteratorList != null || iterators != null); + foreach (var iter in (iteratorList ?? (IList>) iterators)) + iter.Dispose(); } } - finally - { - foreach (var enumerator in enumerators) - enumerator.Dispose(); - } + } + + /// + /// Defines the strategies available when Interleave is passed sequences of unequal length + /// + enum ImbalancedInterleaveStrategy + { + /// + /// Extends a sequence by padding its tail with default(T) + /// + Pad, + /// + /// Removes the sequence from the interleave set, and continues interleaving remaining sequences. + /// + Skip, + /// + /// Stops the interleave operation. + /// + Stop, } } } From 9f6316376007c9b4d9259f78d8f8208f00b565a1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 14:46:42 +0100 Subject: [PATCH 152/157] Remove redundant generic type arg --- MoreLinq.Test/InterleaveTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 95acfe770..9dcab3655 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -95,9 +95,9 @@ public void TestInterleaveDisposesOnError() [TestCase(3)] public void TestInterleaveDisposesOnPartialEnumeration(int count) { - using var sequenceA = TestingSequence.Of(1); - using var sequenceB = TestingSequence.Of(2); - using var sequenceC = TestingSequence.Of(3); + using var sequenceA = TestingSequence.Of(1); + using var sequenceB = TestingSequence.Of(2); + using var sequenceC = TestingSequence.Of(3); sequenceA.Interleave(sequenceB, sequenceC).Take(count).Consume(); } From 20b023ce8bc35b9633ebe6fb9f931a10dd4e0272 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 14:47:51 +0100 Subject: [PATCH 153/157] Use "Assert.That" --- MoreLinq.Test/InterleaveTest.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 9dcab3655..519a43782 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using System.Collections.Generic; using NUnit.Framework; @@ -51,7 +50,7 @@ void Code() sequenceA.Interleave(otherSequences); } - Assert.Throws(Code); + Assert.That(Code, Throws.ArgumentNullException("otherSequences")); } /// @@ -68,7 +67,7 @@ void Code() sequenceA.Interleave(sequenceB).Take(1).Consume(); } - Assert.DoesNotThrow(Code); + Assert.That(Code, Throws.Nothing); } /// @@ -80,8 +79,8 @@ public void TestInterleaveDisposesOnError() { using (var sequenceA = TestingSequence.Of()) { - Assert.Throws(() => // Expected and thrown by BreakingSequence - sequenceA.Interleave(new BreakingSequence()).Consume()); + Assert.That(() => sequenceA.Interleave(new BreakingSequence()).Consume(), + Throws.BreakException); // Expected and thrown by BreakingSequence } } From cc95f65111170d45e2a060a018878d562aecfd41 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 14:48:18 +0100 Subject: [PATCH 154/157] Use using declaration --- MoreLinq.Test/InterleaveTest.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 519a43782..f773ad557 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -77,11 +77,10 @@ void Code() [Test] public void TestInterleaveDisposesOnError() { - using (var sequenceA = TestingSequence.Of()) - { - Assert.That(() => sequenceA.Interleave(new BreakingSequence()).Consume(), - Throws.BreakException); // Expected and thrown by BreakingSequence - } + using var sequenceA = TestingSequence.Of(); + + Assert.That(() => sequenceA.Interleave(new BreakingSequence()).Consume(), + Throws.BreakException); // Expected and thrown by BreakingSequence } /// From 1baa860529531a0d05120338ffbaf1cd7462f481 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 14:50:09 +0100 Subject: [PATCH 155/157] Inline local functions --- MoreLinq.Test/InterleaveTest.cs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index f773ad557..c818e5076 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -42,15 +42,11 @@ public void TestInterleaveIsLazy() [Test] public void TestInterleaveEarlyThrowOnNullElementInOtherSequences() { - void Code() - { - var sequenceA = Enumerable.Range(1, 1); - var otherSequences = new IEnumerable[] {null!}; + var sequenceA = Enumerable.Range(1, 1); + var otherSequences = new IEnumerable[] {null!}; - sequenceA.Interleave(otherSequences); - } - - Assert.That(Code, Throws.ArgumentNullException("otherSequences")); + Assert.That(() => sequenceA.Interleave(otherSequences), + Throws.ArgumentNullException("otherSequences")); } /// @@ -59,15 +55,11 @@ void Code() [Test] public void TestInterleaveDoNoCallMoveNextEagerly() { - void Code() - { - var sequenceA = Enumerable.Range(1, 1); - var sequenceB = MoreEnumerable.From(() => throw new TestException()); - - sequenceA.Interleave(sequenceB).Take(1).Consume(); - } + var sequenceA = Enumerable.Range(1, 1); + var sequenceB = MoreEnumerable.From(() => throw new TestException()); + var result = sequenceA.Interleave(sequenceB).Take(1); - Assert.That(Code, Throws.Nothing); + Assert.That(() => result.Consume(), Throws.Nothing); } /// From dc6ac0b80481fa11e59f5380f2ef04829cab9ed5 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 14:50:29 +0100 Subject: [PATCH 156/157] Fix code formatting --- MoreLinq.Test/InterleaveTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index c818e5076..2dbdb30c5 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -43,7 +43,7 @@ public void TestInterleaveIsLazy() public void TestInterleaveEarlyThrowOnNullElementInOtherSequences() { var sequenceA = Enumerable.Range(1, 1); - var otherSequences = new IEnumerable[] {null!}; + var otherSequences = new IEnumerable[] { null! }; Assert.That(() => sequenceA.Interleave(otherSequences), Throws.ArgumentNullException("otherSequences")); From 681f45699e7e81b424e26e26deaacae12f105f8b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 14 Jan 2023 15:03:52 +0100 Subject: [PATCH 157/157] Use "TestingSequence" consistently --- MoreLinq.Test/InterleaveTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 2dbdb30c5..77dddb0cf 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -55,8 +55,9 @@ public void TestInterleaveEarlyThrowOnNullElementInOtherSequences() [Test] public void TestInterleaveDoNoCallMoveNextEagerly() { - var sequenceA = Enumerable.Range(1, 1); - var sequenceB = MoreEnumerable.From(() => throw new TestException()); + using var sequenceA = TestingSequence.Of(1); + using var sequenceB = MoreEnumerable.From(() => throw new TestException()) + .AsTestingSequence(); var result = sequenceA.Interleave(sequenceB).Take(1); Assert.That(() => result.Consume(), Throws.Nothing);