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

KSP-CKAN / CKAN / 18889661700

28 Oct 2025 09:24PM UTC coverage: 82.304% (+0.4%) from 81.873%
18889661700

Pull #4454

github

HebaruSan
Build on Windows, upload multi-platform coverage
Pull Request #4454: Build on Windows, upload multi-platform coverage

5571 of 7138 branches covered (78.05%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 3 files covered. (100.0%)

12 existing lines in 2 files now uncovered.

12001 of 14212 relevant lines covered (84.44%)

2.6 hits per line

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

88.67
/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.Threading.Tasks;
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);
3✔
15

16
        public static IEnumerable<T> AsParallelIf<T>(this IEnumerable<T> source,
17
                                                     bool                parallel)
18
            => parallel ? source.AsParallel()
3✔
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
        {
3✔
26
            long count       = 0;
3✔
27
            int  prevPercent = -1;
3✔
28
            return progress == null
3✔
29
                ? source
30
                : source.Select(item =>
31
                {
3✔
32
                    var percent = (int)(100 * Interlocked.Increment(ref count) / totalCount);
3✔
33
                    if (percent > prevPercent)
3!
34
                    {
3✔
35
                        progress.Report(percent);
3✔
36
                        prevPercent = percent;
3✔
37
                    }
3✔
38
                    return item;
3✔
39
                });
3✔
40
        }
3✔
41

42
        public static IEnumerable<T> Memoize<T>(this IEnumerable<T> source)
43
            => source is Memoized<T>
3!
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
        {
3✔
51
            var pairs = source.ToList();
3✔
52
            foreach (var kvp in pairs)
9✔
53
            {
3✔
54
                if (predicate(kvp))
3✔
55
                {
3✔
56
                    source.Remove(kvp.Key);
3✔
57
                }
3✔
58
            }
3✔
59
        }
3✔
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,
3✔
71
                                (a, b) => a + b);
3✔
72

73
        /// <summary>
74
        /// Zip a sequence with a sequence generated from the first sequence using the given function
75
        /// </summary>
76
        /// <typeparam name="T">Source sequence type</typeparam>
77
        /// <typeparam name="V">Type of elements returned by func</typeparam>
78
        /// <param name="source">Source sequence</param>
79
        /// <param name="func">Function to generate values of second sequence given source</param>
80
        /// <returns>Sequence of tuples containing pairs from each sequence</returns>
81
        public static IEnumerable<(T First, V Second)> ZipBy<T, V>(this IEnumerable<T> source, Func<IEnumerable<T>, IEnumerable<V>> func)
82
            => source.ToArray() is T[] array
3!
83
                   ? array.Zip(func(array))
84
                   : Enumerable.Empty<(T First, V Second)>();
85

86
        /// <summary>
87
        /// Insert new elements between consecutive pairs of existing elements,
88
        /// preserving the original elements in order, and using null to
89
        /// represent the elements before the beginning and after the end.
90
        /// </summary>
91
        /// <param name="source">Sequence into which to inject</param>
92
        /// <param name="inBetween">Function to generate the new elements</param>
93
        /// <returns>Sequence with new elements in it</returns>
94
        public static IEnumerable<T> Inject<T>(this IEnumerable<T> source,
95
                                               Func<T?, T?, T>     inBetween)
96
            where T : class
97
        {
3✔
98
            using (var e = source.GetEnumerator())
3✔
99
            {
3✔
100
                if (e.MoveNext())
3✔
101
                {
3✔
102
                    yield return inBetween(null, e.Current);
3✔
103
                    yield return e.Current;
3✔
104
                    var prev = e.Current;
3✔
105
                    while (e.MoveNext())
3✔
106
                    {
3✔
107
                        yield return inBetween(prev, e.Current);
3✔
108
                        yield return e.Current;
3✔
109
                        prev = e.Current;
3✔
110
                    }
3✔
111
                    yield return inBetween(prev, null);
3✔
112
                }
3✔
113
                else
UNCOV
114
                {
×
UNCOV
115
                    yield return inBetween(null, null);
×
116
                }
×
117
            }
3✔
118
        }
3✔
119

120
        /// <summary>
121
        /// Poor man's PLINQ, a trivially parallelized SelectMany that
122
        /// runs one process per item in the source sequence.
123
        /// For short sequences and long-running functions,
124
        /// when you don't feel like fighting with Partitioner.Create
125
        /// over how many items should be in each partition.
126
        /// </summary>
127
        /// <param name="source">The sequence to process</param>
