Skip to content

Commit

Permalink
Add option to do an extra run on IService demand (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyredondo authored Feb 19, 2024
1 parent 12b910d commit a76cefc
Show file tree
Hide file tree
Showing 14 changed files with 570 additions and 352 deletions.
628 changes: 320 additions & 308 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>0.1.16</Version>
<Version>0.1.17</Version>
<Authors>Tony Redondo, Grégory Léocadie</Authors>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
5 changes: 5 additions & 0 deletions src/TimeItSharp.Common/Configuration/AssemblyLoadInfo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace TimeItSharp.Common.Configuration;
Expand All @@ -13,6 +14,9 @@ public class AssemblyLoadInfo
[JsonPropertyName("name")]
public string? Name { get; set; }

[JsonPropertyName("options")]
public Dictionary<string, JsonElement?>? Options { get; set; }

[JsonIgnore]
public Type? InMemoryType { get; set; }

Expand All @@ -21,6 +25,7 @@ public class AssemblyLoadInfo
FilePath = FilePath,
Type = Type,
Name = Name,
Options = Options,
InMemoryType = InMemoryType,
};
}
5 changes: 5 additions & 0 deletions src/TimeItSharp.Common/Configuration/Scenario.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
using System.Text.Json.Serialization;
using TimeItSharp.Common.Services;

namespace TimeItSharp.Common.Configuration;

