master
h 732 lines 22.7 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 Common.h
8
9 Abstract:
10
11 This file contains common definitions used for testing.
12
13 --*/
14
15 #pragma once
16
17 #include <WexTestClass.h>
18 #include <LogController.h>
19 #include <future>
20 #include <vector>
21 #include <string>
22 #include <thread>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include "precomp.h"
26 #include "lxsstest.h"
27 #include "wslutil.h"
28 #include "WslCoreConfig.h"
29
30 using namespace std::chrono_literals;
31
32 //
33 // N.B. This is also defined in 'lxtcommon.h' & 'lxsetup.ps1'. Update those
34 // files too, if the distro name changes here.
35 //
36 #define LXSS_DISTRO_NAME_TEST "test_distro"
37 #define LXSS_DISTRO_NAME_TEST_L WIDEN(LXSS_DISTRO_NAME_TEST)
38
39 #define LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE L"-u root -e rm /etc/wsl.conf"
40 #define LXSST_TESTS_INSTALL_COMMAND_LINE L"/bin/bash -c 'cd /data/test; ./build_tests.sh'"
41
42 //
43 // Test method declaration macros that tag tests with TAEF metadata for version-based selection.
44 // If the test version doesn't match the version passed to te.exe, the test is ignored.
45 //
46 #define WSL1_TEST_METHOD(_name) \
47 TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE(_name) \
48 TEST_METHOD_PROPERTY(L"Ignore[not(@Version=1)]", L"true") \
49 TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE() \
50 TEST_METHOD(_name)
51
52 #define WSL2_TEST_METHOD(_name) \
53 TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE(_name) \
54 TEST_METHOD_PROPERTY(L"Ignore[not(@Version=2)]", L"true") \
55 TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE() \
56 TEST_METHOD(_name)
57
58 #define WSLC_TEST_METHOD(_name) \
59 TAEF_BEGIN_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE(_name) \
60 TEST_METHOD_PROPERTY(L"Ignore[not(@Version=2)]", L"true") \
61 TAEF_END_TEST_METHOD_PROPERTIES_IN_CLASS_SCOPE() \
62 TEST_METHOD(_name)
63
64 // macro for skipping tests that are currently failing due to not yet being fully implemented
65 #define SKIP_TEST_NOT_IMPL() \
66 { \
67 LogSkipped("This test is skipped; not yet fully implemented"); \
68 return; \
69 }
70
71 #define WINDOWS_11_TEST_ONLY() \
72 if (!wsl::windows::common::helpers::IsWindows11OrAbove()) \
73 { \
74 LogSkipped("This test is only applicable to Windows 11 and above"); \
75 return; \
76 }
77
78 #define WSL_TEST_VERSION_REQUIRED(_version) \
79 if (wsl::windows::common::helpers::GetWindowsVersion().BuildNumber < _version) \
80 { \
81 LogSkipped("This test requires Windows version %u or later", _version); \
82 return; \
83 }
84
85 #define SKIP_TEST_ARM64() \
86 { \
87 if constexpr (wsl::shared::Arm64) \
88 { \
89 LogSkipped("This test is skipped for ARM64"); \
90 return; \
91 } \
92 }
93
94 #define SKIP_TEST_SERVER() \
95 { \
96 if (IsWindowsServer()) \
97 { \
98 LogSkipped("This test is skipped on Windows Server SKUs"); \
99 return; \
100 } \
101 }
102
103 #define SKIP_TEST_UNSTABLE() \
104 { \
105 LogSkipped("This test is skipped because it's unstable"); \
106 return; \
107 }
108
109 #define WSL_SETTINGS_TEST() \
110 if constexpr (!WSL_BUILD_WSL_SETTINGS) \
111 { \
112 LogSkipped("This test is skipped wslsettings wasn't built"); \
113 return; \
114 }
115
116 #define WSL_TEST_CLASS_PROPERTIES \
117 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"LxssManager.dll") \
118 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"LxssManagerProxyStub.dll") \
119 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslclient.dll") \
120 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslservice.exe") \
121 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"WslServiceProxyStub.dll") \
122 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslhost.exe") \
123 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslrelay.exe") \
124 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslconfig.exe") \
125 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wsl.exe") \
126 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslg.exe") \
127 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"msrdc.exe") \
128 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"msal.wsl.proxy.exe") \
129 TEST_CLASS_PROPERTY(L"BinaryUnderTest", L"wslcsession.exe")
130
131 #define WSL_TEST_CLASS(_name) \
132 BEGIN_TEST_CLASS(_name) \
133 WSL_TEST_CLASS_PROPERTIES \
134 END_TEST_CLASS()
135
136 #define WSLC_TEST_CLASS(_name) \
137 BEGIN_TEST_CLASS(_name) \
138 WSL_TEST_CLASS_PROPERTIES \
139 TEST_CLASS_PROPERTY(L"TestCategory", L"WSLC") \
140 END_TEST_CLASS()
141
142 //
143 // RAII Wrapper that prevents the UVM from timing out
144 //
145 class WslKeepAlive
146 {
147 public:
148 WslKeepAlive(HANDLE Token = nullptr);
149
150 ~WslKeepAlive();
151
152 WslKeepAlive(const WslKeepAlive&) = delete;
153 WslKeepAlive(WslKeepAlive&&) = delete;
154 const WslKeepAlive& operator=(WslKeepAlive&&) = delete;
155 const WslKeepAlive& operator=(WslKeepAlive&) = delete;
156
157 void Set();
158
159 void Run();
160
161 void Reset();
162
163 private:
164 wil::unique_handle m_write;
165 wil::unique_handle m_read;
166 std::thread m_thread;
167 std::optional<std::promise<void>> m_running;
168 HANDLE m_token = nullptr;
169 };
170
171 //
172 // RAII Wrapper for .wslconfig changes
173 //
174
175 class WslConfigChange
176 {
177 public:
178 WslConfigChange(const std::wstring& Content);
179
180 ~WslConfigChange();
181 WslConfigChange(const WslConfigChange&) = delete;
182 WslConfigChange(WslConfigChange&& other);
183 const WslConfigChange& operator=(WslConfigChange&&) = delete;
184 const WslConfigChange& operator=(WslConfigChange&) = delete;
185
186 static std::wstring Update(const std::wstring& Content);
187
188 private:
189 std::optional<std::wstring> m_originalContent;
190 };
191
192 //
193 // RAII wrapper for host file change.
194 //
195
196 class HostFileChange
197 {
198 public:
199 HostFileChange(const std::filesystem::path& Path, const std::string& NewContent);
200
201 ~HostFileChange();
202
203 NON_COPYABLE(HostFileChange);
204 NON_MOVABLE(HostFileChange);
205
206 void Update(const std::string& NewContent) const;
207
208 private:
209 std::filesystem::path m_path;
210 std::optional<std::string> m_originalContent;
211 };
212
213 template <typename T>
214 class RegistryKeyChange
215 {
216 public:
217 RegistryKeyChange(HKEY Hive, LPCWSTR Key, LPCWSTR Name, const T& Value) : m_hive(Hive), m_key(Key), m_value(Name)
218 {
219 m_originalValue = Get();
220
221 Set(Value);
222 }
223
224 ~RegistryKeyChange()
225 {
226 if (m_key != nullptr)
227 {
228 auto key = wsl::windows::common::registry::CreateKey(m_hive, m_key, KEY_ALL_ACCESS);
229
230 if (m_originalValue.has_value())
231 {
232 Set(m_originalValue.value());
233 }
234 else
235 {
236 wsl::windows::common::registry::DeleteKeyValue(key.get(), m_value.c_str());
237 }
238 }
239 }
240
241 wil::unique_hkey OpenKey()
242 {
243 return wsl::windows::common::registry::CreateKey(m_hive, m_key, KEY_ALL_ACCESS);
244 }
245
246 RegistryKeyChange(const RegistryKeyChange&) = delete;
247 RegistryKeyChange(RegistryKeyChange&& other) noexcept :
248 m_hive(other.m_hive), m_key(other.m_key), m_value(std::move(other.m_value)), m_originalValue(std::move(other.m_originalValue))
249 {
250 other.m_hive = nullptr;
251 other.m_key = nullptr;
252 }
253
254 RegistryKeyChange& operator=(RegistryKeyChange&& other)
255 {
256 if (this != &other)
257 {
258 m_hive = std::move(other.m_hive);
259 m_key = std::move(other.m_key);
260 m_value = std::move(other.m_value);
261 m_originalValue = std::move(other.m_originalValue);
262
263 other.m_hive = nullptr;
264 other.m_key = nullptr;
265 }
266
267 return *this;
268 }
269
270 const RegistryKeyChange& operator=(RegistryKeyChange&) = delete;
271
272 void Set(const T& Value)
273 {
274 auto key = wsl::windows::common::registry::CreateKey(m_hive, m_key, KEY_ALL_ACCESS);
275
276 if constexpr (std::is_same_v<std::remove_reference_t<T>, DWORD>)
277 {
278 wsl::windows::common::registry::WriteDword(key.get(), nullptr, m_value.c_str(), Value);
279 }
280 else if constexpr (std::is_same_v<std::remove_reference_t<T>, std::wstring>)
281 {
282 wsl::windows::common::registry::WriteString(key.get(), nullptr, m_value.c_str(), Value.c_str());
283 }
284 else
285 {
286 static_assert(sizeof(T) != sizeof(T));
287 }
288 }
289
290 auto Get() const
291 {
292
293 auto key = wsl::windows::common::registry::CreateKey(m_hive, m_key, KEY_ALL_ACCESS);
294
295 if constexpr (std::is_same_v<T, DWORD>)
296 {
297 DWORD Value = 0;
298 DWORD Size = sizeof(Value);
299 const auto Result = RegGetValueW(key.get(), nullptr, m_value.c_str(), RRF_RT_REG_DWORD, nullptr, &Value, &Size);
300 if (Result == ERROR_SUCCESS)
301 {
302 WI_ASSERT(Size == sizeof(Value));
303 return std::optional<DWORD>{Value};
304 }
305 else if ((Result == ERROR_PATH_NOT_FOUND) || (Result == ERROR_FILE_NOT_FOUND))
306 {
307 return std::optional<DWORD>{};
308 }
309 else
310 {
311 THROW_NTSTATUS(Result);
312 }
313 }
314 else if constexpr (std::is_same_v<std::remove_reference_t<T>, std::wstring>)
315 {
316 return wsl::windows::common::registry::ReadOptionalString(key.get(), nullptr, m_value.c_str());
317 }
318 else
319 {
320 static_assert(sizeof(T) != sizeof(T));
321 }
322 }
323
324 private:
325 HKEY m_hive = nullptr;
326 LPCWSTR m_key = nullptr;
327 std::wstring m_value;
328 std::optional<T> m_originalValue;
329 };
330
331 class ScopedEnvVariable
332 {
333 public:
334 // Captures any existing value and clears the variable.
335 explicit ScopedEnvVariable(const std::wstring& Name);
336
337 // Captures any existing value and sets the variable to Value.
338 ScopedEnvVariable(const std::wstring& Name, const std::wstring& Value);
339
340 // Restores the original value.
341 ~ScopedEnvVariable();
342
343 NON_COPYABLE(ScopedEnvVariable);
344 NON_MOVABLE(ScopedEnvVariable);
345
346 // Sets the variable to a new value.
347 void Set(const std::wstring& Value);
348
349 // Clears (unsets) the variable.
350 void Clear();
351
352 private:
353 std::wstring m_name;
354 std::optional<std::wstring> m_originalValue;
355 };
356
357 class UniqueWebServer
358 {
359 public:
360 UniqueWebServer(LPCWSTR Endpoint, LPCWSTR ResponseContent, UINT StatusCode = 200);
361 UniqueWebServer(LPCWSTR Endpoint, const std::filesystem::path& path);
362 ~UniqueWebServer();
363 UniqueWebServer(const UniqueWebServer&) = delete;
364 UniqueWebServer(UniqueWebServer&&) = delete;
365
366 UniqueWebServer& operator=(const UniqueWebServer&) = delete;
367 UniqueWebServer& operator=(UniqueWebServer&&) = delete;
368
369 private:
370 wil::unique_handle m_process;
371 };
372
373 class DistroFileChange
374 {
375 public:
376 DistroFileChange(LPCWSTR Path, bool exists = true);
377 ~DistroFileChange();
378 DistroFileChange(const DistroFileChange&) = delete;
379 DistroFileChange(DistroFileChange&&) = delete;
380 DistroFileChange& operator=(const DistroFileChange&) = delete;
381 DistroFileChange& operator=(DistroFileChange&&) = delete;
382
383 void SetContent(LPCWSTR Content);
384 void Delete();
385
386 private:
387 std::optional<std::wstring> m_originalContent;
388 LPCWSTR m_path{};
389 };
390
391 class PartialHandleRead
392 {
393 public:
394 NON_COPYABLE(PartialHandleRead);
395 NON_MOVABLE(PartialHandleRead);
396
397 PartialHandleRead(HANDLE Handle);
398 ~PartialHandleRead();
399
400 void Expect(const std::string& Expected);
401 void ExpectConsume(const std::string& Expected);
402 void ExpectClosed(DWORD Timeout = 60 * 1000);
403 void Stop();
404
405 std::string ReadBytes(size_t Length);
406 std::string ConsumeBytes(size_t Length);
407
408 std::string GetData() const;
409
410 private:
411 void Run();
412
413 HANDLE m_handle{};
414 mutable std::mutex m_mutex;
415 wil::unique_event m_exitEvent{wil::EventOptions::ManualReset};
416 std::thread m_thread;
417 std::string m_data;
418 };
419
420 //
421 // Structs and enums.
422 //
423
424 typedef struct _LXSS_TEST_LAUNCHER_TEST
425 {
426 ULONG NumberOfErrors;
427 ULONG NumberOfPasses;
428 } LXSS_TEST_LAUNCHER_TEST, *PLXSS_TEST_LAUNCHER_TEST;
429
430 typedef enum LXSS_TEST_LAUNCHER_MESSAGE_TYPE
431 {
432 LogInfoMessage,
433 LogErrorMessage,
434 LogPassMessage
435 } LXSS_TEST_LAUNCHER_MESSAGE_TYPE,
436 *PLXSS_TEST_LAUNCHER_MESSAGE_TYPE;
437
438 // from nttpapi.h - need to find a way to include later
439 typedef LARGE_INTEGER TP_TIMESTAMP, *PTP_TIMESTAMP;
440
441 std::pair<wil::unique_handle, wil::unique_handle> CreateSubprocessPipe(
442 bool inheritRead, bool inheritWrite, DWORD bufferSize = 0, _In_opt_ SECURITY_ATTRIBUTES* sa = nullptr);
443
444 std::pair<DWORD, DWORD> GetServiceState(SC_HANDLE service);
445
446 DWORD
447 LxsstuLaunchWsl(
448 _In_opt_ LPCWSTR Arguments,
449 _In_opt_ HANDLE StandardInput = nullptr,
450 _In_opt_ HANDLE StandardOutput = nullptr,
451 _In_opt_ HANDLE StandardError = nullptr,
452 _In_opt_ HANDLE Token = nullptr,
453 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);
454
455 DWORD
456 LxsstuLaunchWsl(
457 _In_opt_ const std::wstring& Arguments,
458 _In_opt_ HANDLE StandardInput = nullptr,
459 _In_opt_ HANDLE StandardOutput = nullptr,
460 _In_opt_ HANDLE StandardError = nullptr,
461 _In_opt_ HANDLE Token = nullptr);
462
463 std::pair<std::wstring, std::wstring> LxsstuLaunchWslAndCaptureOutput(
464 _In_ LPCWSTR Cmd,
465 _In_ int ExpectedExitCode = 0,
466 _In_opt_ HANDLE StandardInput = nullptr,
467 _In_opt_ HANDLE Token = nullptr,
468 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,
469 _In_ LPCWSTR Entrypoint = WSL_BINARY_NAME);
470
471 std::wstring LxssGenerateWslCommandLine(_In_opt_ LPCWSTR Arguments, _In_ LPCWSTR EntryPoint = WSL_BINARY_NAME);
472
473 std::pair<std::wstring, std::wstring> LxsstuLaunchWslAndCaptureOutput(
474 _In_ const std::wstring& Cmd,
475 _In_ int ExpectedExitCode = 0,
476 _In_opt_ HANDLE StandardInput = nullptr,
477 _In_opt_ HANDLE Token = nullptr,
478 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,
479 _In_ LPCWSTR EntryPoint = WSL_BINARY_NAME);
480
481 std::pair<std::wstring, std::wstring> LxsstuLaunchCommandAndCaptureOutput(
482 _In_ LPWSTR Cmd, _In_ LPCSTR StandardInput, _In_opt_ HANDLE Token = nullptr, _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);
483
484 std::tuple<std::wstring, std::wstring, int> LxsstuLaunchCommandAndCaptureOutputWithResult(
485 _In_ LPWSTR Cmd,
486 _In_opt_ HANDLE StandardInput = nullptr,
487 _In_opt_ HANDLE Token = nullptr,
488 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);
489
490 std::pair<std::wstring, std::wstring> LxsstuLaunchCommandAndCaptureOutput(
491 _In_ LPWSTR Cmd,
492 _In_ int ExpectedExitCode = 0,
493 _In_opt_ HANDLE StandardInput = nullptr,
494 _In_opt_ HANDLE Token = nullptr,
495 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);
496
497 DWORD
498 LxsstuRunCommand(
499 _In_ LPWSTR Command,
500 _In_opt_ HANDLE StandardInput = nullptr,
501 _In_opt_ HANDLE StandardOutput = nullptr,
502 _In_opt_ HANDLE StandardError = nullptr,
503 _In_opt_ HANDLE Token = nullptr,
504 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);
505
506 wil::unique_handle LxsstuStartProcess(
507 _In_ LPWSTR Command,
508 _In_opt_ HANDLE StandardInput = nullptr,
509 _In_opt_ HANDLE StandardOutput = nullptr,
510 _In_opt_ HANDLE StandardError = nullptr,
511 _In_opt_ HANDLE Token = nullptr,
512 _In_ DWORD Flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT);
513
514 wil::unique_file FileFromHandle(_Inout_ wil::unique_handle& Handle, _In_ const char* Mode);
515
516 BOOL LxsstuInitialize(__in BOOLEAN RunInstanceTests);
517
518 BOOL LxsstuVmMode(VOID);
519
520 std::pair<std::wstring, std::wstring> LxsstuLaunchPowershellAndCaptureOutput(_In_ const std::wstring& Cmd, _In_ int ExpectedExitCode = 0);
521
522 VOID LxsstuUninitialize(__in BOOLEAN RunInstanceTests);
523
524 void LxssLogKernelOutput();
525
526 std::wstring LxsstuGetTestDirectory(VOID);
527
528 std::wstring LxsstuGetLxssDirectory(VOID);
529
530 VOID LxsstuInstanceTests(VOID);
531
532 VOID __stdcall LxsstuWatchdogTimer(_Inout_ PTP_CALLBACK_INSTANCE Instance, _Inout_opt_ PVOID ThreadpoolTimerContext, _Inout_ PTP_TIMER Timer);
533
534 std::vector<std::wstring> LxssSplitString(_In_ const std::wstring& string, _In_ const std::wstring& delim = L" ");
535
536 void RestartWslService();
537
538 wil::unique_handle GetNonElevatedToken(TOKEN_TYPE Type = TokenPrimary);
539
540 std::wstring LxssWriteWslConfig(const std::wstring& Content);
541
542 std::string LxssWriteWslDistroConfig(const std::string& Content, LPCWSTR DistributionName = LXSS_DISTRO_NAME_TEST_L);
543
544 enum class DrvFsMode
545 {
546 WSL1,
547 Plan9,
548 Virtio9p,
549 VirtioFs
550 };
551
552 struct TestConfigDefaults
553 {
554 std::optional<size_t> vmIdleTimeout;
555 std::optional<bool> safeMode;
556 std::optional<bool> guiApplications;
557 std::optional<bool> earlyBootLogging;
558 std::optional<std::wstring> debugConsoleLogFile;
559 std::optional<DrvFsMode> drvFsMode;
560 std::optional<bool> virtioFsAggregateShares;
561 std::optional<wsl::core::NetworkingMode> networkingMode;
562 const std::optional<std::wstring> vmSwitch;
563 const std::optional<std::wstring> macAddress;
564 bool ipv6 = false;
565 std::optional<bool> dnsTunneling;
566 std::optional<std::wstring> dnsTunnelingIpAddress;
567 std::optional<bool> dnsProxy;
568 std::optional<bool> firewall;
569 std::optional<bool> autoProxy;
570 std::optional<std::wstring> kernel;
571 std::optional<std::wstring> kernelCommandLine;
572 std::optional<std::wstring> kernelModules;
573 std::optional<std::wstring> loadKernelModules;
574 std::optional<bool> loadDefaultKernelModules;
575 std::optional<std::wstring> systemDistro;
576 std::optional<bool> sparse;
577 std::optional<bool> hostAddressLoopback;
578 int crashDumpCount = 100;
579 std::optional<std::wstring> CrashDumpFolder;
580 std::optional<bool> isolateDistroCgroup;
581 };
582
583 std::wstring LxssGenerateTestConfig(TestConfigDefaults Default = {});
584
585 NTSTATUS
586 LxsstuParseLinuxLogFiles(__in PCWSTR LogFileName, __out PBOOL TestPassed);
587
588 VOID LxsstuRunTest(_In_ PCWSTR CommandLine, _In_opt_ PCWSTR LogFileName = NULL, _In_opt_ PCWSTR Username = nullptr) noexcept(false);
589
590 NTSTATUS
591 LxsstuParseLogFile(__in HANDLE FileHandle, __in PLXSS_TEST_LAUNCHER_TEST TestRecord);
592
593 bool ModuleSetup();
594
595 bool ModuleCleanup();
596
597 HANDLE
598 LxssRedirectOutput(_In_ DWORD stream, _In_ const std::wstring& file);
599
600 std::pair<HANDLE, HANDLE> UseOriginalStdHandles();
601
602 void RestoreTestStdHandles(_In_ const std::pair<HANDLE, HANDLE>& Handles);
603
604 void CreateUser(_In_ const std::wstring& Username, _Out_ PULONG Uid, _Out_ PULONG Gid);
605
606 bool TryLoadDnsResolverMethods() noexcept;
607
608 bool AreExperimentalNetworkingFeaturesSupported();
609
610 bool IsHyperVFirewallSupported() noexcept;
611
612 bool WslShutdown();
613
614 void TerminateDistribution(LPCWSTR DistributionName = LXSS_DISTRO_NAME_TEST_L);
615
616 void Trim(std::wstring& string);
617
618 inline auto EnableSystemd(const std::string& extraConfig = "", LPCWSTR distroName = LXSS_DISTRO_NAME_TEST_L)
619 {
620 // enable systemd on the test distro by editing /etc/wsl.conf
621 LxssWriteWslDistroConfig("[boot]\nsystemd=true\n" + extraConfig, distroName);
622 TerminateDistribution(distroName);
623
624 return wil::scope_exit([distroName] {
625 // clean up wsl.conf file
626 LxsstuLaunchWsl(std::format(L"-d {} " LXSST_REMOVE_DISTRO_CONF_COMMAND_LINE, distroName));
627 TerminateDistribution(distroName);
628 });
629 }
630
631 std::wstring EscapePath(std::wstring_view Path);
632
633 void StopWslService();
634
635 std::optional<GUID> GetDistributionId(LPCWSTR Name);
636 wil::unique_hkey OpenDistributionKey(LPCWSTR Name);
637
638 void ValidateOutput(LPCWSTR CommandLine, const std::wstring& ExpectedOutput, const std::wstring& ExpectedWarnings = L"", int ExitCode = -1);
639
640 std::string ReadToString(SOCKET Handle);
641 std::string ReadToString(HANDLE Handle);
642
643 // Connects a pair of overlapped TCP sockets via an anonymous bind on the loopback interface.
644 // Returns {client, server}.
645 std::pair<wil::unique_socket, wil::unique_socket> MakeSocketPair();
646
647 std::wstring ReadFileContent(const std::string& Path);
648 std::wstring ReadFileContent(const std::wstring& Path);
649
650 void WaitForOutput(wsl::windows::common::io::HandleWrapper handle, std::string_view targetValue, std::chrono::milliseconds timeout = 60s);
651 inline void WaitForOutput(wil::unique_handle handle, std::string_view targetValue, std::chrono::milliseconds timeout = 60s)
652 {
653 WaitForOutput(wsl::windows::common::io::HandleWrapper{std::move(handle)}, targetValue, timeout);
654 }
655
656 std::string EscapeString(const std::string& Input);
657
658 void VerifyPatternMatch(const std::string& Content, const std::string& Pattern);
659
660 std::filesystem::path GetTestImagePath(std::string_view imageName);
661
662 void LoadTestImage(IWSLCSession& session, std::string_view imageName);
663
664 void ExpectHttpResponse(LPCWSTR Url, std::optional<int> expectedCode, bool retry = false);
665
666 std::optional<std::wstring> GetHostAdapterIpv4();
667
668 template <typename T>
669 void VerifyAreEqualUnordered(const std::vector<T>& expected, const std::vector<T>& actual, const std::source_location& source = std::source_location::current())
670 {
671 std::map<T, size_t> expectedCounts;
672 std::map<T, size_t> actualCounts;
673
674 for (const auto& e : expected)
675 {
676 expectedCounts[e]++;
677 }
678
679 for (const auto& e : actual)
680 {
681 actualCounts[e]++;
682 }
683
684 std::wstring error;
685
686 for (const auto& [value, count] : expectedCounts)
687 {
688 if (actualCounts[value] != count)
689 {
690 error += std::format(L"Value '{}' expected {} times but was found {} times.\n", value, count, actualCounts[value]);
691 }
692 }
693
694 for (const auto& [value, count] : actualCounts)
695 {
696 if (expectedCounts.find(value) == expectedCounts.end())
697 {
698 error += std::format(L"Unexpected value found: '{}'", value);
699 }
700 }
701
702 if (!error.empty())
703 {
704 error += std::format(L"Expected ({} elements):\n", expected.size());
705 for (const auto& e : expected)
706 {
707 error += std::format(L"- {}\n", e);
708 }
709
710 error += std::format(L"Actual ({} elements):\n", actual.size());
711
712 for (const auto& e : actual)
713 {
714 error += std::format(L"- {}\n", e);
715 }
716
717 error += std::format(L"Called from: {}", source);
718
719 LogError("VerifyAreEqualUnordered failed: %ls", error.c_str());
720 VERIFY_FAIL();
721 }
722 }
723
724 void SetPathAccess(const std::filesystem::path& path, DWORD Permissions, ACCESS_MODE Mode);
725
726 void WriteSocket(SOCKET Socket, const void* data, size_t size);
727
728 void ValidateCOMErrorMessage(const std::optional<std::wstring>& Expected, const std::source_location& Source = std::source_location::current());
729
730 void ValidateCOMErrorMessageContains(const std::wstring& ExpectedSubstring);
731
732 std::wstring FormatErrorMessage(std::wstring_view message, std::wstring_view errorCode);