From 949061a468e80895bdc31676f2547d17e279ab6f Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 24 Apr 2026 13:28:49 -0700
Subject: [PATCH 1/2] Update image and container inspect behavior
---
localization/strings/en-US/Resources.resw | 4 +
src/windows/wslc/tasks/ContainerTasks.cpp | 32 +++-
src/windows/wslc/tasks/ImageTasks.cpp | 33 +++-
.../wslc/e2e/WSLCE2EContainerInspectTests.cpp | 163 ++++++++++++++++++
.../wslc/e2e/WSLCE2EImageInspectTests.cpp | 6 +-
5 files changed, 232 insertions(+), 6 deletions(-)
create mode 100644 test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp
diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index de258d29d..5744ee0de 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -2124,6 +2124,10 @@ For privacy information about this product please visit https://aka.ms/privacy.<
Volume not found: '{}'
{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
+
+ Image '{}' not found.
+ {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
+
Missing required option: '{}'
{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
diff --git a/src/windows/wslc/tasks/ContainerTasks.cpp b/src/windows/wslc/tasks/ContainerTasks.cpp
index 804f54282..336daadac 100644
--- a/src/windows/wslc/tasks/ContainerTasks.cpp
+++ b/src/windows/wslc/tasks/ContainerTasks.cpp
@@ -24,6 +24,7 @@ Module Name:
#include
using namespace wsl::shared;
+using namespace wsl::windows::common;
using namespace wsl::windows::common::string;
using namespace wsl::windows::common::wslutil;
using namespace wsl::windows::wslc::execution;
@@ -31,6 +32,26 @@ using namespace wsl::windows::wslc::models;
using namespace wsl::windows::wslc::services;
namespace wsl::windows::wslc::task {
+
+static bool TryInspectContainer(Session& session, const std::string& containerId, std::optional& inspectData)
+{
+ try
+ {
+ inspectData = ContainerService::Inspect(session, containerId);
+ return true;
+ }
+ catch (const wil::ResultException& ex)
+ {
+ if (ex.GetErrorCode() == WSLC_E_CONTAINER_NOT_FOUND)
+ {
+ PrintMessage(Localization::MessageWslcContainerNotFound(containerId.c_str()), stderr);
+ return false;
+ }
+
+ throw;
+ }
+}
+
void AttachContainer::operator()(CLIExecutionContext& context) const
{
WI_ASSERT(context.Data.Contains(Data::Session));
@@ -71,8 +92,15 @@ void InspectContainers(CLIExecutionContext& context)
std::vector result;
for (const auto& id : containerIds)
{
- auto inspectData = ContainerService::Inspect(session, WideToMultiByte(id));
- result.push_back(inspectData);
+ std::optional inspectData;
+ if (TryInspectContainer(session, WideToMultiByte(id), inspectData))
+ {
+ result.push_back(*inspectData);
+ }
+ else
+ {
+ context.ExitCode = 1;
+ }
}
auto json = ToJson(result, c_jsonPrettyPrintIndent);
diff --git a/src/windows/wslc/tasks/ImageTasks.cpp b/src/windows/wslc/tasks/ImageTasks.cpp
index bba509ee4..e177241a9 100644
--- a/src/windows/wslc/tasks/ImageTasks.cpp
+++ b/src/windows/wslc/tasks/ImageTasks.cpp
@@ -26,12 +26,34 @@ Module Name:
#include
using namespace wsl::shared;
+using namespace wsl::windows::common;
using namespace wsl::windows::common::string;
using namespace wsl::windows::common::wslutil;
using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::models;
using namespace wsl::windows::wslc::services;
namespace wsl::windows::wslc::task {
+
+static bool TryInspectImage(Session& session, const std::string& imageId, std::optional& inspectData)
+{
+ try
+ {
+ inspectData = ImageService::Inspect(session, imageId);
+ return true;
+ }
+ catch (const wil::ResultException& ex)
+ {
+ if (ex.GetErrorCode() == WSLC_E_IMAGE_NOT_FOUND)
+ {
+ PrintMessage(Localization::MessageWslcImageNotFound(imageId.c_str()), stderr);
+ return false;
+ }
+
+ throw;
+ }
+}
+
void BuildImage(CLIExecutionContext& context)
{
WI_ASSERT(context.Data.Contains(Data::Session));
@@ -197,8 +219,15 @@ void InspectImages(CLIExecutionContext& context)
std::vector result;
for (const auto& id : imageIds)
{
- auto inspectData = ImageService::Inspect(session, WideToMultiByte(id));
- result.push_back(inspectData);
+ std::optional inspectData;
+ if (TryInspectImage(session, WideToMultiByte(id), inspectData))
+ {
+ result.push_back(*inspectData);
+ }
+ else
+ {
+ context.ExitCode = 1;
+ }
}
auto json = ToJson(result, c_jsonPrettyPrintIndent);
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp
new file mode 100644
index 000000000..6be23208a
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp
@@ -0,0 +1,163 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EContainerInspectTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for WSLC.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+#include
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+using namespace wsl::shared::string;
+
+class WSLCE2EContainerInspectTests
+{
+ WSLC_TEST_CLASS(WSLCE2EContainerInspectTests)
+
+ TEST_CLASS_SETUP(ClassSetup)
+ {
+ EnsureImageIsLoaded(DebianImage);
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ EnsureContainerDoesNotExist(TestContainerName1);
+ EnsureContainerDoesNotExist(TestContainerName2);
+ EnsureImageIsDeleted(DebianImage);
+ return true;
+ }
+
+ TEST_METHOD_SETUP(MethodSetup)
+ {
+ EnsureContainerDoesNotExist(TestContainerName1);
+ EnsureContainerDoesNotExist(TestContainerName2);
+ return true;
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_HelpCommand)
+ {
+ auto result = RunWslc(L"container inspect --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_MissingContainerId)
+ {
+ auto result = RunWslc(L"container inspect");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"Required argument not provided: 'container-id'\r\n", .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_ContainerNotFound)
+ {
+ auto result = RunWslc(std::format(L"container inspect {}", TestContainerName1));
+ result.Verify(
+ {.Stdout = L"[]\r\n",
+ .Stderr = std::format(L"Container '{}' not found.\r\n", TestContainerName1),
+ .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_Success)
+ {
+ auto createResult = RunWslc(std::format(L"container create --name {} {}", TestContainerName1, DebianImage.NameAndTag()));
+ createResult.Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto result = RunWslc(std::format(L"container inspect {}", TestContainerName1));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto inspectData =
+ wsl::shared::FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(1u, inspectData.size());
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName1), inspectData[0].Name);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_InspectMultiple_Success)
+ {
+ // Create two containers to inspect at the same time
+ auto result = RunWslc(std::format(L"container create --name {} {}", TestContainerName1, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ result = RunWslc(std::format(L"container create --name {} {}", TestContainerName2, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ // Inspect both containers in the same command
+ result = RunWslc(std::format(L"container inspect {} {}", TestContainerName1, TestContainerName2));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+ auto inspectData =
+ wsl::shared::FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(2u, inspectData.size());
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName1), inspectData[0].Name);
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName2), inspectData[1].Name);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_MixedFoundNotFound)
+ {
+ // Create one container but not the other
+ auto result = RunWslc(std::format(L"container create --name {} {}", TestContainerName1, DebianImage.NameAndTag()));
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ // Inspect both containers in the same command, expecting one to be found and the other to not be found
+ result = RunWslc(std::format(L"container inspect {} {}", TestContainerName1, TestContainerName2));
+ result.Verify({.Stderr = std::format(L"Container '{}' not found.\r\n", TestContainerName2), .ExitCode = 1});
+
+ // Verify found container
+ auto inspectData =
+ wsl::shared::FromJson>(result.Stdout.value().c_str());
+ VERIFY_ARE_EQUAL(1u, inspectData.size());
+ VERIFY_ARE_EQUAL(WideToMultiByte(TestContainerName1), inspectData[0].Name);
+ }
+
+private:
+ const std::wstring TestContainerName1 = L"wslc-e2e-container-inspect-1";
+ const std::wstring TestContainerName2 = L"wslc-e2e-container-inspect-2";
+ const TestImage& DebianImage = DebianTestImage();
+
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableCommands() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_ContainerInspectLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc container inspect [] \r\n\r\n";
+ }
+
+ std::wstring GetAvailableCommands() const
+ {
+ std::wstringstream commands;
+ commands << L"The following arguments are available:\r\n" //
+ << L" container-id Container ID\r\n" //
+ << L"\r\n";
+ return commands.str();
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n" //
+ << L" --session Specify the session to use\r\n" //
+ << L" -?,--help Shows help about the selected command\r\n" //
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
diff --git a/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp
index 4601aa423..28f97c198 100644
--- a/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp
@@ -51,8 +51,10 @@ class WSLCE2EImageInspectTests
WSLC_TEST_METHOD(WSLCE2E_Image_Inspect_ImageNotFound)
{
auto result = RunWslc(std::format(L"image inspect {}", InvalidImage.NameAndTag()));
- auto errorMessage = std::format(L"No such image: {}\r\nError code: WSLC_E_IMAGE_NOT_FOUND\r\n", InvalidImage.NameAndTag());
- result.Verify({.Stdout = L"", .Stderr = errorMessage, .ExitCode = 1});
+ result.Verify(
+ {.Stdout = L"[]\r\n",
+ .Stderr = std::format(L"Image '{}' not found.\r\n", InvalidImage.NameAndTag()),
+ .ExitCode = 1});
}
WSLC_TEST_METHOD(WSLCE2E_Image_Inspect_Success)
From 3fc3bc0a2e6543f0a4e630468f60bead137ad719 Mon Sep 17 00:00:00 2001
From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com>
Date: Fri, 24 Apr 2026 15:40:17 -0700
Subject: [PATCH 2/2] Clang format
---
test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp | 9 +++------
test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp | 5 +----
2 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp
index 6be23208a..f804004c1 100644
--- a/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EContainerInspectTests.cpp
@@ -61,10 +61,7 @@ class WSLCE2EContainerInspectTests
WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_ContainerNotFound)
{
auto result = RunWslc(std::format(L"container inspect {}", TestContainerName1));
- result.Verify(
- {.Stdout = L"[]\r\n",
- .Stderr = std::format(L"Container '{}' not found.\r\n", TestContainerName1),
- .ExitCode = 1});
+ result.Verify({.Stdout = L"[]\r\n", .Stderr = std::format(L"Container '{}' not found.\r\n", TestContainerName1), .ExitCode = 1});
}
WSLC_TEST_METHOD(WSLCE2E_Container_Inspect_Success)
@@ -153,8 +150,8 @@ class WSLCE2EContainerInspectTests
std::wstring GetAvailableOptions() const
{
std::wstringstream options;
- options << L"The following options are available:\r\n" //
- << L" --session Specify the session to use\r\n" //
+ options << L"The following options are available:\r\n" //
+ << L" --session Specify the session to use\r\n" //
<< L" -?,--help Shows help about the selected command\r\n" //
<< L"\r\n";
return options.str();
diff --git a/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp b/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp
index 28f97c198..caeb451c9 100644
--- a/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EImageInspectTests.cpp
@@ -51,10 +51,7 @@ class WSLCE2EImageInspectTests
WSLC_TEST_METHOD(WSLCE2E_Image_Inspect_ImageNotFound)
{
auto result = RunWslc(std::format(L"image inspect {}", InvalidImage.NameAndTag()));
- result.Verify(
- {.Stdout = L"[]\r\n",
- .Stderr = std::format(L"Image '{}' not found.\r\n", InvalidImage.NameAndTag()),
- .ExitCode = 1});
+ result.Verify({.Stdout = L"[]\r\n", .Stderr = std::format(L"Image '{}' not found.\r\n", InvalidImage.NameAndTag()), .ExitCode = 1});
}
WSLC_TEST_METHOD(WSLCE2E_Image_Inspect_Success)