< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.KestrunHostManager
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/KestrunHostManager.cs
Tag: Kestrun/Kestrun@2d87023b37eb91155071c91dd3d6a2eeb3004705
Line coverage
86%
Covered lines: 75
Uncovered lines: 12
Coverable lines: 87
Total lines: 341
Line coverage: 86.2%
Branch coverage
80%
Covered branches: 56
Total branches: 70
Branch coverage: 80%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 08/26/2025 - 01:25:22 Line coverage: 82% (64/78) Branch coverage: 75.8% (47/62) Total lines: 303 Tag: Kestrun/Kestrun@07f821172e5dc3657f1be7e6818f18d6721cf38a09/03/2025 - 13:55:51 Line coverage: 81.8% (63/77) Branch coverage: 75.8% (47/62) Total lines: 298 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/06/2025 - 18:30:33 Line coverage: 85.3% (70/82) Branch coverage: 80% (56/70) Total lines: 321 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/16/2025 - 04:01:29 Line coverage: 85.3% (70/82) Branch coverage: 80% (56/70) Total lines: 322 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb10/13/2025 - 16:52:37 Line coverage: 86.2% (75/87) Branch coverage: 80% (56/70) Total lines: 341 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e 08/26/2025 - 01:25:22 Line coverage: 82% (64/78) Branch coverage: 75.8% (47/62) Total lines: 303 Tag: Kestrun/Kestrun@07f821172e5dc3657f1be7e6818f18d6721cf38a09/03/2025 - 13:55:51 Line coverage: 81.8% (63/77) Branch coverage: 75.8% (47/62) Total lines: 298 Tag: Kestrun/Kestrun@80ce2a54be2f719c7be1c21a92a8156bfdc48eb409/06/2025 - 18:30:33 Line coverage: 85.3% (70/82) Branch coverage: 80% (56/70) Total lines: 321 Tag: Kestrun/Kestrun@aeddbedb8a96e9137aac94c2d5edd011b57ac87109/16/2025 - 04:01:29 Line coverage: 85.3% (70/82) Branch coverage: 80% (56/70) Total lines: 322 Tag: Kestrun/Kestrun@e5263347b0baba68d9fd62ffbf60a7dd87f994bb10/13/2025 - 16:52:37 Line coverage: 86.2% (75/87) Branch coverage: 80% (56/70) Total lines: 341 Tag: Kestrun/Kestrun@10d476bee71c71ad215bb8ab59f219887b5b4a5e

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_InstanceNames()100%11100%
get_KestrunRoot()100%11100%
set_KestrunRoot(...)100%44100%
Create(...)87.5%8890%
Create(...)100%210%
Create(...)70%101086.66%
Contains(...)100%11100%
TryGet(...)100%11100%
Get(...)100%22100%
get_Default()100%44100%
SetDefault(...)100%22100%
IsRunning(...)50%44100%
StartAsync()50%11650%
StopAsync()83.33%6687.5%
Stop(...)50%2266.66%
StopAllAsync()100%44100%
Destroy(...)78.57%151485.71%
DestroyAll()100%44100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/KestrunHostManager.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using Kestrun.Hosting;
 3using Serilog;
 4using Serilog.Events;
 5
 6namespace Kestrun;
 7
 8/// <summary>
 9/// Provides management functionality for KestrunHost instances, including creation, retrieval, starting, stopping, and 
 10/// </summary>
 11public static class KestrunHostManager
 12{
 13    /// <summary>
 14    /// Holds the collection of KestrunHost instances, keyed by their names.
 15    /// </summary>
 116    private static readonly ConcurrentDictionary<string, KestrunHost> _instances = new(StringComparer.OrdinalIgnoreCase)
 17
 18    /// <summary>
 19    /// The name of the default KestrunHost instance, if one has been set.
 20    /// </summary>
 21    private static string? _defaultName;
 22
 23    /// <summary>
 24    /// The root path for Kestrun operations.
 25    /// </summary>
 26    private static string? _kestrunRoot;
 27
 28    /// <summary>
 29    /// Gets the collection of names for all KestrunHost instances.
 30    /// </summary>
 331    public static IReadOnlyCollection<string> InstanceNames => (IReadOnlyCollection<string>)_instances.Keys;
 32
 33    /// <summary>
 34    /// Gets or sets the root path for Kestrun operations.
 35    /// </summary>
 36    public static string? KestrunRoot
 37    {
 438        get => _kestrunRoot;
 39        set
 40        {
 2141            if (string.IsNullOrWhiteSpace(value))
 42            {
 143                throw new ArgumentException("Kestrun root path cannot be null or empty.", nameof(value));
 44            }
 45
 2046            if (Directory.GetCurrentDirectory() != value)
 47            {
 248                Directory.SetCurrentDirectory(value);
 49            }
 2050            _kestrunRoot = value;
 2051        }
 52    }
 53
 54
 55    /// <summary>
 56    /// Creates a new KestrunHost instance using the provided factory function.
 57    /// </summary>
 58    /// <param name="name">The name of the KestrunHost instance to create.</param>
 59    /// <param name="factory">A factory function that returns a new KestrunHost instance.</param>
 60    /// <param name="setAsDefault">Whether to set this instance as the default.</param>
 61    /// <param name="enablePowershellMiddleware">Whether to enable PowerShell middleware for this instance.</param>
 62    /// <returns>The created KestrunHost instance.</returns>
 63    public static KestrunHost Create(string name, Func<KestrunHost> factory, bool setAsDefault = false, bool enablePower
 64    {
 2165        if (string.IsNullOrWhiteSpace(name))
 66        {
 067            throw new ArgumentException("Instance name is required.", nameof(name));
 68        }
 69
 2170        if (_instances.ContainsKey(name))
 71        {
 272            throw new InvalidOperationException($"A KestrunHost instance with the name '{name}' already exists.");
 73        }
 74
 1975        var host = factory();
 1976        host.PowershellMiddlewareEnabled = enablePowershellMiddleware;
 1977        _instances[name] = host;
 78
 1979        if (setAsDefault || _defaultName == null)
 80        {
 1281            _defaultName = name;
 82        }
 83
 1984        return host;
 85    }
 86
 87    /// <summary>
 88    /// Creates a new KestrunHost instance with the specified name and optional module paths, using the default logger.
 89    /// </summary>
 90    /// <param name="name">The name of the KestrunHost instance to create.</param>
 91    /// <param name="modulePathsObj">Optional array of module paths to load.</param>
 92    /// <param name="setAsDefault">Whether to set this instance as the default.</param>
 93    /// <param name="enablePowershellMiddleware">Whether to enable PowerShell middleware for this instance.</param>
 94    /// <returns>The created KestrunHost instance.</returns>
 95    public static KestrunHost Create(string name,
 96         string[]? modulePathsObj = null, bool setAsDefault = false, bool enablePowershellMiddleware = false) =>
 097         Create(name, Log.Logger, modulePathsObj, setAsDefault, enablePowershellMiddleware);
 98
 99    /// <summary>
 100    /// Creates a new KestrunHost instance with the specified name, logger, root path, and optional module paths.
 101    /// </summary>
 102    /// <param name="name">The name of the KestrunHost instance to create.</param>
 103    /// <param name="logger">The Serilog logger to use for the host.</param>
 104    /// <param name="modulePathsObj">Optional array of module paths to load.</param>
 105    /// <param name="setAsDefault">Whether to set this instance as the default.</param>
 106    /// <param name="enablePowershellMiddleware">Whether to enable PowerShell middleware for this instance.</param>
 107    /// <returns>The created KestrunHost instance.</returns>
 108    public static KestrunHost Create(string name, Serilog.ILogger logger,
 109         string[]? modulePathsObj = null, bool setAsDefault = false, bool enablePowershellMiddleware = false)
 110    {
 2111        if (string.IsNullOrWhiteSpace(name))
 112        {
 0113            throw new ArgumentException("Instance name is required.", nameof(name));
 114        }
 115
 2116        if (_instances.ContainsKey(name))
 117        {
 0118            throw new InvalidOperationException($"A KestrunHost instance with the name '{name}' already exists.");
 119        }
 120
 2121        if (KestrunRoot is null)
 122        {
 1123            throw new InvalidOperationException("Kestrun root path must be set before creating a KestrunHost instance.")
 124        }
 125
 1126        var host = new KestrunHost(name, logger, KestrunRoot, modulePathsObj)
 1127        {
 1128            PowershellMiddlewareEnabled = enablePowershellMiddleware,
 1129            DefaultHost = setAsDefault
 1130        };
 131
 1132        _instances[name] = host;
 133
 1134        if (setAsDefault || _defaultName == null)
 135        {
 1136            _defaultName = name;
 137        }
 138
 1139        return host;
 140    }
 141
 142    /// <summary>
 143    /// Determines whether a KestrunHost instance with the specified name exists.
 144    /// </summary>
 145    /// <param name="name">The name of the KestrunHost instance to check for existence.</param>
 146    /// <returns>True if an instance with the specified name exists; otherwise, false.</returns>
 4147    public static bool Contains(string name) => _instances.ContainsKey(name);
 148
 149    /// <summary>
 150    /// Attempts to retrieve a KestrunHost instance by its name.
 151    /// </summary>
 152    /// <param name="name">The name of the KestrunHost instance to retrieve.</param>
 153    /// <param name="host">When this method returns, contains the KestrunHost instance associated with the specified nam
 154    /// <returns>True if the instance was found; otherwise, false.</returns>
 155    public static bool TryGet(string name, out KestrunHost? host) =>
 8156        _instances.TryGetValue(name, out host);
 157
 158    /// <summary>
 159    /// Retrieves a KestrunHost instance by its name.
 160    /// </summary>
 161    /// <param name="name">The name of the KestrunHost instance to retrieve.</param>
 162    /// <returns>The KestrunHost instance if found; otherwise, null.</returns>
 163    public static KestrunHost? Get(string name) =>
 4164        _instances.TryGetValue(name, out var host) ? host : null;
 165
 166    /// <summary>
 167    /// Gets the default KestrunHost instance, if one has been set.
 168    /// </summary>
 7169    public static KestrunHost? Default => _defaultName != null && _instances.TryGetValue(_defaultName, out var host) ? h
 170
 171    /// <summary>
 172    /// Sets the specified KestrunHost instance as the default.
 173    /// </summary>
 174    /// <param name="name">The name of the KestrunHost instance to set as default.</param>
 175    /// <exception cref="InvalidOperationException">Thrown if no instance with the specified name exists.</exception>
 176    public static void SetDefault(string name)
 177    {
 4178        if (!_instances.ContainsKey(name))
 179        {
 2180            throw new InvalidOperationException($"No KestrunHost instance named '{name}' exists.");
 181        }
 182
 2183        _defaultName = name;
 2184    }
 185
 186    /// <summary>
 187    /// Determines whether the specified KestrunHost instance is currently running.
 188    /// </summary>
 189    /// <param name="name">The name of the KestrunHost instance to check.</param>
 190    /// <returns>True if the instance is running; otherwise, false.</returns>
 1191    public static bool IsRunning(string name) => TryGet(name, out var host) && host != null && host.IsRunning;
 192
 193    /// <summary>
 194    /// Starts the specified KestrunHost instance asynchronously.
 195    /// </summary>
 196    /// <param name="name">The name of the KestrunHost instance to start.</param>
 197    /// <param name="ct">A cancellation token to observe while waiting for the task to complete.</param>
 198    public static async Task StartAsync(string name, CancellationToken ct = default)
 199    {
 1200        if (Log.IsEnabled(LogEventLevel.Debug))
 201        {
 1202            Log.Debug("Starting (Async) KestrunHost instance '{Name}'", name);
 203        }
 204
 1205        if (TryGet(name, out var host))
 206        {
 0207            if (host is not null)
 208            {
 0209                await host.StartAsync(ct);
 210            }
 211            else
 212            {
 0213                throw new InvalidOperationException($"KestrunHost instance '{name}' is null.");
 214            }
 215        }
 216        else
 217        {
 1218            throw new InvalidOperationException($"No KestrunHost instance named '{name}' exists.");
 219        }
 0220    }
 221
 222    /// <summary>
 223    /// Stops the specified KestrunHost instance asynchronously.
 224    /// </summary>
 225    /// <param name="name">The name of the KestrunHost instance to stop.</param>
 226    /// <param name="ct">A cancellation token to observe while waiting for the task to complete.</param>
 227    public static async Task StopAsync(string name, CancellationToken ct = default)
 228    {
 2229        if (Log.IsEnabled(LogEventLevel.Debug))
 230        {
 2231            Log.Debug("Stopping (Async) KestrunHost instance '{Name}'", name);
 232        }
 233
 2234        if (TryGet(name, out var host))
 235        {
 1236            if (host is not null)
 237            {
 1238                await host.StopAsync(ct);
 239            }
 240            else
 241            {
 0242                throw new InvalidOperationException($"KestrunHost instance '{name}' is null.");
 243            }
 244        }
 245        else
 246        {
 1247            throw new InvalidOperationException($"No KestrunHost instance named '{name}' exists.");
 248        }
 1249    }
 250
 251    /// <summary>
 252    /// Stops the specified KestrunHost instance synchronously.
 253    /// </summary>
 254    /// <param name="name">The name of the KestrunHost instance to stop.</param>
 255    public static void Stop(string name)
 256    {
 2257        if (_instances.TryGetValue(name, out var host))
 258        {
 0259            host.Stop();
 260        }
 2261    }
 262
 263    /// <summary>
 264    /// Stops all KestrunHost instances asynchronously.
 265    /// </summary>
 266    /// <param name="ct">A cancellation token to observe while waiting for the tasks to complete.</param>
 267    public static async Task StopAllAsync(CancellationToken ct = default)
 268    {
 2269        if (Log.IsEnabled(LogEventLevel.Debug))
 270        {
 2271            Log.Debug("Stopping all KestrunHost instances (Async)");
 272        }
 273
 12274        foreach (var kv in _instances)
 275        {
 4276            await kv.Value.StopAsync(ct);
 277        }
 2278    }
 279
 280    /// <summary>
 281    /// Destroys the specified KestrunHost instance and disposes its resources.
 282    /// </summary>
 283    /// <param name="name">The name of the KestrunHost instance to destroy.</param>
 284    /// <param name="disposeLogger">Whether to dispose the Serilog logger if this was the last instance.</param>
 285    /// <remarks>
 286    /// If this is the last instance, the logger will be disposed to release any resources.
 287    /// </remarks>
 288    public static void Destroy(string name, bool disposeLogger = false)
 289    {
 20290        if (Log.IsEnabled(LogEventLevel.Debug))
 291        {
 20292            Log.Debug("Destroying KestrunHost instance '{Name}'", name);
 293        }
 294
 20295        if (_instances.TryRemove(name, out var host))
 296        {
 20297            host.Dispose();
 20298            if (_defaultName == name)
 299            {
 19300                _defaultName = _instances.Keys.FirstOrDefault();
 301            }
 302
 303
 20304            if (Log.IsEnabled(LogEventLevel.Debug))
 305            {
 20306                Log.Debug("KestrunHost instance '{Name}' destroyed", name);
 307            }
 308        }
 309        else
 310        {
 0311            if (Log.IsEnabled(LogEventLevel.Warning))
 312            {
 0313                Log.Warning("No KestrunHost instance named '{Name}' exists to destroy", name);
 314            }
 315        }
 316
 317        // If no more instances, clear default and close logger
 20318        if (_instances.IsEmpty)
 319        {
 13320            _defaultName = null;
 321            // If no more instances, close logger
 13322            if (disposeLogger) { Log.CloseAndFlush(); }
 323        }
 20324    }
 325
 326    /// <summary>
 327    /// Destroys all KestrunHost instances and disposes their resources.
 328    /// </summary>
 329    public static void DestroyAll()
 330    {
 19331        if (Log.IsEnabled(LogEventLevel.Debug))
 332        {
 19333            Log.Debug("Destroying all KestrunHost instances");
 334        }
 335
 72336        foreach (var name in _instances.Keys.ToList())
 337        {
 17338            Destroy(name);
 339        }
 19340    }
 341}