128
        /// <param name="func">The function to apply to each item in the sequence</param>
129
        /// <returns>Sequence of all values from the function</returns>
130
        public static IEnumerable<V> SelectManyTasks<T, V>(this IReadOnlyCollection<T> source,
131
                                                           Func<T, IEnumerable<V>>     func)
132
        {
3✔
133
            if (source.Count <= 1)
3!
134
            {
3✔
135
                return source.SelectMany(func);
3✔
136
            }
137
            else
UNCOV
138
            {
×
UNCOV
139
                var tasks = source.Select(item => Task.Run(() => func(item).ToArray()))
×
140
                                  // Force non-lazy creation of tasks
141
                                  .ToArray();
UNCOV
142
                return Utilities.WithRethrowInner(() =>
×
UNCOV
143
                {
×
144
                    // Without this, later tasks don't finish if an earlier one throws
145
                    Task.WaitAll(tasks);
×
UNCOV
146
                    return tasks.SelectMany(task => task.Result);
×
147
                });
×
148
            }
149
        }
3✔
150

151
        /// <summary>
152
        /// Generate a sequence from a linked list
153
        /// </summary>
154
        /// <param name="start">The first node</param>
155
        /// <param name="getNext">Function to go from one node to the next</param>
156
        /// <returns>All the nodes in the list as a sequence</returns>
157
        public static IEnumerable<T> TraverseNodes<T>(this T start, Func<T, T?> getNext)
158
            where T : class
159
        {
3✔
160
            for (T? t = start; t != null; t = Utilities.DefaultIfThrows(() => getNext(t)))
7✔
161
            {
3✔
162
                yield return t;
3✔
163
            }
3✔
164
        }
3✔
165

166
        /// <summary>
167
        /// Apply a function to a sequence and handle any exceptions that are thrown
168
        /// </summary>
169
        /// <typeparam name="TSrc">Type of source sequence</typeparam>
170
        /// <typeparam name="TDest">Type of destination sequence</typeparam>
171
        /// <param name="source">Source sequence</param>
172
        /// <param name="func">Function to apply to each item</param>
173
        /// <param name="onThrow">Function to call if there's an exception</param>
174
        /// <returns>Sequence of return values of given function</returns>
175
        public static IEnumerable<TDest?> SelectWithCatch<TSrc, TDest>(this IEnumerable<TSrc>       source,
176
                                                                       Func<TSrc, TDest>             func,
177
                                                                       Func<TSrc, Exception, TDest?> onThrow)
178
                where TDest : class
179
            => source.Select(item => Utilities.DefaultIfThrows(()  => func(item),
3✔
180
                                                               exc => onThrow(item, exc)));
3✔
181

182
        /// <summary>
183
        /// Apply a sequence-generating function to a sequence and combine the subsequences,
184
        /// skipping elements in the input sequence that throw exceptions.
185
        /// </summary>
186
        /// <param name="source">The sequence to process</param>
187
        /// <param name="func">The function that generates more subsequences</param>
188
        /// <returns>Sequence of values</returns>
189
        public static IEnumerable<V> SelectManyWithCatch<T, V>(this IEnumerable<T>     source,
190
                                                               Func<T, IEnumerable<V>> func)
191
            => source.SelectMany(elt => Utilities.DefaultIfThrows(() => func(elt))
3✔
192
                                        ?? Enumerable.Empty<V>());
193

194
        /// <summary>
195
        /// Get a hash code for a sequence with a variable number of elements
196
        /// </summary>
197
        /// <typeparam name="T">Type of the elements in the sequence</typeparam>
198
        /// <param name="source">The sequence</param>
199
        /// <returns></returns>
200
        public static int ToSequenceHashCode<T>(this IEnumerable<T> source)
201
            => source.Aggregate(new HashCode(),
3✔
202
                                (hc, item) =>
203
                                {
3✔
204
                                    hc.Add(item);
3✔
205
                                    return hc;
3✔
206
                                },
3✔
207
                                hc => hc.ToHashCode());
3✔
208

209
        /// <summary>
210
        /// Accumulate a sequence of values based on a seed value and a function,
211
        /// similar to Aggregate but including intermediate values
212
        /// </summary>
213
        /// <typeparam name="TSource"></typeparam>
214
        /// <typeparam name="TResult"></typeparam>
215
        /// <param name="source">Input sequence</param>
216
        /// <param name="seed">First intermediate value, not included in return sequence</param>