public class Scenario : ProcessData
{
[JsonPropertyName("name")]
public string Name { get; set; }

public IService? ParentService { get; set; }

public Scenario()
{
Expand All @@ -15,6 +18,7 @@ public Scenario()
public Scenario(string name, ProcessData? processData = null)
{
Name = name;
ParentService = null;
if (processData is not null)
{
ProcessName = processData.ProcessName;
Expand All @@ -30,6 +34,7 @@ public Scenario(string name, ProcessData? processData = null)
internal override Scenario Clone() => new Scenario
{
Name = Name,
ParentService = ParentService,
ProcessName = ProcessName,
ProcessArguments = ProcessArguments,
WorkingDirectory = WorkingDirectory,
Expand Down
2 changes: 1 addition & 1 deletion src/TimeItSharp.Common/InitOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

namespace TimeItSharp.Common;

public record struct InitOptions(Config Configuration, TemplateVariables TemplateVariables, object? State);
public record struct InitOptions(Config Configuration, AssemblyLoadInfo? LoadInfo, TemplateVariables TemplateVariables, object? State);
22 changes: 21 additions & 1 deletion src/TimeItSharp.Common/ScenarioProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ public void CleanScenario(Scenario scenario)

public async Task<ScenarioResult?> ProcessScenarioAsync(int index, Scenario scenario, CancellationToken cancellationToken)
{
_callbacksTriggers.ScenarioStart(scenario);
var scenarioStartArgs = new TimeItCallbacks.ScenarioStartArg(scenario);
_callbacksTriggers.ScenarioStart(scenarioStartArgs);
Stopwatch? watch = null;
AnsiConsole.MarkupLine("[dodgerblue1]Scenario:[/] {0}", scenario.Name);

Expand Down Expand Up @@ -233,6 +234,25 @@ await RunScenarioAsync(_configuration.WarmUpCount, index, scenario, TimeItPhase.

watch.Stop();
AnsiConsole.MarkupLine(" Duration: {0}s", Math.Round(watch.Elapsed.TotalSeconds, 3));

foreach (var repeat in scenarioStartArgs.GetRepeats())
{
AnsiConsole.Markup(" [green3]Run for '{0}'[/]", repeat.ServiceAskingForRepeat.Name);
scenario.ParentService = repeat.ServiceAskingForRepeat;
watch.Restart();
await RunScenarioAsync(repeat.Count, index, scenario, TimeItPhase.ExtraRun, false,
cancellationToken: cancellationToken).ConfigureAwait(false);
watch.Stop();
if (cancellationToken.IsCancellationRequested)
{
return null;
}

AnsiConsole.MarkupLine(" Duration: {0}s", Math.Round(watch.Elapsed.TotalSeconds, 3));
}

scenario.ParentService = null;

AnsiConsole.WriteLine();

var lastStandardOutput = string.Empty;
Expand Down
52 changes: 52 additions & 0 deletions src/TimeItSharp.Common/Services/DatadogProfilerConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Text.Json;

namespace TimeItSharp.Common.Services;

public sealed class DatadogProfilerConfiguration
{
internal Dictionary<string, bool>? EnabledScenarios { get; private set; }
internal bool UseExtraRun { get; private set; }
internal int ExtraRunCount { get; private set; }

public DatadogProfilerConfiguration(Dictionary<string, JsonElement?>? options = null)
{
if (options is not null)
{
if (options.TryGetValue("useExtraRun", out var useExtraRunJsonElement) &&
useExtraRunJsonElement is not null)
{
UseExtraRun = useExtraRunJsonElement.Value.GetBoolean();
}

if (options.TryGetValue("extraRunCount", out var extraRunCountJsonElement) &&
extraRunCountJsonElement is not null)
{
ExtraRunCount = extraRunCountJsonElement.Value.GetInt32();
}

if (options.TryGetValue("scenarios", out var scenariosJsonElement) &&
scenariosJsonElement is not null)
{
EnabledScenarios = new Dictionary<string, bool>();
foreach (var scenarioItem in scenariosJsonElement.Value.EnumerateArray())
{
EnabledScenarios[scenarioItem.GetString() ?? string.Empty] = true;
}
}
}
}

public DatadogProfilerConfiguration WithScenario(string scenario, bool enabled)
{
EnabledScenarios ??= new();
EnabledScenarios[scenario] = enabled;
return this;
}

public DatadogProfilerConfiguration WithExtraRun(int count = 0)
{
UseExtraRun = true;
ExtraRunCount = count;
return this;
}
}
71 changes: 60 additions & 11 deletions src/TimeItSharp.Common/Services/DatadogProfilerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using DatadogTestLogger.Vendors.Datadog.Trace.Configuration;
using DatadogTestLogger.Vendors.Datadog.Trace.Util;
using Spectre.Console;
using TimeItSharp.Common.Configuration;
using TimeItSharp.Common.Results;

namespace TimeItSharp.Common.Services;
Expand All @@ -13,12 +14,25 @@ public sealed class DatadogProfilerService : IService
{
private bool _isEnabled;
private IReadOnlyDictionary<string, string?>? _profilerEnvironmentVariables = null;

public string Name => nameof(DatadogProfilerService);
private DatadogProfilerConfiguration? _profilerConfiguration = null;
private Config? _configuration = null;

public string Name => "DatadogProfiler";

public void Initialize(InitOptions options, TimeItCallbacks callbacks)
{
if (options.State is DatadogProfilerConfiguration profilerConfiguration)
{
_profilerConfiguration = profilerConfiguration;
}
else
{
_profilerConfiguration = new(options.LoadInfo?.Options);
}

_configuration = options.Configuration;
_profilerEnvironmentVariables = GetProfilerEnvironmentVariables();
callbacks.OnScenarioStart += CallbacksOnOnScenarioStart;
callbacks.OnExecutionStart += CallbacksOnOnExecutionStart;
callbacks.OnFinish += CallbacksOnOnFinish;
}
Expand All @@ -30,21 +44,56 @@ private void CallbacksOnOnFinish()
: "[red]The Datadog profiler could not be attached to the .NET processes.[/]");
}

private void CallbacksOnOnExecutionStart(DataPoint datapoint, TimeItPhase phase, ref Command command)
private void CallbacksOnOnScenarioStart(TimeItCallbacks.ScenarioStartArg scenario)
{
if (_profilerEnvironmentVariables is { } profilerEnvironmentVariables && datapoint.Scenario is { } scenario)
if (_profilerEnvironmentVariables is not null && _profilerConfiguration?.UseExtraRun == true)
{
var envVar = new Dictionary<string, string?>(profilerEnvironmentVariables);
foreach (var kvp in command.EnvironmentVariables)
var enabledScenarios = _profilerConfiguration.EnabledScenarios;
if (enabledScenarios is null ||
(enabledScenarios.TryGetValue(scenario.Scenario.Name, out var isEnabled) && isEnabled))
{
envVar[kvp.Key] = kvp.Value;
var count = _profilerConfiguration.ExtraRunCount;
if (count < 1)
{
count = Math.Max((_configuration?.Count ?? 1) / 10, 1);
}

scenario.RepeatScenarioForService(this, count);
}
}
}

private void CallbacksOnOnExecutionStart(DataPoint datapoint, TimeItPhase phase, ref Command command)
{
if (_profilerEnvironmentVariables is { } profilerEnvironmentVariables &&
datapoint.Scenario is { } scenario)
{
var enabledScenarios = _profilerConfiguration?.EnabledScenarios;
if (enabledScenarios is null ||
(enabledScenarios.TryGetValue(scenario.Name, out var isEnabled) && isEnabled))
{
var runProfiler = _profilerConfiguration?.UseExtraRun == true &&
phase == TimeItPhase.ExtraRun;

DatadogMetadata.GetIds(scenario, out var traceId, out var spanId);
envVar["DD_INTERNAL_CIVISIBILITY_SPANID"] = spanId.ToString();
runProfiler = runProfiler ||
(_profilerConfiguration?.UseExtraRun != true &&
phase == TimeItPhase.Run);

command = command.WithEnvironmentVariables(envVar);
_isEnabled = true;
if (runProfiler)
{
var envVar = new Dictionary<string, string?>(profilerEnvironmentVariables);
foreach (var kvp in command.EnvironmentVariables)
{
envVar[kvp.Key] = kvp.Value;
}

DatadogMetadata.GetIds(scenario, out var traceId, out var spanId);
envVar["DD_INTERNAL_CIVISIBILITY_SPANID"] = spanId.ToString();

command = command.WithEnvironmentVariables(envVar);
_isEnabled = true;
}
}
}
}

Expand Down
67 changes: 62 additions & 5 deletions src/TimeItSharp.Common/Services/TimeItCallbacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public sealed class TimeItCallbacks
{
public delegate void BeforeAllScenariosStartsDelegate(IReadOnlyList<Scenario> scenarios);

public delegate void OnScenarioStartDelegate(Scenario scenario);
public delegate void OnScenarioStartDelegate(ScenarioStartArg scenario);

public delegate void OnExecutionStartDelegate(DataPoint dataPoint, TimeItPhase phase, ref Command command);

Expand Down Expand Up @@ -42,14 +42,50 @@ internal CallbacksTriggers(TimeItCallbacks callbacks)
public void BeforeAllScenariosStarts(IReadOnlyList<Scenario> scenarios)
=> _callbacks.BeforeAllScenariosStarts?.Invoke(scenarios);

public void ScenarioStart(Scenario scenario)
=> _callbacks.OnScenarioStart?.Invoke(scenario);
public void ScenarioStart(ScenarioStartArg scenarioStartArg)
=> _callbacks.OnScenarioStart?.Invoke(scenarioStartArg);

public void ExecutionStart(DataPoint dataPoint, TimeItPhase phase, ref Command command)
=> _callbacks.OnExecutionStart?.Invoke(dataPoint, phase, ref command);
{
if (dataPoint.Scenario?.ParentService is { } parentService)
{
if (_callbacks.OnExecutionStart is { } onExecutionStartEvent)
{
foreach (var @delegate in onExecutionStartEvent.GetInvocationList())
{
if (@delegate.Target == parentService)
{
((OnExecutionStartDelegate)@delegate).Invoke(dataPoint, phase, ref command);
}
}
}
}
else
{
_callbacks.OnExecutionStart?.Invoke(dataPoint, phase, ref command);
}
}

public void ExecutionEnd(DataPoint dataPoint, TimeItPhase phase)
=> _callbacks.OnExecutionEnd?.Invoke(dataPoint, phase);
{
if (dataPoint.Scenario?.ParentService is { } parentService)
{
if (_callbacks.OnExecutionEnd is { } onExecutionEndEvent)
{
foreach (var @delegate in onExecutionEndEvent.GetInvocationList())
{
if (@delegate.Target == parentService)
{
((OnExecutionEndDelegate)@delegate).Invoke(dataPoint, phase);
}
}
}
}
else
{
_callbacks.OnExecutionEnd?.Invoke(dataPoint, phase);
}
}

public void ScenarioFinish(ScenarioResult scenarioResults)
=> _callbacks.OnScenarioFinish?.Invoke(scenarioResults);
Expand All @@ -60,4 +96,25 @@ public void AfterAllScenariosFinishes(IReadOnlyList<ScenarioResult> scenariosRes
public void Finish()
=> _callbacks.OnFinish?.Invoke();
}

public sealed class ScenarioStartArg
{
private readonly List<(IService ServiceAskingForRepeat, int Count)> _repeats;

public Scenario Scenario { get; private set; }

internal ScenarioStartArg(Scenario scenario)
{
_repeats = new();
Scenario = scenario;
}

public void RepeatScenarioForService(IService serviceAskingForRepeat, int count)
{
_repeats.Add((serviceAskingForRepeat, count));
}

internal IEnumerable<(IService ServiceAskingForRepeat, int Count)> GetRepeats()
=> _repeats;
}
}
Loading

0 comments on commit a76cefc

Please sign in to comment.