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

KSP-CKAN / CKAN / 25198663652

01 May 2026 01:58AM UTC coverage: 87.472% (+1.6%) from 85.851%
25198663652

push

github

HebaruSan
Merge #4594 Windows dark mode in .NET 10 build

1982 of 2112 branches covered (93.84%)

Branch coverage included in aggregate %.

35 of 36 new or added lines in 7 files covered. (97.22%)

33 existing lines in 24 files now uncovered.

8491 of 9861 relevant lines covered (86.11%)

2.69 hits per line

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

82.14
/Core/Extensions/IOExtensions.cs
1
using System;
2
using System.Linq;
3
using System.IO;
4
using System.Collections.Generic;
5
using System.Threading;
6
using System.Security.Cryptography;
7
using Timer = System.Timers.Timer;
8

9
namespace CKAN.Extensions
10
{
11
    public static class IOExtensions
12
    {
13
        /// <summary>
14
        /// Extension method to get from a directory to its drive.
15
        /// </summary>
16
        /// <param name="dir">Any DirectoryInfo object</param>
17
        /// <returns>The DriveInfo associated with this directory, if any, else null</returns>
18
        public static DriveInfo? GetDrive(this DirectoryInfo dir)
19
            => Utilities.DefaultIfThrows(() => new DriveInfo(dir.FullName));
3✔
20

21
        public static bool AncestorPathOf(this DirectoryInfo outer, DirectoryInfo inner)
22
            => inner.TraverseNodes(di => di.Parent)
3✔
23
                    .Select(di => di.FullName)
3✔
24
                    .Any(di => outer.FullName.Equals(di, Platform.PathComparison));
3✔
25

26
        /// <summary>
27
        /// File.WriteAllText replacement that doesn't sometimes write all NULs instead on Windows.
28
        /// https://stackoverflow.com/questions/54078564
29
        /// </summary>
30
        /// <param name="contents">The string to write</param>
31
        /// <param name="path">Where to save it</param>
32
        public static void WriteThroughTo(this string contents, string path)
33
        {
34
            using (var stream = File.Create(path, contents.Length + 1,
3✔
35
                                            FileOptions.WriteThrough))
36
            using (var writer = new StreamWriter(stream, Encoding.UTF8))
3✔
37
            {
38
                writer.Write(contents);
3✔
39
                // If we don't Flush, the file can be truncated,
40
                // and if we Close, the 'using' block throws an exception
41
                writer.Flush();
3✔
42
            }
3✔
43
        }
3✔
44

45
        /// <summary>
46
        /// A version of Stream.CopyTo with progress updates.
47
        /// </summary>
48
        /// <param name="src">Stream from which to copy</param>
49
        /// <param name="dest">Stream to which to copy</param>
50
        /// <param name="progress">Callback to notify as we traverse the input, called with count of bytes received</param>
51
        /// <param name="idleInterval">Maximum timespand to elapse between progress updates, will synthesize extra updates as needed</param>
52
        /// <param name="hasher">Hash algorithm to use, if any</param>
53
        /// <param name="cancelToken">Cancellation token to cancel the operation</param>
54
        public static void CopyTo(this Stream       src,
55
                                  Stream            dest,
56
                                  IProgress<long>   progress,
57
                                  TimeSpan?         idleInterval = null,
58
                                  HashAlgorithm?    hasher       = null,
59
                                  CancellationToken cancelToken  = default)
60
        {
61
            // CopyTo says its default buffer is 81920, but we want more than 1 update for a 100 KiB file
62
            const int bufSize = 16384;
63
            var buffer = new byte[bufSize];
3✔
64
            long total = 0;
3✔
65
            var lastProgressTime = DateTime.Now;
3✔
66
            // Sometimes a server just freezes and times out, send extra updates if requested
67
            Timer? timer = null;
3✔
68
            if (idleInterval.HasValue)
3✔
69
            {
70
                timer = new Timer(idleInterval.Value > minProgressInterval
3✔
71
                                      ? idleInterval.Value.TotalMilliseconds
72
                                      : minProgressInterval.TotalMilliseconds)
73
                {
74
                    AutoReset = true,
75
                };
76
                timer.Elapsed += (sender, args) =>
3✔
77
                {
78
                    progress.Report(total);
×
79
                    lastProgressTime = DateTime.Now;
×
80
                };
×
81
                timer.Start();
3✔
82
            }
83
            // Make sure we get an initial progress notification at the start
84
            progress.Report(total);
3✔
UNCOV
85
            while (true)
×
86
            {
87
                var bytesRead = src.Read(buffer, 0, bufSize);
3✔
88
                if (bytesRead == 0)
3✔
89
                {
90
                    hasher?.TransformFinalBlock(buffer, 0, 0);
3✔
91
                    break;
3✔
92
                }
93
                dest.Write(buffer, 0, bytesRead);
3✔
94
                hasher?.TransformBlock(buffer, 0, bytesRead, buffer, 0);
3✔
95
                total += bytesRead;
3✔
96
                cancelToken.ThrowIfCancellationRequested();
3✔
97
                var now = DateTime.Now;
3✔
98
                if (now - lastProgressTime >= minProgressInterval)
3✔
99
                {
100
                    timer?.Stop();
×
101
                    timer?.Start();
×
102
                    progress.Report(total);
×
103
                    lastProgressTime = now;
×
104
                }
105
            }
106
            if (timer != null)
3✔
107
            {
108
                timer.Stop();
3✔
109
                timer.Close();
3✔
110
                timer = null;
3✔
111
            }
112
            // Make sure we get a final progress notification after we're done
113
            progress.Report(total);
3✔
114
        }
3✔
115

116
        public static IEnumerable<byte> BytesFromStream(this Stream s)
117
        {
118
            int b;
119
            while ((b = s.ReadByte()) != -1)
3✔
120
            {
121
                yield return Convert.ToByte(b);
3✔
122
            }
123
        }
3✔
124

125
        private static readonly TimeSpan minProgressInterval = TimeSpan.FromMilliseconds(200);
3✔
126
    }
127
}
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