< 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@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
85%
Covered lines: 70
Uncovered lines: 12
Coverable lines: 82
Total lines: 321
Line coverage: 85.3%
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

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%8888.88%
Create(...)100%210%
Create(...)70%111081.81%
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{
 113    private static readonly ConcurrentDictionary<string, KestrunHost> _instances = new(StringComparer.OrdinalIgnoreCase)
 14    private static string? _defaultName;
 15
 16    /// <summary>
 17    /// Gets the collection of names for all KestrunHost instances.
 18    /// </summary>
 319    public static IReadOnlyCollection<string> InstanceNames => (IReadOnlyCollection<string>)_instances.Keys;
 20
 21    private static string? _kestrunRoot;
 22    /// <summary>
 23    /// Gets or sets the root path for Kestrun operations.
 24    /// </summary>
 25    public static string? KestrunRoot
 26    {
 427        get => _kestrunRoot;
 28        set
 29        {
 2130            if (string.IsNullOrWhiteSpace(value))
 31            {
 132                throw new ArgumentException("Kestrun root path cannot be null or empty.", nameof(value));
 33            }
 34
 2035            if (Directory.GetCurrentDirectory() != value)
 36            {
 237                Directory.SetCurrentDirectory(value);
 38            }
 2039            _kestrunRoot = value;
 2040        }
 41    }
 42
 43
 44    /// <summary>
 45    /// Creates a new KestrunHost instance using the provided factory function.
 46    /// </summary>
 47    /// <param name="name">The name of the KestrunHost instance to create.</param>
 48    /// <param name="factory">A factory function that returns a new KestrunHost instance.</param>
 49    /// <param name="setAsDefault">Whether to set this instance as the default.</param>
 50    /// <returns>The created KestrunHost instance.</returns>
 51    public static KestrunHost Create(string name, Func<KestrunHost> factory, bool setAsDefault = false)
 52    {
 2153        if (string.IsNullOrWhiteSpace(name))
 54        {
 055            throw new ArgumentException("Instance name is required.", nameof(name));
 56        }
 57
 2158        if (_instances.ContainsKey(name))
 59        {
 260            throw new InvalidOperationException($"A KestrunHost instance with the name '{name}' already exists.");
 61        }
 62
 1963        var host = factory();
 1964        _instances[name] = host;
 65
 1966        if (setAsDefault || _defaultName == null)
 67        {
 1268            _defaultName = name;
 69        }
 70
 1971        return host;
 72    }
 73    /// <summary>
 74    /// Creates a new KestrunHost instance with the specified name and optional module paths, using the default logger.
 75    /// </summary>
 76    /// <param name="name">The name of the KestrunHost instance to create.</param>
 77    /// <param name="modulePathsObj">Optional array of module paths to load.</param>
 78    /// <param name="setAsDefault">Whether to set this instance as the default.</param>
 79    /// <returns>The created KestrunHost instance.</returns>
 80    public static KestrunHost Create(string name,
 81         string[]? modulePathsObj = null, bool setAsDefault = false) =>
 82        // Call the overload with a default logger (null or a default instance as appropriate)
 083        Create(name, Log.Logger, modulePathsObj, setAsDefault);
 84
 85    /// <summary>
 86    /// Creates a new KestrunHost instance with the specified name, logger, root path, and optional module paths.
 87    /// </summary>
 88    /// <param name="name">The name of the KestrunHost instance to create.</param>
 89    /// <param name="logger">The Serilog logger to use for the host.</param>
 90    /// <param name="modulePathsObj">Optional array of module paths to load.</param>
 91    /// <param name="setAsDefault">Whether to set this instance as the default.</param>
 92    /// <returns>The created KestrunHost instance.</returns>
 93    public static KestrunHost Create(string name, Serilog.ILogger logger,
 94         string[]? modulePathsObj = null, bool setAsDefault = false)
 95    {
 296        if (string.IsNullOrWhiteSpace(name))
 97        {
 098            throw new ArgumentException("Instance name is required.", nameof(name));
 99        }
 100
 2101        if (_instances.ContainsKey(name))
 102        {
 0103            throw new InvalidOperationException($"A KestrunHost instance with the name '{name}' already exists.");
 104        }
 105
 2106        if (KestrunRoot is null)
 107        {
 1108            throw new InvalidOperationException("Kestrun root path must be set before creating a KestrunHost instance.")
 109        }
 110
 1111        var host = new KestrunHost(name, logger, KestrunRoot, modulePathsObj);
 1112        _instances[name] = host;
 113
 1114        if (setAsDefault || _defaultName == null)
 115        {
 1116            _defaultName = name;
 117        }
 118
 1119        return host;
 120    }
 121
 122    /// <summary>
 123    /// Determines whether a KestrunHost instance with the specified name exists.
 124    /// </summary>
 125    /// <param name="name">The name of the KestrunHost instance to check for existence.</param>
 126    /// <returns>True if an instance with the specified name exists; otherwise, false.</returns>
 4127    public static bool Contains(string name) => _instances.ContainsKey(name);
 128
 129    /// <summary>
 130    /// Attempts to retrieve a KestrunHost instance by its name.
 131    /// </summary>
 132    /// <param name="name">The name of the KestrunHost instance to retrieve.</param>
 133    /// <param name="host">When this method returns, contains the KestrunHost instance associated with the specified nam
 134    /// <returns>True if the instance was found; otherwise, false.</returns>
 135    public static bool TryGet(string name, out KestrunHost? host) =>
 8136        _instances.TryGetValue(name, out host);
 137
 138    /// <summary>
 139    /// Retrieves a KestrunHost instance by its name.
 140    /// </summary>
 141    /// <param name="name">The name of the KestrunHost instance to retrieve.</param>
 142    /// <returns>The KestrunHost instance if found; otherwise, null.</returns>
 143    public static KestrunHost? Get(string name) =>
 4144        _instances.TryGetValue(name, out var host) ? host : null;
 145
 146    /// <summary>
 147    /// Gets the default KestrunHost instance, if one has been set.
 148    /// </summary>
 7149    public static KestrunHost? Default => _defaultName != null && _instances.TryGetValue(_defaultName, out var host) ? h
 150
 151    /// <summary>
 152    /// Sets the specified KestrunHost instance as the default.
 153    /// </summary>
 154    /// <param name="name">The name of the KestrunHost instance to set as default.</param>
 155    /// <exception cref="InvalidOperationException">Thrown if no instance with the specified name exists.</exception>
 156    public static void SetDefault(string name)
 157    {
 4158        if (!_instances.ContainsKey(name))
 159        {
 2160            throw new InvalidOperationException($"No KestrunHost instance named '{name}' exists.");
 161        }
 162
 2163        _defaultName = name;
 2164    }
 165
 166    /// <summary>
 167    /// Determines whether the specified KestrunHost instance is currently running.
 168    /// </summary>
 169    /// <param name="name">The name of the KestrunHost instance to check.</param>
 170    /// <returns>True if the instance is running; otherwise, false.</returns>
 1171    public static bool IsRunning(string name) => TryGet(name, out var host) && host != null && host.IsRunning;
 172
 173    /// <summary>
 174    /// Starts the specified KestrunHost instance asynchronously.
 175    /// </summary>
 176    /// <param name="name">The name of the KestrunHost instance to start.</param>
 177    /// <param name="ct">A cancellation token to observe while waiting for the task to complete.</param>
 178    public static async Task StartAsync(string name, CancellationToken ct = default)
 179    {
 1180        if (Log.IsEnabled(LogEventLevel.Debug))
 181        {
 1182            Log.Debug("Starting (Async) KestrunHost instance '{Name}'", name);
 183        }
 184
 1185        if (TryGet(name, out var host))
 186        {
 0187            if (host is not null)
 188            {
 0189                await host.StartAsync(ct);
 190            }
 191            else
 192            {
 0193                throw new InvalidOperationException($"KestrunHost instance '{name}' is null.");
 194            }
 195        }
 196        else
 197        {
 1198            throw new InvalidOperationException($"No KestrunHost instance named '{name}' exists.");
 199        }
 0200    }
 201
 202    /// <summary>
 203    /// Stops the specified KestrunHost instance asynchronously.
 204    /// </summary>
 205    /// <param name="name">The name of the KestrunHost instance to stop.</param>
 206    /// <param name="ct">A cancellation token to observe while waiting for the task to complete.</param>
 207    public static async Task StopAsync(string name, CancellationToken ct = default)
 208    {
 2209        if (Log.IsEnabled(LogEventLevel.Debug))
 210        {
 2211            Log.Debug("Stopping (Async) KestrunHost instance '{Name}'", name);
 212        }
 213
 2214        if (TryGet(name, out var host))
 215        {
 1216            if (host is not null)
 217            {
 1218                await host.StopAsync(ct);
 219            }
 220            else
 221            {
 0222                throw new InvalidOperationException($"KestrunHost instance '{name}' is null.");
 223            }
 224        }
 225        else
 226        {
 1227            throw new InvalidOperationException($"No KestrunHost instance named '{name}' exists.");
 228        }
 1229    }
 230
 231    /// <summary>
 232    /// Stops the specified KestrunHost instance synchronously.
 233    /// </summary>
 234    /// <param name="name">The name of the KestrunHost instance to stop.</param>
 235    public static void Stop(string name)
 236    {
 2237        if (_instances.TryGetValue(name, out var host))
 238        {
 0239            host.Stop();
 240        }
 2241    }
 242
 243    /// <summary>
 244    /// Stops all KestrunHost instances asynchronously.
 245    /// </summary>
 246    /// <param name="ct">A cancellation token to observe while waiting for the tasks to complete.</param>
 247    public static async Task StopAllAsync(CancellationToken ct = default)
 248    {
 2249        if (Log.IsEnabled(LogEventLevel.Debug))
 250        {
 2251            Log.Debug("Stopping all KestrunHost instances (Async)");
 252        }
 253
 12254        foreach (var kv in _instances)
 255        {
 4256            await kv.Value.StopAsync(ct);
 257        }
 2258    }
 259
 260    /// <summary>
 261    /// Destroys the specified KestrunHost instance and disposes its resources.
 262    /// </summary>
 263    /// <param name="name">The name of the KestrunHost instance to destroy.</param>
 264    /// <param name="disposeLogger">Whether to dispose the Serilog logger if this was the last instance.</param>
 265    /// <remarks>
 266    /// If this is the last instance, the logger will be disposed to release any resources.
 267    /// </remarks>
 268    public static void Destroy(string name, bool disposeLogger = false)
 269    {
 20270        if (Log.IsEnabled(LogEventLevel.Debug))
 271        {
 20272            Log.Debug("Destroying KestrunHost instance '{Name}'", name);
 273        }
 274
 20275        if (_instances.TryRemove(name, out var host))
 276        {
 20277            host.Dispose();
 20278            if (_defaultName == name)
 279            {
 19280                _defaultName = _instances.Keys.FirstOrDefault();
 281            }
 282
 283
 20284            if (Log.IsEnabled(LogEventLevel.Debug))
 285            {
 20286                Log.Debug("KestrunHost instance '{Name}' destroyed", name);
 287            }
 288        }
 289        else
 290        {
 0291            if (Log.IsEnabled(LogEventLevel.Warning))
 292            {
 0293                Log.Warning("No KestrunHost instance named '{Name}' exists to destroy", name);
 294            }
 295        }
 296
 297        // If no more instances, clear default and close logger
 20298        if (_instances.IsEmpty)
 299        {
 13300            _defaultName = null;
 301            // If no more instances, close logger
 13302            if (disposeLogger) { Log.CloseAndFlush(); }
 303        }
 20304    }
 305
 306    /// <summary>
 307    /// Destroys all KestrunHost instances and disposes their resources.
 308    /// </summary>
 309    public static void DestroyAll()
 310    {
 19311        if (Log.IsEnabled(LogEventLevel.Debug))
 312        {
 19313            Log.Debug("Destroying all KestrunHost instances");
 314        }
 315
 72316        foreach (var name in _instances.Keys.ToList())
 317        {
 17318            Destroy(name);
 319        }
 19320    }
 321}