diff --git a/localization/strings/en-US/Resources.resw b/localization/strings/en-US/Resources.resw
index 7e3f675ae..70fd4d390 100644
--- a/localization/strings/en-US/Resources.resw
+++ b/localization/strings/en-US/Resources.resw
@@ -3100,6 +3100,24 @@ On first run, creates the file with all settings commented out at their defaults
Lists all volumes in the session.
+
+ Remove unused local volumes.
+
+
+ Removes all unused anonymous local volumes. If --all is specified, also removes unused named volumes. A volume is considered unused when it is not referenced by any container.
+ {Locked="--all "}Command line arguments, file names and string inserts should not be translated
+
+
+ Remove all unused volumes, not just anonymous ones.
+
+
+ Deleted: {}
+ {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
+
+
+ Total reclaimed space: {}
+ {FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated
+
Volume name
diff --git a/src/windows/wslc/arguments/ArgumentValidation.cpp b/src/windows/wslc/arguments/ArgumentValidation.cpp
index 7f507ea37..5210f7eb7 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.cpp
+++ b/src/windows/wslc/arguments/ArgumentValidation.cpp
@@ -182,10 +182,7 @@ void ValidateFilter(const std::vector& values)
{
for (const auto& value : values)
{
- if (value.find(L'=') == std::wstring::npos)
- {
- throw ArgumentException(Localization::WSLCCLI_InvalidFilterError(value));
- }
+ std::ignore = ParseFilter(value);
}
}
@@ -422,4 +419,15 @@ std::pair ParseDriverOption(const std::wstring& value)
return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
}
+std::pair ParseFilter(const std::wstring& value)
+{
+ auto pos = value.find(L'=');
+ if (pos == std::wstring::npos)
+ {
+ throw ArgumentException(Localization::WSLCCLI_InvalidFilterError(value));
+ }
+
+ return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
+}
+
} // namespace wsl::windows::wslc::validation
diff --git a/src/windows/wslc/arguments/ArgumentValidation.h b/src/windows/wslc/arguments/ArgumentValidation.h
index 2356ea2cb..9aa0aa8b8 100644
--- a/src/windows/wslc/arguments/ArgumentValidation.h
+++ b/src/windows/wslc/arguments/ArgumentValidation.h
@@ -83,5 +83,6 @@ void ValidateFilter(const std::vector& values);
std::pair ParseLabel(const std::wstring& value);
std::pair ParseDriverOption(const std::wstring& value);
+std::pair ParseFilter(const std::wstring& value);
} // namespace wsl::windows::wslc::validation
diff --git a/src/windows/wslc/commands/VolumeCommand.cpp b/src/windows/wslc/commands/VolumeCommand.cpp
index 9c171a349..dd3ded298 100644
--- a/src/windows/wslc/commands/VolumeCommand.cpp
+++ b/src/windows/wslc/commands/VolumeCommand.cpp
@@ -26,6 +26,7 @@ std::vector> VolumeCommand::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/VolumeCommand.h b/src/windows/wslc/commands/VolumeCommand.h
index 19a2b0634..e8cabdf5e 100644
--- a/src/windows/wslc/commands/VolumeCommand.h
+++ b/src/windows/wslc/commands/VolumeCommand.h
@@ -92,4 +92,19 @@ struct VolumeListCommand final : public Command
void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
void ExecuteInternal(CLIExecutionContext& context) const override;
};
+
+// Prune Command
+struct VolumePruneCommand final : public Command
+{
+ constexpr static std::wstring_view CommandName = L"prune";
+ VolumePruneCommand(const std::wstring& parent) : Command(CommandName, parent)
+ {
+ }
+ std::vector GetArguments() const override;
+ std::wstring ShortDescription() const override;
+ std::wstring LongDescription() const override;
+
+protected:
+ void ExecuteInternal(CLIExecutionContext& context) const override;
+};
} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/commands/VolumePruneCommand.cpp b/src/windows/wslc/commands/VolumePruneCommand.cpp
new file mode 100644
index 000000000..d62585a34
--- /dev/null
+++ b/src/windows/wslc/commands/VolumePruneCommand.cpp
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ VolumePruneCommand.cpp
+
+Abstract:
+
+ Implementation of command execution logic.
+
+--*/
+
+#include "VolumeCommand.h"
+#include "CLIExecutionContext.h"
+#include "SessionTasks.h"
+#include "VolumeTasks.h"
+#include "Task.h"
+
+using namespace wsl::windows::wslc::execution;
+using namespace wsl::windows::wslc::task;
+using namespace wsl::shared;
+
+namespace wsl::windows::wslc {
+// Volume Prune Command
+std::vector VolumePruneCommand::GetArguments() const
+{
+ return {
+ Argument::Create(ArgType::All, std::nullopt, std::nullopt, Localization::WSLCCLI_VolumePruneAllArgDescription()),
+ Argument::Create(ArgType::Filter, false, NO_LIMIT),
+ Argument::Create(ArgType::Session),
+ };
+}
+
+std::wstring VolumePruneCommand::ShortDescription() const
+{
+ return Localization::WSLCCLI_VolumePruneDesc();
+}
+
+std::wstring VolumePruneCommand::LongDescription() const
+{
+ return Localization::WSLCCLI_VolumePruneLongDesc();
+}
+
+void VolumePruneCommand::ExecuteInternal(CLIExecutionContext& context) const
+{
+ context //
+ << CreateSession //
+ << PruneVolumes;
+}
+} // namespace wsl::windows::wslc
diff --git a/src/windows/wslc/services/VolumeModel.h b/src/windows/wslc/services/VolumeModel.h
index e31fd1ec3..6aa4adbf8 100644
--- a/src/windows/wslc/services/VolumeModel.h
+++ b/src/windows/wslc/services/VolumeModel.h
@@ -27,4 +27,10 @@ struct CreateVolumeOptions
std::vector> Labels{};
};
+struct PruneVolumesResult
+{
+ std::vector PrunedVolumes;
+ ULONGLONG SpaceReclaimed{};
+};
+
} // namespace wsl::windows::wslc::models
diff --git a/src/windows/wslc/services/VolumeService.cpp b/src/windows/wslc/services/VolumeService.cpp
index c56da8fdc..1ae3ea2ec 100644
--- a/src/windows/wslc/services/VolumeService.cpp
+++ b/src/windows/wslc/services/VolumeService.cpp
@@ -12,6 +12,7 @@ Module Name:
--*/
#include "VolumeService.h"
+#include "WarningCallback.h"
#include
#include
@@ -81,4 +82,42 @@ wsl::windows::common::wslc_schema::InspectVolume VolumeService::Inspect(models::
THROW_IF_FAILED(session.Get()->InspectVolume(name.c_str(), &output));
return FromJson(output.get());
}
+
+models::PruneVolumesResult VolumeService::Prune(models::Session& session, bool all, const std::vector>& filters)
+{
+ const bool hasExplicitAll = std::any_of(filters.begin(), filters.end(), [](const auto& f) { return f.first == "all"; });
+
+ std::vector filterEntries;
+ filterEntries.reserve(filters.size() + ((all && !hasExplicitAll) ? 1 : 0));
+ if (all && !hasExplicitAll)
+ {
+ filterEntries.push_back({.Key = "all", .Value = "true"});
+ }
+
+ for (const auto& [key, value] : filters)
+ {
+ filterEntries.push_back({.Key = key.c_str(), .Value = value.c_str()});
+ }
+
+ auto warningCallback = Microsoft::WRL::Make();
+ wil::unique_cotaskmem_array_ptr volumes;
+ ULONGLONG spaceReclaimed = 0;
+ THROW_IF_FAILED(session.Get()->PruneVolumes(
+ filterEntries.empty() ? nullptr : filterEntries.data(),
+ static_cast(filterEntries.size()),
+ warningCallback.Get(),
+ &volumes,
+ volumes.size_address(),
+ &spaceReclaimed));
+
+ models::PruneVolumesResult result;
+ result.SpaceReclaimed = spaceReclaimed;
+ result.PrunedVolumes.reserve(volumes.size());
+ for (auto ptr = volumes.get(), end = volumes.get() + volumes.size(); ptr != end; ++ptr)
+ {
+ result.PrunedVolumes.emplace_back(*ptr);
+ }
+
+ return result;
+}
} // namespace wsl::windows::wslc::services
diff --git a/src/windows/wslc/services/VolumeService.h b/src/windows/wslc/services/VolumeService.h
index bd0fe5441..4e468128e 100644
--- a/src/windows/wslc/services/VolumeService.h
+++ b/src/windows/wslc/services/VolumeService.h
@@ -24,5 +24,6 @@ struct VolumeService
static void Delete(models::Session& session, const std::string& name);
static std::vector List(models::Session& session);
static wsl::windows::common::wslc_schema::InspectVolume Inspect(models::Session& session, const std::string& name);
+ static models::PruneVolumesResult Prune(models::Session& session, bool all, const std::vector>& filters = {});
};
} // namespace wsl::windows::wslc::services
diff --git a/src/windows/wslc/tasks/VolumeTasks.cpp b/src/windows/wslc/tasks/VolumeTasks.cpp
index c608d4e5a..e472d61cc 100644
--- a/src/windows/wslc/tasks/VolumeTasks.cpp
+++ b/src/windows/wslc/tasks/VolumeTasks.cpp
@@ -194,4 +194,28 @@ void ListVolumes(CLIExecutionContext& context)
THROW_HR(E_UNEXPECTED);
}
}
+
+void PruneVolumes(CLIExecutionContext& context)
+{
+ WI_ASSERT(context.Data.Contains(Data::Session));
+ auto& session = context.Data.Get();
+
+ const bool all = context.Args.Contains(ArgType::All);
+
+ std::vector> filters;
+ for (const auto& value : context.Args.GetAll())
+ {
+ filters.push_back(validation::ParseFilter(value));
+ }
+
+ auto result = VolumeService::Prune(session, all, filters);
+
+ for (const auto& volumeName : result.PrunedVolumes)
+ {
+ PrintMessage(Localization::WSLCCLI_VolumePruneDeleted(MultiByteToWide(volumeName)));
+ }
+
+ PrintMessage(L"");
+ PrintMessage(Localization::WSLCCLI_VolumePruneSpaceReclaimed(wsl::shared::string::FormatBytes(result.SpaceReclaimed)));
+}
} // namespace wsl::windows::wslc::task
diff --git a/src/windows/wslc/tasks/VolumeTasks.h b/src/windows/wslc/tasks/VolumeTasks.h
index d6fe39479..bb2e3b7a2 100644
--- a/src/windows/wslc/tasks/VolumeTasks.h
+++ b/src/windows/wslc/tasks/VolumeTasks.h
@@ -21,4 +21,5 @@ void DeleteVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void GetVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void InspectVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
void ListVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
+void PruneVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
} // namespace wsl::windows::wslc::task
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumePruneTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumePruneTests.cpp
new file mode 100644
index 000000000..1b36a5833
--- /dev/null
+++ b/test/windows/wslc/e2e/WSLCE2EVolumePruneTests.cpp
@@ -0,0 +1,273 @@
+/*++
+
+Copyright (c) Microsoft. All rights reserved.
+
+Module Name:
+
+ WSLCE2EVolumePruneTests.cpp
+
+Abstract:
+
+ This file contains end-to-end tests for the WSLC volume prune command.
+--*/
+
+#include "precomp.h"
+#include "windows/Common.h"
+#include "WSLCExecutor.h"
+#include "WSLCE2EHelpers.h"
+
+namespace WSLCE2ETests {
+using namespace wsl::shared;
+
+class WSLCE2EVolumePruneTests
+{
+ WSLC_TEST_CLASS(WSLCE2EVolumePruneTests)
+
+ TEST_CLASS_SETUP(ClassSetup)
+ {
+ EnsureImageIsLoaded(DebianImage);
+ CleanUpAllTestState();
+ return true;
+ }
+
+ TEST_METHOD_SETUP(MethodSetup)
+ {
+ CleanUpAllTestState();
+ return true;
+ }
+
+ TEST_CLASS_CLEANUP(ClassCleanup)
+ {
+ CleanUpAllTestState();
+ EnsureImageIsDeleted(DebianImage);
+ return true;
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_HelpCommand)
+ {
+ const auto result = RunWslc(L"volume prune --help");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_NoVolumes)
+ {
+ // Prune when no volumes exist should succeed and report a reclaimed-space line.
+ const auto result = RunWslc(L"volume prune");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"Total reclaimed space:"));
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_NoAllFlag_PreservesNamedVolumes)
+ {
+ RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+
+ auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(TestVolumeName); });
+
+ const auto result = RunWslc(L"volume prune");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto output = result.GetStdoutLines();
+ VERIFY_ARE_EQUAL(2u, output.size());
+ VERIFY_ARE_EQUAL(output[0], L"");
+ VERIFY_ARE_NOT_EQUAL(std::wstring::npos, output[1].find(L"Total reclaimed space:"));
+
+ VerifyVolumeIsListed(TestVolumeName);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_AllFlag_RemovesNamedVolume)
+ {
+ RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+
+ auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(TestVolumeName); });
+
+ const auto result = RunWslc(L"volume prune --all");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto output = result.GetStdoutLines();
+ VERIFY_ARE_EQUAL(3u, output.size());
+ VERIFY_ARE_NOT_EQUAL(std::wstring::npos, output[0].find(std::format(L"Deleted: {}", TestVolumeName)));
+ VERIFY_ARE_EQUAL(output[1], L"");
+ VERIFY_ARE_NOT_EQUAL(std::wstring::npos, output[2].find(L"Total reclaimed space:"));
+
+ VerifyVolumeIsNotListed(TestVolumeName);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_AllFlag_RemovesMultipleVolumes)
+ {
+ RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ RunWslc(std::format(L"volume create {}", TestVolumeName2)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+ VerifyVolumeIsListed(TestVolumeName2);
+
+ auto cleanup = wil::scope_exit([&]() {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ });
+
+ const auto result = RunWslc(L"volume prune --all");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)));
+ VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName2)));
+
+ VerifyVolumeIsNotListed(TestVolumeName);
+ VerifyVolumeIsNotListed(TestVolumeName2);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_InUseVolume_Preserved)
+ {
+ RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+
+ // Start a container that holds the volume open.
+ RunWslc(std::format(
+ L"container run -d --name {} -v {}:/data {} sleep infinity", WslcContainerName, TestVolumeName, DebianImage.NameAndTag()))
+ .Verify({.Stderr = L"", .ExitCode = 0});
+
+ auto cleanup = wil::scope_exit([&]() {
+ EnsureContainerDoesNotExist(WslcContainerName);
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ });
+
+ const auto result = RunWslc(L"volume prune --all");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VERIFY_IS_FALSE(
+ result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)),
+ L"Volume in use by a running container must not be pruned");
+
+ VerifyVolumeIsListed(TestVolumeName);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_LabelFilter_PreservesNonMatchingVolume)
+ {
+ RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+
+ auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(TestVolumeName); });
+
+ // A label filter that does not match the volume should preserve it
+ const auto filteredPrune = RunWslc(L"volume prune --all --filter label=wslc.test.never=present");
+ filteredPrune.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_IS_FALSE(
+ filteredPrune.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)),
+ L"Filtered prune should not have deleted the non-matching volume");
+ VerifyVolumeIsListed(TestVolumeName);
+
+ // Subsequent unfiltered prune --all should still remove it, proving
+ // the filter was the reason it survived.
+ const auto unfilteredPrune = RunWslc(L"volume prune --all");
+ unfilteredPrune.Verify({.Stderr = L"", .ExitCode = 0});
+ VERIFY_IS_TRUE(unfilteredPrune.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)));
+ VerifyVolumeIsNotListed(TestVolumeName);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_LabelFilter_MatchingValueIsDeleted)
+ {
+ RunWslc(std::format(L"volume create --label wslc.test.prune=keep {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ RunWslc(std::format(L"volume create {}", TestVolumeName2)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+ VerifyVolumeIsListed(TestVolumeName2);
+
+ auto cleanup = wil::scope_exit([&]() {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ });
+
+ const auto result = RunWslc(L"volume prune --all --filter label=wslc.test.prune=keep");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)));
+ VERIFY_IS_FALSE(
+ result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName2)),
+ L"Volume without the matching label must not be deleted");
+
+ VerifyVolumeIsNotListed(TestVolumeName);
+ VerifyVolumeIsListed(TestVolumeName2);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_NegatedLabelFilter_PreservesLabeledVolume)
+ {
+ RunWslc(std::format(L"volume create --label wslc.test.keep=yes {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
+ RunWslc(std::format(L"volume create {}", TestVolumeName2)).Verify({.Stderr = L"", .ExitCode = 0});
+ VerifyVolumeIsListed(TestVolumeName);
+ VerifyVolumeIsListed(TestVolumeName2);
+
+ auto cleanup = wil::scope_exit([&]() {
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ });
+
+ const auto result = RunWslc(L"volume prune --all --filter label!=wslc.test.keep");
+ result.Verify({.Stderr = L"", .ExitCode = 0});
+
+ VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName2)));
+ VERIFY_IS_FALSE(
+ result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)),
+ L"Labeled volume must be preserved when prune negates that label");
+
+ VerifyVolumeIsListed(TestVolumeName);
+ VerifyVolumeIsNotListed(TestVolumeName2);
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_Filter_MalformedValue)
+ {
+ const auto result = RunWslc(L"volume prune --filter label");
+ result.Verify({.Stdout = GetHelpMessage(), .Stderr = Localization::WSLCCLI_InvalidFilterError(L"label") + L"\r\n", .ExitCode = 1});
+ }
+
+ WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_Filter_InvalidKey)
+ {
+ const auto result = RunWslc(L"volume prune --filter color=red");
+ result.Verify({.Stdout = L"", .Stderr = L"invalid filter 'color'\r\nError code: E_INVALIDARG\r\n", .ExitCode = 1});
+ }
+
+private:
+ const TestImage& DebianImage = DebianTestImage();
+ const std::wstring TestVolumeName = L"wslc-e2e-volume-prune";
+ const std::wstring TestVolumeName2 = L"wslc-e2e-volume-prune-2";
+ const std::wstring WslcContainerName = L"wslc-volume-prune-test-container";
+
+ void CleanUpAllTestState()
+ {
+ EnsureContainerDoesNotExist(WslcContainerName);
+ EnsureVolumeDoesNotExist(TestVolumeName);
+ EnsureVolumeDoesNotExist(TestVolumeName2);
+ }
+
+ std::wstring GetHelpMessage() const
+ {
+ std::wstringstream output;
+ output << GetWslcHeader() //
+ << GetDescription() //
+ << GetUsage() //
+ << GetAvailableOptions();
+ return output.str();
+ }
+
+ std::wstring GetDescription() const
+ {
+ return Localization::WSLCCLI_VolumePruneLongDesc() + L"\r\n\r\n";
+ }
+
+ std::wstring GetUsage() const
+ {
+ return L"Usage: wslc volume prune []\r\n\r\n";
+ }
+
+ std::wstring GetAvailableOptions() const
+ {
+ std::wstringstream options;
+ options << L"The following options are available:\r\n"
+ << L" -a,--all " << Localization::WSLCCLI_VolumePruneAllArgDescription() << L"\r\n"
+ << L" -f,--filter " << Localization::WSLCCLI_FilterArgDescription() << L"\r\n"
+ << L" --session " << Localization::WSLCCLI_SessionIdArgDescription() << L"\r\n"
+ << L" -?,--help " << Localization::WSLCCLI_HelpArgDescription() << L"\r\n"
+ << L"\r\n";
+ return options.str();
+ }
+};
+} // namespace WSLCE2ETests
diff --git a/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp b/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
index 123a801ab..d271922f9 100644
--- a/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
+++ b/test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
@@ -71,6 +71,7 @@ class WSLCE2EVolumeTests
{L"remove", Localization::WSLCCLI_VolumeRemoveDesc()},
{L"inspect", Localization::WSLCCLI_VolumeInspectDesc()},
{L"list", Localization::WSLCCLI_VolumeListDesc()},
+ {L"prune", Localization::WSLCCLI_VolumePruneDesc()},
};
size_t maxLen = 0;