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

KSP-CKAN / CKAN / 20999419978

14 Jan 2026 03:20PM UTC coverage: 85.275% (+0.04%) from 85.236%
20999419978

push

github

HebaruSan
Merge #4498 Work around GitHub API has_wiki defect

1995 of 2160 branches covered (92.36%)

Branch coverage included in aggregate %.

4 of 8 new or added lines in 1 file covered. (50.0%)

6 existing lines in 1 file now uncovered.

11944 of 14186 relevant lines covered (84.2%)

1.76 hits per line

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

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

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

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

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

110
        public static IEnumerable<byte> BytesFromStream(this Stream s)
111
        {
2✔
112
            int b;
113
            while ((b = s.ReadByte()) != -1)
2✔
114
            {
2✔
115
                yield return Convert.ToByte(b);
2✔
116
            }
2✔
117
        }
2✔
118

119
        private static readonly TimeSpan minProgressInterval = TimeSpan.FromMilliseconds(200);
2✔
120
    }
121
}
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