From 3e4738a17f6dee2e2b68122025f712a4c7db043f Mon Sep 17 00:00:00 2001 From: Pooja Trivedi Date: Thu, 26 Mar 2026 15:54:20 -0400 Subject: [PATCH 1/5] [WSLC] Add 'wslc version' subcommand Adds a 'version' subcommand to the WSLC CLI as an alternative to the existing '--version' flag, following the subcommand pattern used by other WSLC commands. Includes unit tests for command structure and command-line parsing. Co-authored-by: Pooja Trivedi Co-Authored-By: Claude Sonnet 4.6 --- src/windows/wslc/commands/RootCommand.cpp | 2 + src/windows/wslc/commands/VersionCommand.cpp | 33 +++++++++++++++ src/windows/wslc/commands/VersionCommand.h | 30 ++++++++++++++ test/windows/wslc/CommandLineTestCases.h | 5 +++ test/windows/wslc/WSLCCLICommandUnitTests.cpp | 41 +++++++++++++++++++ 5 files changed, 111 insertions(+) create mode 100644 src/windows/wslc/commands/VersionCommand.cpp create mode 100644 src/windows/wslc/commands/VersionCommand.h diff --git a/src/windows/wslc/commands/RootCommand.cpp b/src/windows/wslc/commands/RootCommand.cpp index faa6a2317..1803863e2 100644 --- a/src/windows/wslc/commands/RootCommand.cpp +++ b/src/windows/wslc/commands/RootCommand.cpp @@ -17,6 +17,7 @@ Module Name: #include "ContainerCommand.h" #include "ImageCommand.h" #include "SessionCommand.h" +#include "VersionCommand.h" using namespace wsl::windows::wslc::execution; @@ -43,6 +44,7 @@ std::vector> RootCommand::GetCommands() const commands.push_back(std::make_unique(FullName())); commands.push_back(std::make_unique(FullName())); commands.push_back(std::make_unique(FullName())); + commands.push_back(std::make_unique(FullName())); return commands; } diff --git a/src/windows/wslc/commands/VersionCommand.cpp b/src/windows/wslc/commands/VersionCommand.cpp new file mode 100644 index 000000000..4c8ecff18 --- /dev/null +++ b/src/windows/wslc/commands/VersionCommand.cpp @@ -0,0 +1,33 @@ +/*++ + +Copyright (c) Microsoft. All rights reserved. + +Module Name: + + VersionCommand.cpp + +Abstract: + + Implementation of the version command. + +--*/ +#include "VersionCommand.h" + +using namespace wsl::windows::wslc::execution; + +namespace wsl::windows::wslc { +std::wstring VersionCommand::ShortDescription() const +{ + return {L"Show version information."}; +} + +std::wstring VersionCommand::LongDescription() const +{ + return {L"Show version information for this tool."}; +} + +void VersionCommand::ExecuteInternal(CLIExecutionContext& context) const +{ + wsl::windows::common::wslutil::PrintMessage(std::format(L"{} v{}", s_ExecutableName, WSL_PACKAGE_VERSION)); +} +} // namespace wsl::windows::wslc diff --git a/src/windows/wslc/commands/VersionCommand.h b/src/windows/wslc/commands/VersionCommand.h new file mode 100644 index 000000000..1662d55bd --- /dev/null +++ b/src/windows/wslc/commands/VersionCommand.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) Microsoft. All rights reserved. + +Module Name: + + VersionCommand.h + +Abstract: + + Declaration of the VersionCommand. + +--*/ +#pragma once +#include "Command.h" + +namespace wsl::windows::wslc { +struct VersionCommand final : public Command +{ + constexpr static std::wstring_view CommandName = L"version"; + VersionCommand(const std::wstring& parent) : Command(CommandName, parent) + { + } + std::wstring ShortDescription() const override; + std::wstring LongDescription() const override; + +protected: + void ExecuteInternal(CLIExecutionContext& context) const override; +}; +} // namespace wsl::windows::wslc diff --git a/test/windows/wslc/CommandLineTestCases.h b/test/windows/wslc/CommandLineTestCases.h index 32c471461..bd729226a 100644 --- a/test/windows/wslc/CommandLineTestCases.h +++ b/test/windows/wslc/CommandLineTestCases.h @@ -111,6 +111,11 @@ COMMAND_LINE_TEST_CASE(L"image list -q", L"list", true) COMMAND_LINE_TEST_CASE(L"image pull ubuntu", L"pull", true) COMMAND_LINE_TEST_CASE(L"pull ubuntu", L"pull", true) +// Version command tests +COMMAND_LINE_TEST_CASE(L"version", L"version", true) +COMMAND_LINE_TEST_CASE(L"version --help", L"version", true) +COMMAND_LINE_TEST_CASE(L"version extraarg", L"version", false) + // Error cases COMMAND_LINE_TEST_CASE(L"invalid command", L"", false) COMMAND_LINE_TEST_CASE(L"CONTAINER list", L"list", false) // We are intentionally case-sensitive diff --git a/test/windows/wslc/WSLCCLICommandUnitTests.cpp b/test/windows/wslc/WSLCCLICommandUnitTests.cpp index 2a3c72ae2..f357ac321 100644 --- a/test/windows/wslc/WSLCCLICommandUnitTests.cpp +++ b/test/windows/wslc/WSLCCLICommandUnitTests.cpp @@ -20,6 +20,7 @@ Module Name: #include "RootCommand.h" #include "ContainerCommand.h" #include "SessionCommand.h" +#include "VersionCommand.h" using namespace wsl::windows::wslc; using namespace WSLCTestHelpers; @@ -95,6 +96,46 @@ class WSLCCLICommandUnitTests VERIFY_IS_NOT_NULL(subcmd.get()); } } + + // Test: Verify VersionCommand has the correct name + TEST_METHOD(VersionCommand_HasCorrectName) + { + auto cmd = VersionCommand(L"wslc"); + VERIFY_ARE_EQUAL(std::wstring_view(L"version"), cmd.Name()); + } + + // Test: Verify VersionCommand has no subcommands + TEST_METHOD(VersionCommand_HasNoSubcommands) + { + auto cmd = VersionCommand(L"wslc"); + VERIFY_ARE_EQUAL(0u, cmd.GetCommands().size()); + } + + // Test: Verify VersionCommand has no arguments (only the auto-added --help) + TEST_METHOD(VersionCommand_HasNoArguments) + { + auto cmd = VersionCommand(L"wslc"); + VERIFY_ARE_EQUAL(0u, cmd.GetArguments().size()); + } + + // Test: Verify RootCommand contains VersionCommand as a subcommand + TEST_METHOD(RootCommand_ContainsVersionCommand) + { + auto root = RootCommand(); + auto subcommands = root.GetCommands(); + + bool found = false; + for (const auto& subcmd : subcommands) + { + if (subcmd->Name() == VersionCommand::CommandName) + { + found = true; + break; + } + } + + VERIFY_IS_TRUE(found, L"RootCommand should contain VersionCommand"); + } }; } // namespace WSLCCLICommandUnitTests \ No newline at end of file From 660b493efbf8bf41a0f19527dc754e70b8b23feb Mon Sep 17 00:00:00 2001 From: Pooja Trivedi Date: Fri, 27 Mar 2026 14:27:06 -0400 Subject: [PATCH 2/5] [WSLC] Fix E2E help/invalid-command tests after version subcommand addition Update expected help output in WSLCE2EGlobalTests to include the newly added 'version' subcommand, fixing WSLCE2E_HelpCommand and WSLCE2E_InvalidCommand_DisplaysErrorMessage test failures. Co-Authored-By: Claude Sonnet 4.6 --- test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp index c2927c950..e3c4a89de 100644 --- a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp +++ b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp @@ -90,6 +90,7 @@ class WSLCE2EGlobalTests << L" run Run a container.\r\n" << L" start Start a container.\r\n" << L" stop Stop containers.\r\n" + << L" version Show version information.\r\n" << L"\r\n" << L"For more details on a specific command, pass it the help argument. [-h]\r\n\r\n"; return commands.str(); From 70b1e83c275401840307a32837a2c2cf5d06456e Mon Sep 17 00:00:00 2001 From: Pooja Trivedi Date: Sat, 28 Mar 2026 23:57:50 -0400 Subject: [PATCH 3/5] [WSLC] Add E2E version command test and strengthen unit test - Add WSLCE2E_VersionCommand E2E test in WSLCE2EGlobalTests verifying stdout, empty stderr, and exit code for 'wslc version' - Add assertion to VersionCommand_HasNoArguments unit test to verify only the auto-added --help argument is present Authored-By: Pooja Trivedi Co-Authored-By: Claude Sonnet 4.6 --- test/windows/wslc/WSLCCLICommandUnitTests.cpp | 2 ++ test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/test/windows/wslc/WSLCCLICommandUnitTests.cpp b/test/windows/wslc/WSLCCLICommandUnitTests.cpp index f357ac321..4e5d88f67 100644 --- a/test/windows/wslc/WSLCCLICommandUnitTests.cpp +++ b/test/windows/wslc/WSLCCLICommandUnitTests.cpp @@ -116,6 +116,8 @@ class WSLCCLICommandUnitTests { auto cmd = VersionCommand(L"wslc"); VERIFY_ARE_EQUAL(0u, cmd.GetArguments().size()); + // Test out that auto added help command is the only one + VERIFY_ARE_EQUAL(1u, cmd.GetAllArguments().size()); } // Test: Verify RootCommand contains VersionCommand as a subcommand diff --git a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp index e3c4a89de..17799ac1c 100644 --- a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp +++ b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp @@ -44,6 +44,12 @@ class WSLCE2EGlobalTests RunWslcAndVerify(L"INVALID_CMD", {.Stdout = GetHelpMessage(), .Stderr = L"Unrecognized command: 'INVALID_CMD'\r\n", .ExitCode = 1}); } + TEST_METHOD(WSLCE2E_VersionCommand) + { + WSL2_TEST_ONLY(); + RunWslcAndVerify(L"version", {.Stdout = GetVersionMessage(), .Stderr = L"", .ExitCode = 0}); + } + private: std::wstring GetHelpMessage() const { @@ -56,6 +62,11 @@ class WSLCE2EGlobalTests return output.str(); } + std::wstring GetVersionMessage() const + { + return std::format(L"wslc v{}\r\n", WSL_PACKAGE_VERSION); + } + std::wstring GetDescription() const { return L"WSLC is the Windows Subsystem for Linux Container CLI tool. It enables management and interaction with WSL " From bf04c87e2841318f8fc026146dd5809de1f12709 Mon Sep 17 00:00:00 2001 From: Pooja Trivedi Date: Sun, 29 Mar 2026 00:16:21 -0400 Subject: [PATCH 4/5] Mark 'context' as UNREFERENCED_PARAMETER in VersionCommand::ExecuteInternal to avoid build issues --- src/windows/wslc/commands/VersionCommand.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/windows/wslc/commands/VersionCommand.cpp b/src/windows/wslc/commands/VersionCommand.cpp index 4c8ecff18..4e83b387f 100644 --- a/src/windows/wslc/commands/VersionCommand.cpp +++ b/src/windows/wslc/commands/VersionCommand.cpp @@ -28,6 +28,7 @@ std::wstring VersionCommand::LongDescription() const void VersionCommand::ExecuteInternal(CLIExecutionContext& context) const { + UNREFERENCED_PARAMETER(context); wsl::windows::common::wslutil::PrintMessage(std::format(L"{} v{}", s_ExecutableName, WSL_PACKAGE_VERSION)); } } // namespace wsl::windows::wslc From 182b0d97c15e875ced05ba4a859a771b29989ff0 Mon Sep 17 00:00:00 2001 From: Pooja Trivedi Date: Fri, 3 Apr 2026 20:15:18 -0400 Subject: [PATCH 5/5] Address PR feedback on wslc version command - Use Localization::WSLCCLI_VersionDesc/LongDesc instead of hard-coded strings; add entries to en-US Resources.resw - Centralize version printing in VersionCommand::PrintVersion(); reuse from RootCommand --version flag - Drop 'v' prefix from version output per OneBlue's feedback - Add 'version' entry to E2E GetAvailableCommands() to fix WSLCE2E_HelpCommand and WSLCE2E_InvalidCommand_DisplaysErrorMessage Co-Authored-By: Pooja Trivedi --- localization/strings/en-US/Resources.resw | 6 ++++++ src/windows/wslc/commands/RootCommand.cpp | 2 +- src/windows/wslc/commands/VersionCommand.cpp | 11 ++++++++--- src/windows/wslc/commands/VersionCommand.h | 1 + test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp | 3 ++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw index ff843e40e..3e9587df5 100644 --- a/localization/strings/en-US/Resources.resw +++ b/localization/strings/en-US/Resources.resw @@ -2294,6 +2294,12 @@ On first run, creates the file with all settings commented out at their defaults Settings reset to defaults. + + Show version information. + + + Show version information for this tool. + Show all regardless of state. diff --git a/src/windows/wslc/commands/RootCommand.cpp b/src/windows/wslc/commands/RootCommand.cpp index f56ece4e8..732c19404 100644 --- a/src/windows/wslc/commands/RootCommand.cpp +++ b/src/windows/wslc/commands/RootCommand.cpp @@ -73,7 +73,7 @@ void RootCommand::ExecuteInternal(CLIExecutionContext& context) const { if (context.Args.Contains(ArgType::Version)) { - wsl::windows::common::wslutil::PrintMessage(std::format(L"{} v{}", s_ExecutableName, WSL_PACKAGE_VERSION)); + VersionCommand::PrintVersion(); return; } diff --git a/src/windows/wslc/commands/VersionCommand.cpp b/src/windows/wslc/commands/VersionCommand.cpp index 4e83b387f..eba4c565f 100644 --- a/src/windows/wslc/commands/VersionCommand.cpp +++ b/src/windows/wslc/commands/VersionCommand.cpp @@ -18,17 +18,22 @@ using namespace wsl::windows::wslc::execution; namespace wsl::windows::wslc { std::wstring VersionCommand::ShortDescription() const { - return {L"Show version information."}; + return Localization::WSLCCLI_VersionDesc(); } std::wstring VersionCommand::LongDescription() const { - return {L"Show version information for this tool."}; + return Localization::WSLCCLI_VersionLongDesc(); +} + +void VersionCommand::PrintVersion() +{ + wsl::windows::common::wslutil::PrintMessage(std::format(L"{} {}", s_ExecutableName, WSL_PACKAGE_VERSION)); } void VersionCommand::ExecuteInternal(CLIExecutionContext& context) const { UNREFERENCED_PARAMETER(context); - wsl::windows::common::wslutil::PrintMessage(std::format(L"{} v{}", s_ExecutableName, WSL_PACKAGE_VERSION)); + PrintVersion(); } } // namespace wsl::windows::wslc diff --git a/src/windows/wslc/commands/VersionCommand.h b/src/windows/wslc/commands/VersionCommand.h index 1662d55bd..be700f550 100644 --- a/src/windows/wslc/commands/VersionCommand.h +++ b/src/windows/wslc/commands/VersionCommand.h @@ -21,6 +21,7 @@ struct VersionCommand final : public Command VersionCommand(const std::wstring& parent) : Command(CommandName, parent) { } + static void PrintVersion(); std::wstring ShortDescription() const override; std::wstring LongDescription() const override; diff --git a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp index 5e02fbd6f..ae5fc2c57 100644 --- a/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp +++ b/test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp @@ -407,7 +407,7 @@ class WSLCE2EGlobalTests std::wstring GetVersionMessage() const { - return std::format(L"wslc v{}\r\n", WSL_PACKAGE_VERSION); + return std::format(L"wslc {}\r\n", WSL_PACKAGE_VERSION); } std::wstring GetDescription() const @@ -445,6 +445,7 @@ class WSLCE2EGlobalTests {L"save", Localization::WSLCCLI_ImageSaveDesc()}, {L"start", Localization::WSLCCLI_ContainerStartDesc()}, {L"stop", Localization::WSLCCLI_ContainerStopDesc()}, + {L"version", Localization::WSLCCLI_VersionDesc()}, }; size_t maxLen = 0;