master
cpp 120 lines 4.5 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WSLCE2EWarningTests.cpp
8
9 Abstract:
10
11 End-to-end tests validating how warnings emitted by the WSLC COM service are
12 surfaced (or intentionally suppressed) on the wslc.exe CLI's stderr via the
13 IWarningCallback integration.
14 --*/
15
16 #include "precomp.h"
17 #include "windows/Common.h"
18 #include "WSLCExecutor.h"
19 #include "WSLCE2EHelpers.h"
20 #include "TestImageRegistry.h"
21 #include <WSLCProcessLauncher.h>
22
23 namespace WSLCE2ETests {
24 using namespace wsl::shared;
25
26 class WSLCE2EWarningTests
27 {
28 WSLC_TEST_CLASS(WSLCE2EWarningTests)
29
30 const TestImage AlpineImage = AlpineTestImage();
31 WSADATA m_wsaData{};
32
33 TEST_CLASS_SETUP(ClassSetup)
34 {
35 THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &m_wsaData));
36 TestImageRegistry::Instance().EnsureLoaded(AlpineImage);
37 return true;
38 }
39
40 TEST_CLASS_CLEANUP(ClassCleanup)
41 {
42 WSACleanup();
43 return true;
44 }
45
46 static std::string RunDockerInSession(IWSLCSession& session, std::vector<std::string>&& args)
47 {
48 wsl::windows::common::WSLCProcessLauncher launcher("/usr/bin/docker", args);
49 auto result = launcher.Launch(session).WaitAndCaptureOutput();
50 VERIFY_ARE_EQUAL(0, result.Code);
51
52 // Trim trailing whitespace from the captured stdout (fd 1).
53 auto output = result.Output[1];
54 output.erase(output.find_last_not_of(" \n\r") + 1);
55 return output;
56 }
57
58 // Best-effort removal of a container from docker.
59 static void RemoveContainerNoThrow(const std::string& containerId)
60 try
61 {
62 wil::com_ptr<IWSLCSessionManager> sessionManager;
63 THROW_IF_FAILED(CoCreateInstance(__uuidof(WSLCSessionManager), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&sessionManager)));
64 wsl::windows::common::security::ConfigureForCOMImpersonation(sessionManager.get());
65
66 wil::com_ptr<IWSLCSession> session;
67 THROW_IF_FAILED(sessionManager->OpenSessionByName(nullptr, &session));
68 wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
69
70 wsl::windows::common::WSLCProcessLauncher launcher("/usr/bin/docker", {"/usr/bin/docker", "rm", "-f", containerId});
71 launcher.Launch(*session).WaitAndCaptureOutput();
72 }
73 CATCH_LOG()
74
75 // Injects a container with corrupt WSLC metadata into the default session's storage,
76 // then verifies that running the wslc.exe CLI does not surface the COM service's recovery
77 // warning on stderr: recovery runs outside the user's current command, so it is logged
78 // (and written to the event log) rather than streamed back via IWarningCallback.
79 WSLC_TEST_METHOD(WSLCE2E_Warning_ContainerRecoveryNotPrintedOnStderr)
80 {
81 std::string corruptContainerId;
82
83 // Inject a container whose WSLC metadata label is not valid JSON. RecoverExistingContainers
84 // will fail to parse it the next time the session is created.
85 {
86 auto session = OpenDefaultElevatedSession();
87 corruptContainerId = RunDockerInSession(
88 *session,
89 {"/usr/bin/docker",
90 "create",
91 "--label",
92 "wslc.container.metadata=INVALID_JSON",
93 string::WideToMultiByte(AlpineImage.NameAndTag())});
94 VERIFY_IS_FALSE(corruptContainerId.empty());
95 }
96
97 // cleanup: remove the corrupt container
98 auto cleanup = wil::scope_exit([&]() { RemoveContainerNoThrow(corruptContainerId); });
99
100 // Terminate the default session so the next wslc command recreates it and runs recovery.
101 EnsureSessionIsTerminated();
102
103 // Run the CLI: recovery of the corrupt container fails, but because the recovery runs
104 // outside the user's current command, the warning is not printed on stderr.
105 auto result = RunWslc(L"container list");
106 VERIFY_IS_TRUE(result.ExitCode.has_value());
107 VERIFY_ARE_EQUAL(0u, result.ExitCode.value());
108
109 const auto recoveryWarning =
110 wsl::shared::Localization::MessageWslcFailedToRecoverContainer(string::MultiByteToWide(corruptContainerId));
111
112 // Assert that capture actually worked before searching it, matching the other e2e tests:
113 // RunWslc always populates Stderr, so a missing value means capture broke and the search
114 // below would pass without proving anything.
115 VERIFY_IS_TRUE(result.Stderr.has_value());
116 VERIFY_IS_TRUE(result.Stderr->find(recoveryWarning) == std::wstring::npos);
117 }
118 };
119
120 } // namespace WSLCE2ETests