From d9bb295622bd3dfb5fb4db2bffb962d1feea8a5a Mon Sep 17 00:00:00 2001 From: Alex Williams-Ferreira Date: Fri, 24 Apr 2026 11:54:53 -0700 Subject: [PATCH 1/2] Fix quoting in apt-daily mask ExecuteCommand + extend to FIO profiles Follow-up to #694. Two issues reported by the CRC QoS team after the initial change merged: 1. Quoting bug: the Command parameter used single quotes to group the bash subcommand: bash -c 'systemctl mask ...' VC's ExecuteCommand splits the first whitespace-delimited token (bash) as the executable and passes the remainder to .NET Process.Start as the Arguments string. .NET's argument tokenizer (on both Linux and Windows) treats double quotes as the grouping character and does NOT strip single quotes. As a result bash received argv[2] = 'systemctl literally, which caused it to fail with a command not found style error. Replaced with escaped double quotes: bash -c "systemctl mask ..." The CRC team validated this form locally and has a Juno experiment running against a profile with this corrected syntax. 2. Scope: the mid-run VC restart on Ubuntu 24.04 is not SPEC-specific. The team explicitly reported reproducing it with FIO in experiment SYSAUTO-CHPI_MWH25PrdApp17_Fio_PV2_all_sizes_ga_version_Standard_M176bs_v3- 20260421135821. Added the same MaskAptDailyTimers dependency step to PERF-IO-FIO.json and PERF-IO-FIO-OLTP.json. Broader coverage across all Linux perf profiles (or a code-level startup hook in ExecuteProfileCommand so no profile opt-in is needed) can follow after further review discussion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json | 8 ++++++++ .../VirtualClient.Main/profiles/PERF-IO-FIO.json | 8 ++++++++ .../VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json | 2 +- .../VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json | 2 +- .../VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json | 2 +- .../profiles/PERF-SPECCPU-INTSPEED.json | 2 +- 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json index 9cf4c5b2c9..c3f8b817c5 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json @@ -112,6 +112,14 @@ } ], "Dependencies": [ + { + "Type": "ExecuteCommand", + "Parameters": { + "Scenario": "MaskAptDailyTimers", + "SupportedPlatforms": "linux-x64,linux-arm64", + "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" + } + }, { "Type": "LinuxPackageInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json index 281d72f2fb..0ee94bb808 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json @@ -491,6 +491,14 @@ } ], "Dependencies": [ + { + "Type": "ExecuteCommand", + "Parameters": { + "Scenario": "MaskAptDailyTimers", + "SupportedPlatforms": "linux-x64,linux-arm64", + "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" + } + }, { "Type": "LinuxPackageInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json index 852da775b1..9527c9dacb 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json @@ -46,7 +46,7 @@ "Parameters": { "Scenario": "MaskAptDailyTimers", "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c 'systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0'" + "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" } }, { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json index 9104d251c9..b848e9fe06 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json @@ -46,7 +46,7 @@ "Parameters": { "Scenario": "MaskAptDailyTimers", "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c 'systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0'" + "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" } }, { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json index 13d7844874..e0c071a669 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json @@ -46,7 +46,7 @@ "Parameters": { "Scenario": "MaskAptDailyTimers", "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c 'systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0'" + "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" } }, { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json index 5e53cca496..2b360fff09 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json @@ -46,7 +46,7 @@ "Parameters": { "Scenario": "MaskAptDailyTimers", "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c 'systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0'" + "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" } }, { From 8b95d22cd732f5ca8475ebade190a843ae0e95b0 Mon Sep 17 00:00:00 2001 From: Alex Williams-Ferreira Date: Fri, 24 Apr 2026 12:04:34 -0700 Subject: [PATCH 2/2] Detect Ubuntu 24.04+ and disable apt-daily auto-updates at VC startup Problem ------- On Ubuntu 24.04+, the default installation of `needrestart` combined with the `apt-daily-upgrade.timer` (fires daily 06:00-07:00 UTC with RandomizedDelaySec=60min) automatically restarts any service whose shared libraries are updated by unattended upgrades. For long-running Virtual Client workloads this manifests as VC being SIGKILL'd mid-run - observed at ~29% of VMs in CRC SYSAUTO experiments, concentrated at hour 6 UTC (uniform 0-59 minute distribution, matching the apt timer signature). Web / distro research confirmed this behavior is specific to Ubuntu 24.04+: - Ubuntu 24.04+: needrestart installed AND auto-restart-on-unattended-upgrade is the default (this is the regression). - Ubuntu 22.04/22.10/23.04: needrestart installed but list-only in non-interactive mode; not known to cause the issue in the field. - Debian 11/12: needrestart not installed by default. Fix --- Add a best-effort startup hook in ExecuteProfileCommand that: - runs exactly once per VC invocation, immediately after Platform.Initialize - is a no-op on non-Unix platforms - parses the Ubuntu major version out of PRETTY_NAME and only runs on >=24 - masks + stops the four apt-daily units via `bash -c "..."` (double-quoted because .NET Process argument tokenization follows Windows CommandLineToArgvW rules - single quotes do not group) - swallows any exception so VC startup is never blocked by this mitigation - logs telemetry (DisabledLinuxAutoUpdates / DisableLinuxAutoUpdatesFailed) Because the mitigation now runs unconditionally for every profile on the affected OS, the per-profile MaskAptDailyTimers step added to the SPEC CPU and FIO profiles in PR #694 is redundant and has been removed. Bumped VERSION to 3.1.3. Tests ----- Added parameterized coverage for TryGetUbuntuMajorVersion (9 cases, all passing). ExecuteProfileCommandTests: 24/24 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- VERSION | 2 +- .../ExecuteProfileCommand.cs | 94 +++++++++++++++++++ .../profiles/PERF-IO-FIO-OLTP.json | 8 -- .../profiles/PERF-IO-FIO.json | 8 -- .../profiles/PERF-SPECCPU-FPRATE.json | 8 -- .../profiles/PERF-SPECCPU-FPSPEED.json | 8 -- .../profiles/PERF-SPECCPU-INTRATE.json | 8 -- .../profiles/PERF-SPECCPU-INTSPEED.json | 8 -- .../ExecuteProfileCommandTests.cs | 16 ++++ 9 files changed, 111 insertions(+), 49 deletions(-) diff --git a/VERSION b/VERSION index 6ebad14888..711ee4f504 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.1.2 \ No newline at end of file +3.1.3 \ No newline at end of file diff --git a/src/VirtualClient/VirtualClient.Main/ExecuteProfileCommand.cs b/src/VirtualClient/VirtualClient.Main/ExecuteProfileCommand.cs index aef26e512e..e121823ebb 100644 --- a/src/VirtualClient/VirtualClient.Main/ExecuteProfileCommand.cs +++ b/src/VirtualClient/VirtualClient.Main/ExecuteProfileCommand.cs @@ -17,6 +17,7 @@ namespace VirtualClient using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; + using VirtualClient.Common; using VirtualClient.Common.Contracts; using VirtualClient.Common.Extensions; using VirtualClient.Common.Telemetry; @@ -163,6 +164,11 @@ protected override async Task ExecuteAsync(string[] args, IServiceCollectio logger.LogMessage($"Platform.Initialize", telemetryContext); + // Defend long-running workloads against unattended-upgrades triggered SIGKILL on + // Ubuntu 24.04+ (needrestart auto-restart-on-upgrade is enabled by default). + // Best-effort; never fails VC startup. + await this.DisableLinuxAutoUpdatesAsync(logger, systemManagement, telemetryContext, cancellationToken); + if (this.Profiles?.Any() == true) { this.LogContextToConsole(dependencies); @@ -243,6 +249,94 @@ protected override async Task ExecuteAsync(string[] args, IServiceCollectio return exitCode; } + /// + /// On Ubuntu 24.04 and later, the default installation of the + /// needrestart + /// package (combined with the apt-daily-upgrade.timer systemd timer that fires every day + /// between 06:00 and 07:00 UTC) will automatically restart services whose shared libraries are + /// updated by unattended upgrades. For long-running workloads this manifests as the Virtual Client + /// process being killed mid-run, desynchronizing multi-VM experiments and invalidating results. + /// + /// This method is a best-effort mitigation that masks (and stops) the apt-daily timers and + /// services when running on Ubuntu 24.04 or newer. Failures are logged but never propagated + /// so that Virtual Client startup is unaffected. + /// + /// + protected virtual async Task DisableLinuxAutoUpdatesAsync( + ILogger logger, + ISystemManagement systemManagement, + EventContext telemetryContext, + CancellationToken cancellationToken) + { + if (systemManagement == null || systemManagement.Platform != PlatformID.Unix) + { + return; + } + + try + { + LinuxDistributionInfo distroInfo = await systemManagement.GetLinuxDistributionAsync(cancellationToken) + .ConfigureAwait(false); + + // The auto-restart-on-unattended-upgrade behavior has only been confirmed on Ubuntu 24.04+. + // Earlier Ubuntu releases and Debian do not auto-restart services in non-interactive mode + // and are not known to cause this issue in the field. + if (distroInfo?.LinuxDistribution != LinuxDistribution.Ubuntu + || !ExecuteProfileCommand.TryGetUbuntuMajorVersion(distroInfo.OperationSystemFullName, out int majorVersion) + || majorVersion < 24) + { + return; + } + + EventContext maskContext = telemetryContext.Clone() + .AddContext("linuxDistribution", distroInfo.LinuxDistribution.ToString()) + .AddContext("linuxDistributionFullName", distroInfo.OperationSystemFullName) + .AddContext("ubuntuMajorVersion", majorVersion); + + // Double-quoted -c argument is required: .NET Process argument tokenization follows + // Windows CommandLineToArgvW-style rules (double-quote grouping only), so single quotes + // would be passed literally to bash. + string bashArgs = "-c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service;" + + " systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service;" + + " exit 0\""; + + using (IProcessProxy process = systemManagement.ProcessManager.CreateProcess("bash", bashArgs)) + { + await process.StartAndWaitAsync(cancellationToken).ConfigureAwait(false); + maskContext.AddContext("exitCode", process.ExitCode); + maskContext.AddContext("standardError", process.StandardError?.ToString()); + } + + logger.LogMessage($"{nameof(ExecuteProfileCommand)}.DisabledLinuxAutoUpdates", LogLevel.Information, maskContext); + } + catch (Exception exc) + { + // Never fail VC startup because of this mitigation. + logger.LogMessage( + $"{nameof(ExecuteProfileCommand)}.DisableLinuxAutoUpdatesFailed", + LogLevel.Warning, + telemetryContext.Clone().AddError(exc)); + } + } + + /// + /// Parses the Ubuntu major version (e.g. 22, 24) from the PRETTY_NAME string returned + /// by (e.g. "Ubuntu 24.04.1 LTS"). + /// Returns false when the name does not contain a parseable Ubuntu version. + /// + internal static bool TryGetUbuntuMajorVersion(string osFullName, out int majorVersion) + { + majorVersion = 0; + + if (string.IsNullOrWhiteSpace(osFullName)) + { + return false; + } + + Match match = Regex.Match(osFullName, @"Ubuntu\s+(\d+)\.", RegexOptions.IgnoreCase); + return match.Success && int.TryParse(match.Groups[1].Value, out majorVersion); + } + /// /// Downloads the profile from a remote location to the local profile downloads folder. /// diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json index c3f8b817c5..9cf4c5b2c9 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO-OLTP.json @@ -112,14 +112,6 @@ } ], "Dependencies": [ - { - "Type": "ExecuteCommand", - "Parameters": { - "Scenario": "MaskAptDailyTimers", - "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" - } - }, { "Type": "LinuxPackageInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json index 0ee94bb808..281d72f2fb 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-IO-FIO.json @@ -491,14 +491,6 @@ } ], "Dependencies": [ - { - "Type": "ExecuteCommand", - "Parameters": { - "Scenario": "MaskAptDailyTimers", - "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" - } - }, { "Type": "LinuxPackageInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json index 9527c9dacb..9400ceece9 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPRATE.json @@ -41,14 +41,6 @@ } ], "Dependencies": [ - { - "Type": "ExecuteCommand", - "Parameters": { - "Scenario": "MaskAptDailyTimers", - "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" - } - }, { "Type": "ChocolateyInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json index b848e9fe06..1a6c80b7c6 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-FPSPEED.json @@ -41,14 +41,6 @@ } ], "Dependencies": [ - { - "Type": "ExecuteCommand", - "Parameters": { - "Scenario": "MaskAptDailyTimers", - "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" - } - }, { "Type": "ChocolateyInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json index e0c071a669..5b3970ff09 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTRATE.json @@ -41,14 +41,6 @@ } ], "Dependencies": [ - { - "Type": "ExecuteCommand", - "Parameters": { - "Scenario": "MaskAptDailyTimers", - "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" - } - }, { "Type": "ChocolateyInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json index 2b360fff09..183431d0fa 100644 --- a/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json +++ b/src/VirtualClient/VirtualClient.Main/profiles/PERF-SPECCPU-INTSPEED.json @@ -41,14 +41,6 @@ } ], "Dependencies": [ - { - "Type": "ExecuteCommand", - "Parameters": { - "Scenario": "MaskAptDailyTimers", - "SupportedPlatforms": "linux-x64,linux-arm64", - "Command": "bash -c \"systemctl mask apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; systemctl stop apt-daily.timer apt-daily-upgrade.timer apt-daily.service apt-daily-upgrade.service; exit 0\"" - } - }, { "Type": "ChocolateyInstallation", "Parameters": { diff --git a/src/VirtualClient/VirtualClient.UnitTests/ExecuteProfileCommandTests.cs b/src/VirtualClient/VirtualClient.UnitTests/ExecuteProfileCommandTests.cs index e69672c718..904d794906 100644 --- a/src/VirtualClient/VirtualClient.UnitTests/ExecuteProfileCommandTests.cs +++ b/src/VirtualClient/VirtualClient.UnitTests/ExecuteProfileCommandTests.cs @@ -498,6 +498,22 @@ public async Task RunProfileCommandSupportsParametersOnListInProfile_Scenario4_P Assert.AreEqual("conditionalA", profile.Parameters["Parameter4"].ToString()); } + [TestCase("Ubuntu 24.04.1 LTS", true, 24)] + [TestCase("Ubuntu 24.04 LTS", true, 24)] + [TestCase("Ubuntu 22.04.3 LTS", true, 22)] + [TestCase("Ubuntu 25.10", true, 25)] + [TestCase("ubuntu 24.04.1 lts", true, 24)] + [TestCase("Debian GNU/Linux 12 (bookworm)", false, 0)] + [TestCase("Ubuntu Noble Numbat (development branch)", false, 0)] + [TestCase("", false, 0)] + [TestCase(null, false, 0)] + public void TryGetUbuntuMajorVersionParsesExpectedValues(string prettyName, bool expectedSuccess, int expectedMajor) + { + bool actualSuccess = ExecuteProfileCommand.TryGetUbuntuMajorVersion(prettyName, out int actualMajor); + Assert.AreEqual(expectedSuccess, actualSuccess); + Assert.AreEqual(expectedMajor, actualMajor); + } + private class TestRunProfileCommand : ExecuteProfileCommand { public new PlatformExtensions Extensions