• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

KSP-CKAN / CKAN / 15714778159

17 Jun 2025 06:03PM UTC coverage: 28.326% (+1.2%) from 27.151%
15714778159

Pull #4396

github

web-flow
Merge 61a285d24 into a01cebc8d
Pull Request #4396: More tests for Core

3880 of 12089 branches covered (32.1%)

Branch coverage included in aggregate %.

12 of 42 new or added lines in 12 files covered. (28.57%)

5 existing lines in 2 files now uncovered.

8370 of 31158 relevant lines covered (26.86%)

0.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.71
/Core/Extensions/EnumerableExtensions.cs
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Collections.Concurrent;
5
using System.Linq;
6
using System.Threading;
7
using System.Text.RegularExpressions;
8

9
namespace CKAN.Extensions
10
{
11
    public static class EnumerableExtensions
12
    {
13
        public static ConcurrentDictionary<K, V> ToConcurrentDictionary<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) where K: class
14
            => new ConcurrentDictionary<K, V>(pairs);
2✔
15

16
        public static IEnumerable<T> AsParallelIf<T>(this IEnumerable<T> source,
17
                                                     bool                parallel)
18
            => parallel ? source.AsParallel()
2✔
19
                        : source;
20

21
        // https://stackoverflow.com/a/55591477/2422988
22
        public static ParallelQuery<T> WithProgress<T>(this ParallelQuery<T> source,
23
                                                       long                  totalCount,
24
                                                       IProgress<int>?       progress)
25
        {
2✔
26
            long count       = 0;
2✔
27
            int  prevPercent = -1;
2✔
28
            return progress == null
2!
29
                ? source
30
                : source.Select(item =>
31
                {
×
32
                    var percent = (int)(100 * Interlocked.Increment(ref count) / totalCount);
×
33
                    if (percent > prevPercent)
×
34
                    {
×
35
                        progress.Report(percent);
×
36
                        prevPercent = percent;
×
37
                    }
×
38
                    return item;
×
39
                });
×
40
        }
2✔
41

42
        public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source)
43
            => source is Memoized<T>
2!
44
                   // Already memoized, don't wrap another layer
45
                   ? source
46
                   : new Memoized<T>(source);
47

48
        public static void RemoveWhere<K, V>(this Dictionary<K, V> source,
49
                                             Func<KeyValuePair<K, V>, bool> predicate) where K: class
50
        {
2✔
51
            var pairs = source.ToList();
2✔
52
            foreach (var kvp in pairs)
5✔
53
            {
2✔
54
                if (predicate(kvp))
2✔
55
                {
2✔
56
                    source.Remove(kvp.Key);
2✔
57
                }
2✔
58
            }
2✔
59
        }
2✔
60

61
        /// <summary>
62
        /// Sum a sequence of TimeSpans.
63
        /// Mysteriously not defined standardly.
64
        /// </summary>
65
        /// <param name="source">Sequence of TimeSpans to sum</param>
66
        /// <returns>
67
        /// Sum of the TimeSpans
68
        /// </returns>
69
        public static TimeSpan Sum(this IEnumerable<TimeSpan> source)
70
            => source.Aggregate(TimeSpan.Zero,
×
71
                                (a, b) => a + b);
×
72

73
        /// <summary>
74
        /// Select : SelectMany :: Zip : ZipMany
75
        /// </summary>
76
        /// <param name="seq1">Sequence from which to get first values</param>
77
        /// <param name="seq2">Sequence from which to get second values</param>
78
        /// <param name="func">Function to transform a value from each input sequence into a sequence of multiple outputs</param>
79
        /// <returns>Flattened sequence of values from func applies to seq1 and seq2</returns>
80
        public static IEnumerable<V> ZipMany<T, U, V>(this IEnumerable<T> seq1, IEnumerable<U> seq2, Func<T, U, IEnumerable<V>> func)
81
            => seq1.Zip(seq2, func).SelectMany(seqs => seqs);
×
82

83
        /// <summary>
84
        /// Zip a sequence with a sequence generated from the first sequence using the given function
85
        /// </summary>
86
        /// <typeparam name="T">Source sequence type</typeparam>
87
        /// <typeparam name="V">Type of elements returned by func</typeparam>
