@samitouri / QOSAMI-WSL / commits / 3973c863

[CLI] Initial support for volume prune (#40737)

* Init volume prune * Clang format * Fix test * Update tests * Update unit

AmirMS committed Jun 12, 2026 at 23:50 UTC 3973c8632cf97a75192e2631e1b6c418b17615e6
13 files changed +444 -4
localization/strings/en-US/Resources.resw
+18
@@ -3100,6 +3100,24 @@ On first run, creates the file with all settings commented out at their defaults
3100 <data name="WSLCCLI_VolumeListLongDesc" xml:space="preserve">
3101 <value>Lists all volumes in the session.</value>
3102 </data>
3103 + <data name="WSLCCLI_VolumePruneDesc" xml:space="preserve">
3104 + <value>Remove unused local volumes.</value>
3105 + </data>
3106 + <data name="WSLCCLI_VolumePruneLongDesc" xml:space="preserve">
3107 + <value>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.</value>
3108 + <comment>{Locked="--all "}Command line arguments, file names and string inserts should not be translated</comment>
3109 + </data>
3110 + <data name="WSLCCLI_VolumePruneAllArgDescription" xml:space="preserve">
3111 + <value>Remove all unused volumes, not just anonymous ones.</value>
3112 + </data>
3113 + <data name="WSLCCLI_VolumePruneDeleted" xml:space="preserve">
3114 + <value>Deleted: {}</value>
3115 + <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3116 + </data>
3117 + <data name="WSLCCLI_VolumePruneSpaceReclaimed" xml:space="preserve">
3118 + <value>Total reclaimed space: {}</value>
3119 + <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3120 + </data>
3121 <data name="WSLCCLI_VolumeNameArgDescription" xml:space="preserve">
3122 <value>Volume name</value>
3123 </data>
src/windows/wslc/arguments/ArgumentValidation.cpp
+12 -4
@@ -182,10 +182,7 @@ void ValidateFilter(const std::vector<std::wstring>& values)
182 {
183 for (const auto& value : values)
184 {
185 - if (value.find(L'=') == std::wstring::npos)
186 - {
187 - throw ArgumentException(Localization::WSLCCLI_InvalidFilterError(value));
188 - }
185 + std::ignore = ParseFilter(value);
186 }
187 }
188
@@ -422,4 +419,15 @@ std::pair<std::string, std::string> ParseDriverOption(const std::wstring& value)
419 return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
420 }
421
422 +std::pair<std::string, std::string> ParseFilter(const std::wstring& value)
423 +{
424 + auto pos = value.find(L'=');
425 + if (pos == std::wstring::npos)
426 + {
427 + throw ArgumentException(Localization::WSLCCLI_InvalidFilterError(value));
428 + }
429 +
430 + return {WideToMultiByte(value.substr(0, pos)), WideToMultiByte(value.substr(pos + 1))};
431 +}
432 +
433 } // namespace wsl::windows::wslc::validation
src/windows/wslc/arguments/ArgumentValidation.h
+1
@@ -83,5 +83,6 @@ void ValidateFilter(const std::vector<std::wstring>& values);
83
84 std::pair<std::string, std::string> ParseLabel(const std::wstring& value);
85 std::pair<std::string, std::string> ParseDriverOption(const std::wstring& value);
86 +std::pair<std::string, std::string> ParseFilter(const std::wstring& value);
87
88 } // namespace wsl::windows::wslc::validation
src/windows/wslc/commands/VolumeCommand.cpp
+1
@@ -26,6 +26,7 @@ std::vector<std::unique_ptr<Command>> VolumeCommand::GetCommands() const
26 commands.push_back(std::make_unique<VolumeRemoveCommand>(FullName()));
27 commands.push_back(std::make_unique<VolumeInspectCommand>(FullName()));
28 commands.push_back(std::make_unique<VolumeListCommand>(FullName()));
29 + commands.push_back(std::make_unique<VolumePruneCommand>(FullName()));
30 return commands;
31 }
32
src/windows/wslc/commands/VolumeCommand.h
+15
@@ -92,4 +92,19 @@ protected:
92 void ValidateArgumentsInternal(const ArgMap& execArgs) const override;
93 void ExecuteInternal(CLIExecutionContext& context) const override;
94 };
95 +
96 +// Prune Command
97 +struct VolumePruneCommand final : public Command
98 +{
99 + constexpr static std::wstring_view CommandName = L"prune";
100 + VolumePruneCommand(const std::wstring& parent) : Command(CommandName, parent)
101 + {
102 + }
103 + std::vector<Argument> GetArguments() const override;
104 + std::wstring ShortDescription() const override;
105 + std::wstring LongDescription() const override;
106 +
107 +protected:
108 + void ExecuteInternal(CLIExecutionContext& context) const override;
109 +};
110 } // namespace wsl::windows::wslc
src/windows/wslc/commands/VolumePruneCommand.cpp new
+52
@@ -0,0 +1,52 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + VolumePruneCommand.cpp
8 +
9 +Abstract:
10 +
11 + Implementation of command execution logic.
12 +
13 +--*/
14 +
15 +#include "VolumeCommand.h"
16 +#include "CLIExecutionContext.h"
17 +#include "SessionTasks.h"
18 +#include "VolumeTasks.h"
19 +#include "Task.h"
20 +
21 +using namespace wsl::windows::wslc::execution;
22 +using namespace wsl::windows::wslc::task;
23 +using namespace wsl::shared;
24 +
25 +namespace wsl::windows::wslc {
26 +// Volume Prune Command
27 +std::vector<Argument> VolumePruneCommand::GetArguments() const
28 +{
29 + return {
30 + Argument::Create(ArgType::All, std::nullopt, std::nullopt, Localization::WSLCCLI_VolumePruneAllArgDescription()),
31 + Argument::Create(ArgType::Filter, false, NO_LIMIT),
32 + Argument::Create(ArgType::Session),
33 + };
34 +}
35 +
36 +std::wstring VolumePruneCommand::ShortDescription() const
37 +{
38 + return Localization::WSLCCLI_VolumePruneDesc();
39 +}
40 +
41 +std::wstring VolumePruneCommand::LongDescription() const
42 +{
43 + return Localization::WSLCCLI_VolumePruneLongDesc();
44 +}
45 +
46 +void VolumePruneCommand::ExecuteInternal(CLIExecutionContext& context) const
47 +{
48 + context //
49 + << CreateSession //
50 + << PruneVolumes;
51 +}
52 +} // namespace wsl::windows::wslc
src/windows/wslc/services/VolumeModel.h
+6
@@ -27,4 +27,10 @@ struct CreateVolumeOptions
27 std::vector<std::pair<std::string, std::string>> Labels{};
28 };
29
30 +struct PruneVolumesResult
31 +{
32 + std::vector<std::string> PrunedVolumes;
33 + ULONGLONG SpaceReclaimed{};
34 +};
35 +
36 } // namespace wsl::windows::wslc::models
src/windows/wslc/services/VolumeService.cpp
+39
@@ -12,6 +12,7 @@ Abstract:
12
13 --*/
14 #include "VolumeService.h"
15 +#include "WarningCallback.h"
16 #include <wslutil.h>
17 #include <wslc.h>
18
@@ -81,4 +82,42 @@ wsl::windows::common::wslc_schema::InspectVolume VolumeService::Inspect(models::
82 THROW_IF_FAILED(session.Get()->InspectVolume(name.c_str(), &output));
83 return FromJson<wsl::windows::common::wslc_schema::InspectVolume>(output.get());
84 }
85 +
86 +models::PruneVolumesResult VolumeService::Prune(models::Session& session, bool all, const std::vector<std::pair<std::string, std::string>>& filters)
87 +{
88 + const bool hasExplicitAll = std::any_of(filters.begin(), filters.end(), [](const auto& f) { return f.first == "all"; });
89 +
90 + std::vector<WSLCFilter> filterEntries;
91 + filterEntries.reserve(filters.size() + ((all && !hasExplicitAll) ? 1 : 0));
92 + if (all && !hasExplicitAll)
93 + {
94 + filterEntries.push_back({.Key = "all", .Value = "true"});
95 + }
96 +
97 + for (const auto& [key, value] : filters)
98 + {
99 + filterEntries.push_back({.Key = key.c_str(), .Value = value.c_str()});
100 + }
101 +
102 + auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
103 + wil::unique_cotaskmem_array_ptr<WSLCVolumeName> volumes;
104 + ULONGLONG spaceReclaimed = 0;
105 + THROW_IF_FAILED(session.Get()->PruneVolumes(
106 + filterEntries.empty() ? nullptr : filterEntries.data(),
107 + static_cast<ULONG>(filterEntries.size()),
108 + warningCallback.Get(),
109 + &volumes,
110 + volumes.size_address<ULONG>(),
111 + &spaceReclaimed));
112 +
113 + models::PruneVolumesResult result;
114 + result.SpaceReclaimed = spaceReclaimed;
115 + result.PrunedVolumes.reserve(volumes.size());
116 + for (auto ptr = volumes.get(), end = volumes.get() + volumes.size(); ptr != end; ++ptr)
117 + {
118 + result.PrunedVolumes.emplace_back(*ptr);
119 + }
120 +
121 + return result;
122 +}
123 } // namespace wsl::windows::wslc::services
src/windows/wslc/services/VolumeService.h
+1
@@ -24,5 +24,6 @@ struct VolumeService
24 static void Delete(models::Session& session, const std::string& name);
25 static std::vector<WSLCVolumeInformation> List(models::Session& session);
26 static wsl::windows::common::wslc_schema::InspectVolume Inspect(models::Session& session, const std::string& name);
27 + static models::PruneVolumesResult Prune(models::Session& session, bool all, const std::vector<std::pair<std::string, std::string>>& filters = {});
28 };
29 } // namespace wsl::windows::wslc::services
src/windows/wslc/tasks/VolumeTasks.cpp
+24
@@ -194,4 +194,28 @@ void ListVolumes(CLIExecutionContext& context)
194 THROW_HR(E_UNEXPECTED);
195 }
196 }
197 +
198 +void PruneVolumes(CLIExecutionContext& context)
199 +{
200 + WI_ASSERT(context.Data.Contains(Data::Session));
201 + auto& session = context.Data.Get<Data::Session>();
202 +
203 + const bool all = context.Args.Contains(ArgType::All);
204 +
205 + std::vector<std::pair<std::string, std::string>> filters;
206 + for (const auto& value : context.Args.GetAll<ArgType::Filter>())
207 + {
208 + filters.push_back(validation::ParseFilter(value));
209 + }
210 +
211 + auto result = VolumeService::Prune(session, all, filters);
212 +
213 + for (const auto& volumeName : result.PrunedVolumes)
214 + {
215 + PrintMessage(Localization::WSLCCLI_VolumePruneDeleted(MultiByteToWide(volumeName)));
216 + }
217 +
218 + PrintMessage(L"");
219 + PrintMessage(Localization::WSLCCLI_VolumePruneSpaceReclaimed(wsl::shared::string::FormatBytes(result.SpaceReclaimed)));
220 +}
221 } // namespace wsl::windows::wslc::task
src/windows/wslc/tasks/VolumeTasks.h
+1
@@ -21,4 +21,5 @@ void DeleteVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
21 void GetVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
22 void InspectVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
23 void ListVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
24 +void PruneVolumes(wsl::windows::wslc::execution::CLIExecutionContext& context);
25 } // namespace wsl::windows::wslc::task
test/windows/wslc/e2e/WSLCE2EVolumePruneTests.cpp new
+273
@@ -0,0 +1,273 @@
1 +/*++
2 +
3 +Copyright (c) Microsoft. All rights reserved.
4 +
5 +Module Name:
6 +
7 + WSLCE2EVolumePruneTests.cpp
8 +
9 +Abstract:
10 +
11 + This file contains end-to-end tests for the WSLC volume prune command.
12 +--*/
13 +
14 +#include "precomp.h"
15 +#include "windows/Common.h"
16 +#include "WSLCExecutor.h"
17 +#include "WSLCE2EHelpers.h"
18 +
19 +namespace WSLCE2ETests {
20 +using namespace wsl::shared;
21 +
22 +class WSLCE2EVolumePruneTests
23 +{
24 + WSLC_TEST_CLASS(WSLCE2EVolumePruneTests)
25 +
26 + TEST_CLASS_SETUP(ClassSetup)
27 + {
28 + EnsureImageIsLoaded(DebianImage);
29 + CleanUpAllTestState();
30 + return true;
31 + }
32 +
33 + TEST_METHOD_SETUP(MethodSetup)
34 + {
35 + CleanUpAllTestState();
36 + return true;
37 + }
38 +
39 + TEST_CLASS_CLEANUP(ClassCleanup)
40 + {
41 + CleanUpAllTestState();
42 + EnsureImageIsDeleted(DebianImage);
43 + return true;
44 + }
45 +
46 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_HelpCommand)
47 + {
48 + const auto result = RunWslc(L"volume prune --help");
49 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = L"", .ExitCode = 0});
50 + }
51 +
52 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_NoVolumes)
53 + {
54 + // Prune when no volumes exist should succeed and report a reclaimed-space line.
55 + const auto result = RunWslc(L"volume prune");
56 + result.Verify({.Stderr = L"", .ExitCode = 0});
57 +
58 + VERIFY_IS_TRUE(result.StdoutContainsSubstring(L"Total reclaimed space:"));
59 + }
60 +
61 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_NoAllFlag_PreservesNamedVolumes)
62 + {
63 + RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
64 + VerifyVolumeIsListed(TestVolumeName);
65 +
66 + auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(TestVolumeName); });
67 +
68 + const auto result = RunWslc(L"volume prune");
69 + result.Verify({.Stderr = L"", .ExitCode = 0});
70 +
71 + auto output = result.GetStdoutLines();
72 + VERIFY_ARE_EQUAL(2u, output.size());
73 + VERIFY_ARE_EQUAL(output[0], L"");
74 + VERIFY_ARE_NOT_EQUAL(std::wstring::npos, output[1].find(L"Total reclaimed space:"));
75 +
76 + VerifyVolumeIsListed(TestVolumeName);
77 + }
78 +
79 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_AllFlag_RemovesNamedVolume)
80 + {
81 + RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
82 + VerifyVolumeIsListed(TestVolumeName);
83 +
84 + auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(TestVolumeName); });
85 +
86 + const auto result = RunWslc(L"volume prune --all");
87 + result.Verify({.Stderr = L"", .ExitCode = 0});
88 +
89 + auto output = result.GetStdoutLines();
90 + VERIFY_ARE_EQUAL(3u, output.size());
91 + VERIFY_ARE_NOT_EQUAL(std::wstring::npos, output[0].find(std::format(L"Deleted: {}", TestVolumeName)));
92 + VERIFY_ARE_EQUAL(output[1], L"");
93 + VERIFY_ARE_NOT_EQUAL(std::wstring::npos, output[2].find(L"Total reclaimed space:"));
94 +
95 + VerifyVolumeIsNotListed(TestVolumeName);
96 + }
97 +
98 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_AllFlag_RemovesMultipleVolumes)
99 + {
100 + RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
101 + RunWslc(std::format(L"volume create {}", TestVolumeName2)).Verify({.Stderr = L"", .ExitCode = 0});
102 + VerifyVolumeIsListed(TestVolumeName);
103 + VerifyVolumeIsListed(TestVolumeName2);
104 +
105 + auto cleanup = wil::scope_exit([&]() {
106 + EnsureVolumeDoesNotExist(TestVolumeName);
107 + EnsureVolumeDoesNotExist(TestVolumeName2);
108 + });
109 +
110 + const auto result = RunWslc(L"volume prune --all");
111 + result.Verify({.Stderr = L"", .ExitCode = 0});
112 +
113 + VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)));
114 + VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName2)));
115 +
116 + VerifyVolumeIsNotListed(TestVolumeName);
117 + VerifyVolumeIsNotListed(TestVolumeName2);
118 + }
119 +
120 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_InUseVolume_Preserved)
121 + {
122 + RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
123 + VerifyVolumeIsListed(TestVolumeName);
124 +
125 + // Start a container that holds the volume open.
126 + RunWslc(std::format(
127 + L"container run -d --name {} -v {}:/data {} sleep infinity", WslcContainerName, TestVolumeName, DebianImage.NameAndTag()))
128 + .Verify({.Stderr = L"", .ExitCode = 0});
129 +
130 + auto cleanup = wil::scope_exit([&]() {
131 + EnsureContainerDoesNotExist(WslcContainerName);
132 + EnsureVolumeDoesNotExist(TestVolumeName);
133 + });
134 +
135 + const auto result = RunWslc(L"volume prune --all");
136 + result.Verify({.Stderr = L"", .ExitCode = 0});
137 +
138 + VERIFY_IS_FALSE(
139 + result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)),
140 + L"Volume in use by a running container must not be pruned");
141 +
142 + VerifyVolumeIsListed(TestVolumeName);
143 + }
144 +
145 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_LabelFilter_PreservesNonMatchingVolume)
146 + {
147 + RunWslc(std::format(L"volume create {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
148 + VerifyVolumeIsListed(TestVolumeName);
149 +
150 + auto cleanup = wil::scope_exit([&]() { EnsureVolumeDoesNotExist(TestVolumeName); });
151 +
152 + // A label filter that does not match the volume should preserve it
153 + const auto filteredPrune = RunWslc(L"volume prune --all --filter label=wslc.test.never=present");
154 + filteredPrune.Verify({.Stderr = L"", .ExitCode = 0});
155 + VERIFY_IS_FALSE(
156 + filteredPrune.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)),
157 + L"Filtered prune should not have deleted the non-matching volume");
158 + VerifyVolumeIsListed(TestVolumeName);
159 +
160 + // Subsequent unfiltered prune --all should still remove it, proving
161 + // the filter was the reason it survived.
162 + const auto unfilteredPrune = RunWslc(L"volume prune --all");
163 + unfilteredPrune.Verify({.Stderr = L"", .ExitCode = 0});
164 + VERIFY_IS_TRUE(unfilteredPrune.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)));
165 + VerifyVolumeIsNotListed(TestVolumeName);
166 + }
167 +
168 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_LabelFilter_MatchingValueIsDeleted)
169 + {
170 + RunWslc(std::format(L"volume create --label wslc.test.prune=keep {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
171 + RunWslc(std::format(L"volume create {}", TestVolumeName2)).Verify({.Stderr = L"", .ExitCode = 0});
172 + VerifyVolumeIsListed(TestVolumeName);
173 + VerifyVolumeIsListed(TestVolumeName2);
174 +
175 + auto cleanup = wil::scope_exit([&]() {
176 + EnsureVolumeDoesNotExist(TestVolumeName);
177 + EnsureVolumeDoesNotExist(TestVolumeName2);
178 + });
179 +
180 + const auto result = RunWslc(L"volume prune --all --filter label=wslc.test.prune=keep");
181 + result.Verify({.Stderr = L"", .ExitCode = 0});
182 +
183 + VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)));
184 + VERIFY_IS_FALSE(
185 + result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName2)),
186 + L"Volume without the matching label must not be deleted");
187 +
188 + VerifyVolumeIsNotListed(TestVolumeName);
189 + VerifyVolumeIsListed(TestVolumeName2);
190 + }
191 +
192 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_NegatedLabelFilter_PreservesLabeledVolume)
193 + {
194 + RunWslc(std::format(L"volume create --label wslc.test.keep=yes {}", TestVolumeName)).Verify({.Stderr = L"", .ExitCode = 0});
195 + RunWslc(std::format(L"volume create {}", TestVolumeName2)).Verify({.Stderr = L"", .ExitCode = 0});
196 + VerifyVolumeIsListed(TestVolumeName);
197 + VerifyVolumeIsListed(TestVolumeName2);
198 +
199 + auto cleanup = wil::scope_exit([&]() {
200 + EnsureVolumeDoesNotExist(TestVolumeName);
201 + EnsureVolumeDoesNotExist(TestVolumeName2);
202 + });
203 +
204 + const auto result = RunWslc(L"volume prune --all --filter label!=wslc.test.keep");
205 + result.Verify({.Stderr = L"", .ExitCode = 0});
206 +
207 + VERIFY_IS_TRUE(result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName2)));
208 + VERIFY_IS_FALSE(
209 + result.StdoutContainsLine(std::format(L"Deleted: {}", TestVolumeName)),
210 + L"Labeled volume must be preserved when prune negates that label");
211 +
212 + VerifyVolumeIsListed(TestVolumeName);
213 + VerifyVolumeIsNotListed(TestVolumeName2);
214 + }
215 +
216 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_Filter_MalformedValue)
217 + {
218 + const auto result = RunWslc(L"volume prune --filter label");
219 + result.Verify({.Stdout = GetHelpMessage(), .Stderr = Localization::WSLCCLI_InvalidFilterError(L"label") + L"\r\n", .ExitCode = 1});
220 + }
221 +
222 + WSLC_TEST_METHOD(WSLCE2E_Volume_Prune_Filter_InvalidKey)
223 + {
224 + const auto result = RunWslc(L"volume prune --filter color=red");
225 + result.Verify({.Stdout = L"", .Stderr = L"invalid filter 'color'\r\nError code: E_INVALIDARG\r\n", .ExitCode = 1});
226 + }
227 +
228 +private:
229 + const TestImage& DebianImage = DebianTestImage();
230 + const std::wstring TestVolumeName = L"wslc-e2e-volume-prune";
231 + const std::wstring TestVolumeName2 = L"wslc-e2e-volume-prune-2";
232 + const std::wstring WslcContainerName = L"wslc-volume-prune-test-container";
233 +
234 + void CleanUpAllTestState()
235 + {
236 + EnsureContainerDoesNotExist(WslcContainerName);
237 + EnsureVolumeDoesNotExist(TestVolumeName);
238 + EnsureVolumeDoesNotExist(TestVolumeName2);
239 + }
240 +
241 + std::wstring GetHelpMessage() const
242 + {
243 + std::wstringstream output;
244 + output << GetWslcHeader() //
245 + << GetDescription() //
246 + << GetUsage() //
247 + << GetAvailableOptions();
248 + return output.str();
249 + }
250 +
251 + std::wstring GetDescription() const
252 + {
253 + return Localization::WSLCCLI_VolumePruneLongDesc() + L"\r\n\r\n";
254 + }
255 +
256 + std::wstring GetUsage() const
257 + {
258 + return L"Usage: wslc volume prune [<options>]\r\n\r\n";
259 + }
260 +
261 + std::wstring GetAvailableOptions() const
262 + {
263 + std::wstringstream options;
264 + options << L"The following options are available:\r\n"
265 + << L" -a,--all " << Localization::WSLCCLI_VolumePruneAllArgDescription() << L"\r\n"
266 + << L" -f,--filter " << Localization::WSLCCLI_FilterArgDescription() << L"\r\n"
267 + << L" --session " << Localization::WSLCCLI_SessionIdArgDescription() << L"\r\n"
268 + << L" -?,--help " << Localization::WSLCCLI_HelpArgDescription() << L"\r\n"
269 + << L"\r\n";
270 + return options.str();
271 + }
272 +};
273 +} // namespace WSLCE2ETests
test/windows/wslc/e2e/WSLCE2EVolumeTests.cpp
+1
@@ -71,6 +71,7 @@ private:
71 {L"remove", Localization::WSLCCLI_VolumeRemoveDesc()},
72 {L"inspect", Localization::WSLCCLI_VolumeInspectDesc()},
73 {L"list", Localization::WSLCCLI_VolumeListDesc()},
74 + {L"prune", Localization::WSLCCLI_VolumePruneDesc()},
75 };
76
77 size_t maxLen = 0;