Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,102 @@ public void DiskSpdWorkloadProfileActionsWillNotBeExecutedIfTheWorkloadPackageDo
}
}

[Test]
[TestCase("PERF-IO-DISKSPD-PHYSICAL-DISK.json")]
public void DiskSpdRawDiskWorkloadProfileParametersAreInlinedCorrectly(string profile)
{
this.mockFixture.Setup(PlatformID.Win32NT);
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
{
WorkloadAssert.ParameterReferencesInlined(executor.Profile);
}
}

[Test]
[TestCase("PERF-IO-DISKSPD-PHYSICAL-DISK.json")]
public async Task DiskSpdRawDiskWorkloadProfileInstallsTheExpectedDependenciesOnWindowsPlatform(string profile)
{
// Raw disk profiles do not require disk formatting — disks are accessed directly at the raw block level.
this.mockFixture.Setup(PlatformID.Win32NT);

using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies, dependenciesOnly: true))
{
await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false);

// Workload dependency package expectations
// The workload dependency package should have been installed at this point.
WorkloadAssert.WorkloadPackageInstalled(this.mockFixture, "diskspd");
}
}

[Test]
[TestCase("PERF-IO-DISKSPD-PHYSICAL-DISK.json")]
public async Task DiskSpdRawDiskWorkloadProfileExecutesTheExpectedWorkloadsOnWindowsPlatform(string profile)
{
IEnumerable<string> expectedCommands = DiskSpdProfileTests.GetDiskSpdRawDiskProfileExpectedCommands();

// Setup the expectations for the workload
// - Workload package is installed and exists.
// - Workload binaries/executables exist on the file system.
// - DiskFilter=DiskIndex:hdd triggers Get-PhysicalDisk auto-discovery; mock returns disks 6 and 7.
// - The workload generates valid results.
this.mockFixture.Setup(PlatformID.Win32NT);
this.mockFixture.SetupPackage("diskspd", expectedFiles: $@"win-x64\diskspd.exe");

this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) =>
{
IProcessProxy process = this.mockFixture.CreateProcess(command, arguments, workingDir);
if (arguments.Contains("Get-PhysicalDisk", StringComparison.OrdinalIgnoreCase))
{
// Simulate auto-discovery of 2 HDD raw disks (indices 6 and 7)
process.StandardOutput.Append("6\r\n7");
}
else if (arguments.Contains("diskspd", StringComparison.OrdinalIgnoreCase))
{
process.StandardOutput.Append(TestDependencies.GetResourceFileContents("Results_DiskSpd.txt"));
}

return process;
};

using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
{
await executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None).ConfigureAwait(false);

WorkloadAssert.CommandsExecuted(this.mockFixture, expectedCommands.ToArray());
}
}

[Test]
[TestCase("PERF-IO-DISKSPD-PHYSICAL-DISK.json")]
public void DiskSpdRawDiskWorkloadProfileActionsWillNotBeExecutedIfWorkloadPackageDoesNotExist(string profile)
{
this.mockFixture.Setup(PlatformID.Win32NT);

// We ensure the workload package does not exist.
this.mockFixture.PackageManager.Clear();

using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
{
executor.ExecuteDependencies = false;

DependencyException error = Assert.ThrowsAsync<DependencyException>(() => executor.ExecuteAsync(ProfileTiming.OneIteration(), CancellationToken.None));
Assert.AreEqual(ErrorReason.WorkloadDependencyMissing, error.Reason);
Assert.IsFalse(this.mockFixture.ProcessManager.Commands.Contains("diskspd.exe"));
}
}

private static IEnumerable<string> GetDiskSpdRawDiskProfileExpectedCommands()
{
return new List<string>
{
// ProcessModel=SingleProcessPerDisk: one diskspd process per auto-discovered raw disk.
// Duration=00:01:00 -> 60 seconds; disks #6 and #7 discovered via Get-PhysicalDisk.
@"-b128K -d60 -o32 -t1 -r -w0 -Sh -L -Rtext #6",
@"-b128K -d60 -o32 -t1 -r -w0 -Sh -L -Rtext #7"
};
}

private static IEnumerable<string> GetDiskSpdStressProfileExpectedCommands()
{
return new List<string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace VirtualClient.Actions
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -39,13 +40,44 @@ static TestDependencies()
/// <summary>
/// Creates a <see cref="ProfileExecutor"/> for the workload profile provided (e.g. PERF-IO-FIO-STRESS.json).
/// </summary>
public static ProfileExecutor CreateProfileExecutor(string profile, IServiceCollection dependencies, bool dependenciesOnly = false)
/// <param name="profile">The name of the workload profile file (e.g. PERF-IO-DISKSPD.json).</param>
/// <param name="dependencies">Service dependencies required by the profile executor.</param>
/// <param name="dependenciesOnly">True to execute only profile dependencies, not actions.</param>
/// <param name="parameterOverrides">
/// Optional parameter overrides applied to every non-disk-fill action in the profile after inlining
/// (e.g. <c>new Dictionary&lt;string, IConvertible&gt; {{ "DiskFilter", "DiskIndex:6,7" }}</c>).
/// Useful for testing scenarios that would normally be driven by CLI <c>--parameters</c>.
/// </param>
public static ProfileExecutor CreateProfileExecutor(
string profile,
IServiceCollection dependencies,
bool dependenciesOnly = false,
IDictionary<string, IConvertible> parameterOverrides = null)
{
ExecutionProfile workloadProfile = ExecutionProfile.ReadProfileAsync(Path.Combine(TestDependencies.ProfileDirectory, profile))
.GetAwaiter().GetResult();

workloadProfile.Inline();

if (parameterOverrides?.Count > 0)
{
foreach (ExecutionProfileElement action in workloadProfile.Actions)
{
// Do not apply overrides to disk-fill actions — DiskFill is incompatible with
// DiskIndex: targeting and would fail validation if both are set.
bool isDiskFill = action.Parameters.TryGetValue("DiskFill", out IConvertible df)
&& bool.TryParse(df?.ToString(), out bool dfValue) && dfValue;

if (!isDiskFill)
{
foreach (KeyValuePair<string, IConvertible> kvp in parameterOverrides)
{
action.Parameters[kvp.Key] = kvp.Value;
}
}
}
}

ComponentSettings settings = new ComponentSettings
{
ExitWait = TimeSpan.Zero
Expand Down
Loading