88
        /// <param name="source">Source sequence</param>
89
        /// <param name="func">Function to generate values of second sequence given source</param>
90
        /// <returns>Sequence of tuples containing pairs from each sequence</returns>
91
        public static IEnumerable<(T First, V Second)> ZipBy<T, V>(this IEnumerable<T> source, Func<IEnumerable<T>, IEnumerable<V>> func)
92
            => source.ToArray() is T[] array
2!
93
                   ? array.Zip(func(array))
94
                   : Enumerable.Empty<(T First, V Second)>();
95

96
        /// <summary>
97
        /// Insert new elements between consecutive pairs of existing elements,
98
        /// preserving the original elements in order, and using null to
99
        /// represent the elements before the beginning and after the end.
100
        /// </summary>
101
        /// <param name="source">Sequence into which to inject</param>
102
        /// <param name="inBetween">Function to generate the new elements</param>
103
        /// <returns>Sequence with new elements in it</returns>
104
        public static IEnumerable<T> Inject<T>(this IEnumerable<T> source,
105
                                               Func<T?, T?, T>     inBetween)
106
            where T : class
NEW
107
        {
×
NEW
108
            using (var e = source.GetEnumerator())
×
NEW
109
            {
×
NEW
110
                if (e.MoveNext())
×
NEW
111
                {
×
NEW
112
                    yield return inBetween(null, e.Current);
×
NEW
113
                    yield return e.Current;
×
NEW
114
                    var prev = e.Current;
×
NEW
115
                    while (e.MoveNext())
×
NEW
116
                    {
×
NEW
117
                        yield return inBetween(prev, e.Current);
×
NEW
118
                        yield return e.Current;
×
NEW
119
                        prev = e.Current;
×
NEW
120
                    }
×
NEW
121
                    yield return inBetween(prev, null);
×
NEW
122
                }
×
123
                else
NEW
124
                {
×
NEW
125
                    yield return inBetween(null, null);
×
NEW
126
                }
×
NEW
127
            }
×
NEW
128
        }
×
129

130
        /// <summary>
131
        /// Generate a sequence from a linked list
132
        /// </summary>
133
        /// <param name="start">The first node</param>
134
        /// <param name="getNext">Function to go from one node to the next</param>
135
        /// <returns>All the nodes in the list as a sequence</returns>
136
        public static IEnumerable<T> TraverseNodes<T>(this T start, Func<T, T?> getNext)
137
            where T : class
138
        {
2✔
139
            for (T? t = start; t != null; t = Utilities.DefaultIfThrows(() => getNext(t)))
4✔
140
            {
2✔
141
                yield return t;
2✔
142
            }
2✔
143
        }
2✔
144

145
        /// <summary>
146
        /// Try matching a regex against a series of strings and return the Match objects
147
        /// </summary>
148
        /// <param name="source">Sequence of strings to scan</param>
149
        /// <param name="pattern">Pattern to match</param>
150
        /// <returns>Sequence of Match objects</returns>
151
        public static IEnumerable<Match> WithMatches(this IEnumerable<string> source, Regex pattern)
152
            => source.Select(val => pattern.TryMatch(val, out Match? match) ? match : null)
×
153
                     .OfType<Match>();
154

155
        /// <summary>
156
        /// Apply a function to a sequence and handle any exceptions that are thrown
157
        /// </summary>
158
        /// <typeparam name="TSrc">Type of source sequence</typeparam>
159
        /// <typeparam name="TDest">Type of destination sequence</typeparam>
160
        /// <param name="source">Source sequence</param>
161
        /// <param name="func">Function to apply to each item</param>
162
        /// <param name="onThrow">Function to call if there's an exception</param>
163
        /// <returns>Sequence of return values of given function</returns>
164
        public static IEnumerable<TDest?> SelectWithCatch<TSrc, TDest>(this IEnumerable<TSrc>       source,
165
                                                                      Func<TSrc, TDest>             func,
166
                                                                      Func<TSrc, Exception, TDest?> onThrow)
167
                where TDest : class
168
            => source.Select(item => Utilities.DefaultIfThrows(()  => func(item),
2✔
169
                                                               exc => onThrow(item, exc)));
