master
cpp 238 lines 9.29 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 /*++
4
5 Module Name:
6
7 WSLCE2EContainerStatsTests.cpp
8
9 Abstract:
10
11 This file contains end-to-end tests for the WSLC container stats command.
12 --*/
13
14 #include "precomp.h"
15 #include "windows/Common.h"
16 #include "WSLCExecutor.h"
17 #include "WSLCE2EHelpers.h"
18 #include "TestImageRegistry.h"
19
20 namespace WSLCE2ETests {
21
22 using namespace wsl::shared;
23 using namespace wsl::windows::common::string;
24
25 class WSLCE2EContainerStatsTests
26 {
27 WSLC_TEST_CLASS(WSLCE2EContainerStatsTests)
28
29 TEST_CLASS_SETUP(ClassSetup)
30 {
31 TestImageRegistry::Instance().EnsureLoaded(DebianImage);
32 return true;
33 }
34
35 TEST_CLASS_CLEANUP(ClassCleanup)
36 {
37 EnsureContainerDoesNotExist(WslcContainerName);
38 return true;
39 }
40
41 TEST_METHOD_SETUP(TestMethodSetup)
42 {
43 EnsureContainerDoesNotExist(WslcContainerName);
44 return true;
45 }
46
47 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_HelpCommand)
48 {
49 auto result = RunWslc(L"container stats --help");
50 result.Verify({.Stderr = L"", .ExitCode = 0});
51 }
52
53 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_NoContainers)
54 {
55 // With no running containers, stats should produce no output rows (header only or empty).
56 auto result = RunWslc(L"container stats");
57 result.Verify({.Stderr = L"", .ExitCode = 0});
58 VERIFY_ARE_EQUAL(
59 static_cast<size_t>(1), result.GetStdoutLines().size(), L"Expected only the header row when there are no containers");
60 }
61
62 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_RunningContainer_HasExpectedColumns)
63 {
64 auto runResult =
65 RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
66 runResult.Verify({.Stderr = L"", .ExitCode = 0});
67
68 auto result = RunWslc(L"container stats");
69 result.Verify({.Stderr = L"", .ExitCode = 0});
70
71 const auto lines = result.GetStdoutLines();
72 VERIFY_IS_TRUE(lines.size() >= 2, L"Expected header row and at least one data row");
73
74 // Verify the header row contains all expected column titles.
75 const auto& header = lines[0];
76 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderContainerId()));
77 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderName()));
78 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderCpuPercent()));
79 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderMemUsageLimit()));
80 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderMemPercent()));
81 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderNetIo()));
82 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderBlockIo()));
83 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, header.find(Localization::WSLCCLI_TableHeaderPids()));
84 }
85
86 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_RunningContainer_ContainerIdAndNamePresent)
87 {
88 auto runResult =
89 RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
90 runResult.Verify({.Stderr = L"", .ExitCode = 0});
91 const auto containerId = TruncateId(runResult.GetStdoutOneLine());
92 VERIFY_IS_FALSE(containerId.empty());
93
94 auto result = RunWslc(L"container stats");
95 result.Verify({.Stderr = L"", .ExitCode = 0});
96
97 // The data row must contain the truncated container ID and the container name.
98 bool foundContainer = false;
99 for (const auto& line : result.GetStdoutLines())
100 {
101 if (line.find(containerId) != std::wstring::npos)
102 {
103 foundContainer = true;
104 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, line.find(WslcContainerName));
105 break;
106 }
107 }
108
109 VERIFY_IS_TRUE(foundContainer, L"Container ID not found in stats output");
110 }
111
112 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_RunningContainer_NoTrunc)
113 {
114 auto runResult =
115 RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
116 runResult.Verify({.Stderr = L"", .ExitCode = 0});
117 const auto fullContainerId = runResult.GetStdoutOneLine();
118 VERIFY_IS_FALSE(fullContainerId.empty());
119
120 auto result = RunWslc(L"container stats --no-trunc");
121 result.Verify({.Stderr = L"", .ExitCode = 0});
122
123 // With --no-trunc the full container ID must appear.
124 bool foundContainer = false;
125 for (const auto& line : result.GetStdoutLines())
126 {
127 if (line.find(fullContainerId) != std::wstring::npos)
128 {
129 foundContainer = true;
130 break;
131 }
132 }
133
134 VERIFY_IS_TRUE(foundContainer, L"Full container ID not found in stats --no-trunc output");
135 }
136
137 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_SpecificContainerId)
138 {
139 auto runResult =
140 RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
141 runResult.Verify({.Stderr = L"", .ExitCode = 0});
142 const auto containerId = TruncateId(runResult.GetStdoutOneLine());
143 VERIFY_IS_FALSE(containerId.empty());
144
145 // Pass the container ID explicitly — only that container's row should appear.
146 auto result = RunWslc(std::format(L"container stats {}", containerId));
147 result.Verify({.Stderr = L"", .ExitCode = 0});
148
149 const auto lines = result.GetStdoutLines();
150 // Header + exactly one data row.
151 VERIFY_ARE_EQUAL(2u, lines.size());
152 VERIFY_ARE_NOT_EQUAL(std::wstring::npos, lines[1].find(containerId));
153 }
154
155 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_StoppedContainerExcluded)
156 {
157 // Create (but do not start) a container.
158 auto createResult = RunWslc(std::format(L"container create --name {} {}", WslcContainerName, DebianImage.NameAndTag()));
159 createResult.Verify({.Stderr = L"", .ExitCode = 0});
160 const auto containerId = TruncateId(createResult.GetStdoutOneLine());
161 VERIFY_IS_FALSE(containerId.empty());
162
163 // Stats without --all must not show non-running containers.
164 auto result = RunWslc(L"container stats");
165 result.Verify({.Stderr = L"", .ExitCode = 0});
166
167 for (const auto& line : result.GetStdoutLines())
168 {
169 VERIFY_ARE_EQUAL(std::wstring::npos, line.find(containerId));
170 }
171 }
172
173 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_AllFlag_IncludesStoppedContainers)
174 {
175 // Create (but do not start) a container.
176 auto createResult = RunWslc(std::format(L"container create --name {} {}", WslcContainerName, DebianImage.NameAndTag()));
177 createResult.Verify({.Stderr = L"", .ExitCode = 0});
178 const auto containerId = TruncateId(createResult.GetStdoutOneLine());
179 VERIFY_IS_FALSE(containerId.empty());
180
181 // Stats with --all must include non-running containers.
182 auto result = RunWslc(L"container stats --all");
183 result.Verify({.Stderr = L"", .ExitCode = 0});
184
185 bool foundContainer = false;
186 for (const auto& line : result.GetStdoutLines())
187 {
188 if (line.find(containerId) != std::wstring::npos)
189 {
190 foundContainer = true;
191 break;
192 }
193 }
194
195 VERIFY_IS_TRUE(foundContainer, L"Stopped container not found in stats --all output");
196 }
197
198 WSLC_TEST_METHOD(WSLCE2E_Container_Stats_JsonFormat)
199 {
200 // Run a container in the background
201 auto result = RunWslc(std::format(L"container run -d --name {} {} sleep infinity", WslcContainerName, DebianImage.NameAndTag()));
202 result.Verify({.Stderr = L"", .ExitCode = 0});
203 const auto containerId = result.GetStdoutOneLine();
204 VERIFY_IS_FALSE(containerId.empty());
205
206 // Get stats in JSON format
207 result = RunWslc(std::format(L"container stats --no-trunc --format json {}", containerId));
208 result.Verify({.Stderr = L"", .ExitCode = 0});
209
210 // Parse and validate the JSON output
211 const auto entries = ParseNdjsonOutput(result);
212 VERIFY_IS_GREATER_THAN_OR_EQUAL(entries.size(), 1U);
213
214 const auto& entry = entries[0];
215 VERIFY_IS_TRUE(entry.contains("ID"));
216 VERIFY_IS_TRUE(entry.contains("Name"));
217 VERIFY_IS_TRUE(entry.contains("CPUPerc"));
218 VERIFY_IS_TRUE(entry.contains("MemUsage"));
219 VERIFY_IS_TRUE(entry.contains("MemPerc"));
220 VERIFY_IS_TRUE(entry.contains("NetIO"));
221 VERIFY_IS_TRUE(entry.contains("BlockIO"));
222 VERIFY_IS_TRUE(entry.contains("PIDs"));
223
224 VERIFY_ARE_EQUAL(containerId, MultiByteToWide(entry["ID"].get<std::string>()));
225 VERIFY_IS_TRUE(entry["CPUPerc"].is_string());
226 VERIFY_IS_TRUE(entry["MemUsage"].is_string());
227 VERIFY_IS_TRUE(entry["MemPerc"].is_string());
228 VERIFY_IS_TRUE(entry["NetIO"].is_string());
229 VERIFY_IS_TRUE(entry["BlockIO"].is_string());
230 VERIFY_IS_TRUE(entry["PIDs"].is_number_unsigned());
231 }
232
233 private:
234 const std::wstring WslcContainerName = L"wslc-stats-test";
235 const TestImage& DebianImage = DebianTestImage();
236 };
237
238 } // namespace WSLCE2ETests