< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Scheduling.JobFactory
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Scheduling/JobFactory.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
86%
Covered lines: 82
Uncovered lines: 13
Coverable lines: 95
Total lines: 139
Line coverage: 86.3%
Branch coverage
50%
Covered branches: 6
Total branches: 12
Branch coverage: 50%
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
.ctor(...)100%11100%
get_Language()100%11100%
get_Code()100%11100%
get_Log()100%11100%
get_Pool()100%11100%
get_ExtraImports()100%210%
get_ExtraRefs()100%210%
get_Locals()100%210%
get_LanguageVersion()100%210%
Create(...)37.5%10870%
CreateAsync()75%4487.5%
Create(...)100%11100%
PowerShellJob(...)100%1193.84%
RoslynJob(...)100%210%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Scheduling/JobFactory.cs

#LineLine coverage
 1using System.Management.Automation;
 2using System.Reflection;
 3using Kestrun.Scripting;
 4using Kestrun.Utilities;
 5using Microsoft.CodeAnalysis.CSharp;
 6using Serilog.Events;
 7
 8namespace Kestrun.Scheduling;
 9
 10internal static class JobFactory
 11{
 1212    internal record JobConfig(
 1713          ScriptLanguage Language,
 4314          string Code,
 9115          Serilog.ILogger Log,
 3916           KestrunRunspacePoolManager? Pool = null,
 017          string[]? ExtraImports = null,
 018          Assembly[]? ExtraRefs = null,
 019            IReadOnlyDictionary<string, object?>? Locals = null,
 020          LanguageVersion LanguageVersion = LanguageVersion.CSharp12
 1221          );
 22
 23    internal static Func<CancellationToken, Task> Create(JobConfig config)
 24    {
 1225        return config.Language switch
 1226        {
 1227            ScriptLanguage.PowerShell =>
 1228                config.Pool is null
 1229                    ? throw new InvalidOperationException("PowerShell runspace pool must be provided for PowerShell jobs
 1230                    : PowerShellJob(config),
 031            ScriptLanguage.CSharp => RoslynJob(config),
 032            ScriptLanguage.VBNet => RoslynJob(config),
 033            _ => throw new NotSupportedException($"Language {config.Language} not supported.")
 1234        };
 35    }
 36
 37    public static async Task<Func<CancellationToken, Task>> CreateAsync(
 38     JobConfig config, FileInfo fileInfo, CancellationToken ct = default)
 39    {
 540        ArgumentNullException.ThrowIfNull(fileInfo);
 541        if (!fileInfo.Exists)
 42        {
 043            throw new FileNotFoundException(fileInfo.FullName);
 44        }
 45
 546        var updatedConfig = config with { Code = await File.ReadAllTextAsync(fileInfo.FullName, ct) };
 547        if (updatedConfig.Log.IsEnabled(LogEventLevel.Debug))
 48        {
 549            updatedConfig.Log.Debug("Creating job for {File} with language {Lang}", fileInfo.FullName, updatedConfig.Lan
 50        }
 51
 552        return Create(updatedConfig);
 553    }
 54
 55    public static Func<CancellationToken, Task> Create(
 256        JobConfig config, FileInfo fileInfo) => CreateAsync(config, fileInfo).GetAwaiter().GetResult();
 57
 58
 59    /* ----------------  PowerShell  ---------------- */
 60    private static Func<CancellationToken, Task> PowerShellJob(
 61       JobConfig config)
 62    {
 1163        return async ct =>
 1164        {
 965            if (config.Log.IsEnabled(LogEventLevel.Debug))
 1166            {
 967                config.Log.Debug("Building PowerShell delegate, script length={Length}", config.Code?.Length);
 1168            }
 1169
 970            if (config.Pool is null)
 1171            {
 072                throw new InvalidOperationException("PowerShell runspace pool must be provided for PowerShell jobs.");
 1173            }
 1174
 975            var runspace = config.Pool.Acquire();
 1176            try
 1177            {
 978                using var ps = PowerShell.Create();
 979                ps.Runspace = runspace;
 980                _ = ps.AddScript(config.Code);
 981                if (config.Log.IsEnabled(LogEventLevel.Debug))
 1182                {
 983                    config.Log.Debug("Executing PowerShell script with {RunspaceId} - {Preview}", runspace.Id, config.Co
 1184                }
 1185
 1186                // Register cancellation
 987                using var reg = ct.Register(() => ps.Stop());
 1188
 1189                // Wait for the PowerShell script to complete
 990                var psResults = await ps.InvokeAsync().WaitAsync(ct).ConfigureAwait(false);
 1191
 892                config.Log.Verbose($"PowerShell script executed with {psResults.Count} results.");
 893                if (config.Log.IsEnabled(LogEventLevel.Debug))
 1194                {
 895                    config.Log.Debug("PowerShell script output:");
 2096                    foreach (var r in psResults.Take(10))      // first 10 only
 1197                    {
 298                        config.Log.Debug("   • {Result}", r);
 1199                    }
 11100
 8101                    if (psResults.Count > 10)
 11102                    {
 0103                        config.Log.Debug("   … {Count} more", psResults.Count - 10);
 11104                    }
 11105                }
 11106
 8107                if (ps.HadErrors || ps.Streams.Error.Count != 0 || ps.Streams.Verbose.Count > 0 || ps.Streams.Debug.Coun
 11108                {
 0109                    config.Log.Verbose("PowerShell script completed with verbose/debug/warning/info messages.");
 0110                    config.Log.Verbose(BuildError.Text(ps));
 11111                }
 8112            }
 1113            catch (Exception ex)
 11114            {
 1115                config.Log.Error(ex, "PowerShell job failed - {Preview}", config.Code?[..Math.Min(40, config.Code.Length
 1116                throw;
 11117            }
 11118            finally
 11119            {
 9120                if (config.Log.IsEnabled(LogEventLevel.Debug))
 11121                {
 9122                    config.Log.Debug("PowerShell job completed, releasing runspace back to pool.");
 11123                }
 11124                // Ensure we release the runspace back to the pool
 9125                config.Pool.Release(runspace);
 11126            }
 19127        };
 128    }
 129
 130    /// <summary>
 131    /// Creates a C# or VB.NET job using Roslyn compilation.
 132    /// </summary>
 133    /// <param name="config">The job configuration containing code, logger, and other parameters.</param>
 134    /// <returns>A function that executes the job.</returns>
 135    /// <remarks>
 136    /// This method uses Roslyn to compile and execute C# or VB.NET code.
 137    /// </remarks>
 0138    private static Func<CancellationToken, Task> RoslynJob(JobConfig config) => RoslynJobFactory.Build(config.Code, conf
 139}