217
        /// <param name="func">Function to transform a previous result and a next input sequence element into the next result</param>
218
        /// <returns></returns>
219
        public static IEnumerable<TResult> Accumulate<TSource, TResult>(this IEnumerable<TSource>       source,
220
                                                                        TResult                         seed,
221
                                                                        Func<TResult, TSource, TResult> func)
222
        {
3✔
223
            var result = seed;
3✔
224
            foreach (var item in source)
9✔
225
            {
3✔
226
                result = func(result, item);
3✔
227
                yield return result;
3✔
228

229

230
            }
3✔
UNCOV
231
        }
×
232

233
        public static IEnumerable<string> ExceptContainsAny(this   IEnumerable<string> source,
234
                                                            params string[]            strings)
235
            => source.Where(elt => !strings.Any(s => elt.Contains(s)));
3✔
236

237
        public static bool IsEmptyOrAny<T>(this IReadOnlyCollection<T> source,
238
                                           Func<T, bool>               func)
239
            => source.Count == 0 || source.Any(func);
3!
240

241
        public static Dictionary<K, T[]> ToGroupedDictionary<T, K>(this IEnumerable<T> source,
242
                                                                   Func<T, K>          keyFunc)
243
            where K: notnull
244
            => source.GroupBy(keyFunc)
3✔
245
                     .ToDictionary(grp => grp.Key,
3✔
246
                                   grp => grp.ToArray());
3✔
247

248
        public static Dictionary<K, V[]> ToGroupedDictionary<T, K, V>(this IEnumerable<T> source,
249
                                                                      Func<T, K>          keyFunc,
250
                                                                      Func<T, V>          valFunc)
251
            where K: notnull
252
            => source.GroupBy(keyFunc, valFunc)
3✔
253
                     .ToDictionary(grp => grp.Key,
3✔
254
                                   grp => grp.ToArray());
3✔
255

256
    }
257

258
    /// <summary>
259
    /// Memoized lazy evaluation in C#!
260
    /// From https://stackoverflow.com/a/12428250/2422988
261
    /// </summary>
262
    public class Memoized<T> : IEnumerable<T>
263
    {
264
        public Memoized(IEnumerable<T> source)
3✔
265
        {
3✔
266
            this.source = source;
3✔
267
        }
3✔
268

269
        IEnumerator IEnumerable.GetEnumerator()
270
            => GetEnumerator();
3✔
271

272
        public IEnumerator<T> GetEnumerator()
273
        {
3✔
274
            lock (gate)
3✔
275
            {
3✔
276
                if (isCacheComplete)
3✔
277
                {
3✔
278
                    return cache.GetEnumerator();
3✔
279
                }
280
                else
281
                {
3✔
282
                    enumerator ??= source.GetEnumerator();
3✔
283
                }
3✔
284
            }
3✔
285
            return GetMemoizingEnumerator();
3✔
286
        }
3✔
287

288
        private IEnumerator<T> GetMemoizingEnumerator()
289
        {
3✔
290
            for (int index = 0; TryGetItem(index, out T? item); ++index)
7✔
291
            {
3✔
292
                yield return item;
3✔
293
            }
3✔
294
        }
3✔
295

296
        private bool TryGetItem(int index,
297
                                out T item)
298
        {
3✔
299
            lock (gate)
3✔
300
            {
3✔
301
                if (enumerator is not null && !IsItemInCache(index))
3✔
302
                {
3✔
303
                    // The iteration may have completed while waiting for the lock
304
                    #nullable disable
305
                    if (isCacheComplete)
3!
UNCOV
306
                    {
×
UNCOV
307
                        item = default;
×
308
                        return false;
×
309
                    }
310
                    if (!enumerator.MoveNext())
3✔
311
                    {
3✔
312
                        item = default;
3✔
313
                        isCacheComplete = true;
3✔
314
                        enumerator.Dispose();
3✔
315
                        return false;
3✔
316
                    }
317
                    #nullable enable
318
                    cache.Add(enumerator.Current);
3✔
319
                }
3✔
320
                item = cache[index];
3✔
321
                return true;
3✔
322
            }
323
        }
3✔
324

325
        private bool IsItemInCache(int index)
326
            => index < cache.Count;
3✔
327

328
        private readonly IEnumerable<T>  source;
329
        private          IEnumerator<T>? enumerator;
330
        private readonly List<T>         cache = new List<T>();
3✔
331
        private          bool            isCacheComplete;
332
        private readonly object          gate = new object();
3✔
333
    }
334
}
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