2✔
170

171
        /// <summary>
172
        /// Get a hash code for a sequence with a variable number of elements
173
        /// </summary>
174
        /// <typeparam name="T">Type of the elements in the sequence</typeparam>
175
        /// <param name="source">The sequence</param>
176
        /// <returns></returns>
177
        public static int ToSequenceHashCode<T>(this IEnumerable<T> source)
178
            => source.Aggregate(new HashCode(),
×
179
                                (hc, item) =>
180
                                {
×
181
                                    hc.Add(item);
×
182
                                    return hc;
×
183
                                },
×
184
                                hc => hc.ToHashCode());
×
185

186
        /// <summary>
187
        /// Accumulate a sequence of values based on a seed value and a function,
188
        /// similar to Aggregate but including intermediate values
189
        /// </summary>
190
        /// <typeparam name="TSource"></typeparam>
191
        /// <typeparam name="TResult"></typeparam>
192
        /// <param name="source">Input sequence</param>
193
        /// <param name="seed">First intermediate value, not included in return sequence</param>
194
        /// <param name="func">Function to transform a previous result and a next input sequence element into the next result</param>
195
        /// <returns></returns>
196
        public static IEnumerable<TResult> Accumulate<TSource, TResult>(this IEnumerable<TSource>       source,
197
                                                                        TResult                         seed,
198
                                                                        Func<TResult, TSource, TResult> func)
199
        {
×
200
            var result = seed;
×
201
            foreach (var item in source)
×
202
            {
×
203
                result = func(result, item);
×
204
                yield return result;
×
205
            }
×
206
        }
×
207
    }
208

209
    /// <summary>
210
    /// Memoized lazy evaluation in C#!
211
    /// From https://stackoverflow.com/a/12428250/2422988
212
    /// </summary>
213
    public class Memoized<T> : IEnumerable<T>
214
    {
215
        public Memoized(IEnumerable<T> source)
2✔
216
        {
2✔
217
            this.source = source;
2✔
218
        }
2✔
219

220
        IEnumerator IEnumerable.GetEnumerator()
221
            => GetEnumerator();
×
222

223
        public IEnumerator<T> GetEnumerator()
224
        {
2✔
225
            lock (gate)
2✔
226
            {
2✔
227
                if (isCacheComplete)
2✔
228
                {
2✔
229
                    return cache.GetEnumerator();
2✔
230
                }
231
                else
232
                {
2✔
233
                    enumerator ??= source.GetEnumerator();
2!
234
                }
2✔
235
            }
2✔
236
            return GetMemoizingEnumerator();
2✔
237
        }
2✔
238

239
        private IEnumerator<T> GetMemoizingEnumerator()
240
        {
2✔
241
            for (int index = 0; TryGetItem(index, out T? item); ++index)
4✔
242
            {
2✔
243
                yield return item;
2✔
244
            }
2✔
245
        }
2✔
246

247
        private bool TryGetItem(int index,
248
                                out T item)
249
        {
2✔
250
            lock (gate)
2✔
251
            {
2✔
252
                if (enumerator is not null && !IsItemInCache(index))
2!
253
                {
2✔
254
                    // The iteration may have completed while waiting for the lock
255
                    #nullable disable
256
                    if (isCacheComplete)
2!
257
                    {
×
258
                        item = default;
×
259
                        return false;
×
260
                    }
261
                    if (!enumerator.MoveNext())
2✔
262
                    {
2✔
263
                        item = default;
2✔
264
                        isCacheComplete = true;
2✔
265
                        enumerator.Dispose();
2✔
266
                        return false;
2✔
267
                    }
268
                    #nullable enable
269
                    cache.Add(enumerator.Current);
2✔
270
                }
2✔
271
                item = cache[index];
2✔
272
                return true;
2✔
273
            }
274
        }
2✔
275

276
        private bool IsItemInCache(int index)
277
            => index < cache.Count;
2✔
278

279
        private readonly IEnumerable<T>  source;
280
        private          IEnumerator<T>? enumerator;
281
        private readonly List<T>         cache = new List<T>();
2✔
282
        private          bool            isCacheComplete;
283
        private readonly object          gate = new object();
2✔
284
    }
285
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc