Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace VirtualClient.Actions
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using VirtualClient.Common;
using VirtualClient.Contracts;

[TestFixture]
[Category("Functional")]
public class ExampleWorkloadWithAffinityProfileTests
{
private DependencyFixture mockFixture;

[OneTimeSetUp]
public void SetupFixture()
{
this.mockFixture = new DependencyFixture();
ComponentTypeCache.Instance.LoadComponentTypes(TestDependencies.TestDirectory);
}

[Test]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix, Architecture.X64)]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix, Architecture.Arm64)]
public void ExampleWorkloadProfileParametersAreInlinedCorrectly_Linux(string profile, PlatformID platform, Architecture architecture)
{
this.mockFixture.Setup(platform, architecture);
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
{
WorkloadAssert.ParameterReferencesInlined(executor.Profile);
}
}

[Test]
[Platform(Exclude = "Unix,Linux,MacOsX")]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT, Architecture.X64)]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT, Architecture.Arm64)]
public void ExampleWorkloadProfileParametersAreInlinedCorrectly_Windows(string profile, PlatformID platform, Architecture architecture)
{
this.mockFixture.Setup(platform, architecture);
using (ProfileExecutor executor = TestDependencies.CreateProfileExecutor(profile, this.mockFixture.Dependencies))
{
WorkloadAssert.ParameterReferencesInlined(executor.Profile);
}
}

[Test]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix)]
public async Task ExampleWorkloadProfileInstallsTheExpectedDependenciesOnLinuxPlatform(string profile, PlatformID platform)
{
this.mockFixture.Setup(platform);

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

// Workload dependency package should be installed
WorkloadAssert.WorkloadPackageInstalled(this.mockFixture, "exampleworkload");
}
}

[Test]
[Platform(Exclude = "Unix,Linux,MacOsX")]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT)]
public async Task ExampleWorkloadProfileInstallsTheExpectedDependenciesOnWindowsPlatform(string profile, PlatformID platform)
{
this.mockFixture.Setup(platform);

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

// Workload dependency package should be installed
WorkloadAssert.WorkloadPackageInstalled(this.mockFixture, "exampleworkload");
}
}

[Test]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Unix)]
public async Task ExampleWorkloadProfileExecutesTheExpectedWorkloadWithAffinityOnLinux(string profile, PlatformID platform)
{
this.mockFixture.Setup(platform);
this.mockFixture.SetupPackage("exampleworkload", expectedFiles: "linux-x64/ExampleWorkload");

this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) =>
{
IProcessProxy process = this.mockFixture.CreateProcess(command, arguments, workingDir);
if (command.Contains("bash") && arguments.Contains("numactl"))
{
// Verify numactl wrapper is used for CPU affinity on Linux
process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }");
}

return process;
};

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

// Verify numactl was used for CPU affinity
WorkloadAssert.CommandsExecuted(this.mockFixture, "\"numactl -C");
Comment on lines +95 to +109
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test assertions are too weak to catch the incorrect command construction. The test only checks that "bash" is in the command and "numactl" is in the arguments (line 95), and that "numactl -C" appears somewhere (line 109).

The test should verify the exact command structure. For the correct implementation, it should verify something like:

  • command is "/bin/bash"
  • arguments match the pattern: -c "numactl -C 0-3 /path/to/ExampleWorkload Workload --duration=00:00:30"

This would catch the bug in lines 245-247 where GetCommandWithAffinity is called incorrectly.

Copilot uses AI. Check for mistakes.
}
}

[Test]
[Platform(Exclude = "Unix,Linux,MacOsX")]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json", PlatformID.Win32NT)]
public async Task ExampleWorkloadProfileExecutesTheExpectedWorkloadWithAffinityOnWindows(string profile, PlatformID platform)
{
this.mockFixture.Setup(platform);
this.mockFixture.SetupPackage("exampleworkload", expectedFiles: "win-x64/ExampleWorkload.exe");

this.mockFixture.ProcessManager.OnCreateProcess = (command, arguments, workingDir) =>
{
InMemoryProcess process = this.mockFixture.CreateProcess(command, arguments, workingDir);
if (command.Contains("ExampleWorkload"))
{
// Set HasExited to false initially, true after "wait"
bool hasExited = false;
process.OnHasExited = () => hasExited;
process.OnStart = () =>
{
hasExited = false;
return true;
};

process.OnApplyAffinity = (mask) =>
{
// Verify affinity mask is set while process is running
Assert.IsFalse(hasExited, "Affinity should be applied while process is running");
Assert.Greater(mask.ToInt64(), 0);
};

// Simulate process completion when WaitForExitAsync is called
process.WaitForExitAsync(CancellationToken.None);
process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }");
hasExited = true;
Comment on lines +134 to +145
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test has a logic issue: it calls WaitForExitAsync synchronously (without await), then immediately sets hasExited = true and appends output. This means the test setup happens synchronously before the actual executor code runs.

The correct flow should be:

  1. Set up OnWaitForExit delegate (similar to OnStart) to set hasExited = true and append output when WaitForExitAsync is actually called by the executor
  2. Or, ensure the StandardOutput is appended before the OnStart completes

Currently, the affinity assertion at line 138 will always pass because hasExited is set to true after line 143 executes synchronously, but the executor's WaitForExitAsync call hasn't happened yet.

Suggested change
process.OnApplyAffinity = (mask) =>
{
// Verify affinity mask is set while process is running
Assert.IsFalse(hasExited, "Affinity should be applied while process is running");
Assert.Greater(mask.ToInt64(), 0);
};
// Simulate process completion when WaitForExitAsync is called
process.WaitForExitAsync(CancellationToken.None);
process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }");
hasExited = true;
process.OnApplyAffinity = (mask) =>
{
// Verify affinity mask is set while process is running
Assert.IsFalse(hasExited, "Affinity should be applied while process is running");
Assert.Greater(mask.ToInt64(), 0);
};
// Simulate process completion when WaitForExitAsync is actually called
process.OnWaitForExitAsync = (token) =>
{
process.StandardOutput.Append("{ \"metric1\": 100, \"metric2\": 200 }");
hasExited = true;
return Task.CompletedTask;
};

Copilot uses AI. Check for mistakes.
}

return process;
};

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

// Verify the process was executed
WorkloadAssert.CommandsExecuted(this.mockFixture, "ExampleWorkload");
}
}

[Test]
[TestCase("PERF-CPU-EXAMPLE-AFFINITY.json")]
public void ExampleWorkloadProfileActionsWillNotBeExecutedIfTheWorkloadPackageDoesNotExist(string profile)
{
this.mockFixture.Setup(PlatformID.Unix);

// 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);
}
}
}
}
Loading
Loading