master
cpp 189 lines 8.04 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WSLCE2EImageSaveTests.cpp
8
9 Abstract:
10
11 This file contains end-to-end tests for WSLC image save.
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 using namespace wsl::shared;
22
23 class WSLCE2EImageSaveTests
24 {
25 WSLC_TEST_CLASS(WSLCE2EImageSaveTests)
26
27 TEST_CLASS_CLEANUP(ClassCleanup)
28 {
29 TestImageRegistry::Instance().Delete(DebianImage);
30 TestImageRegistry::Instance().Delete(AlpineImage);
31 return true;
32 }
33
34 TEST_METHOD_SETUP(MethodSetup)
35 {
36 TestImageRegistry::Instance().EnsureLoaded(DebianImage);
37 SavedArchivePath = wsl::windows::common::filesystem::GetTempFilename();
38 return true;
39 }
40
41 TEST_METHOD_CLEANUP(MethodCleanup)
42 {
43 DeleteFileW(SavedArchivePath.c_str());
44 return true;
45 }
46
47 WSLC_TEST_METHOD(WSLCE2E_Image_Save_HelpCommand)
48 {
49 auto result = RunWslc(L"image save --help");
50 result.Verify({.Stderr = L"", .ExitCode = 0});
51 VERIFY_IS_FALSE(result.Stdout.value().empty());
52 }
53
54 WSLC_TEST_METHOD(WSLCE2E_Image_Save_MissingImageName)
55 {
56 const auto result = RunWslc(std::format(L"image save --output \"{}\"", SavedArchivePath.wstring()));
57 result.Verify({.Stdout = L"", .ExitCode = 1});
58 VERIFY_IS_TRUE(result.StderrContainsSubstring(L"Required argument not provided: 'image'"));
59 }
60
61 WSLC_TEST_METHOD(WSLCE2E_Image_Save_ImageNotFound)
62 {
63 const auto result = RunWslc(std::format(L"image save --output \"{}\" {}", SavedArchivePath.wstring(), InvalidImage.NameAndTag()));
64 result.Verify({.Stdout = L"", .Stderr = FormatErrorMessage(L"reference does not exist", L"WSLC_E_IMAGE_NOT_FOUND"), .ExitCode = 1});
65 }
66
67 WSLC_TEST_METHOD(WSLCE2E_Image_Save_Success)
68 {
69 const auto result = RunWslc(std::format(L"image save --output \"{}\" {}", SavedArchivePath.wstring(), DebianImage.NameAndTag()));
70 result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
71
72 VERIFY_IS_TRUE(std::filesystem::exists(SavedArchivePath));
73 auto sourceFileSize = std::filesystem::file_size(DebianImage.Path);
74 auto archiveFileSize = std::filesystem::file_size(SavedArchivePath);
75 VERIFY_ARE_EQUAL(sourceFileSize, archiveFileSize);
76 }
77
78 WSLC_TEST_METHOD(WSLCE2E_Image_Save_Load)
79 {
80 // Save source image
81 auto saveResult = RunWslc(std::format(L"image save --output \"{}\" {}", SavedArchivePath.wstring(), DebianImage.NameAndTag()));
82 saveResult.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
83
84 // Delete source image
85 TestImageRegistry::Instance().Delete(DebianImage);
86
87 // Load from saved archive
88 auto loadResult = RunWslc(std::format(L"image load --input \"{}\"", SavedArchivePath.wstring()));
89 loadResult.Verify({.Stderr = L"", .ExitCode = 0});
90
91 // Run a container from the loaded image to verify it works
92 auto runResult = RunWslc(std::format(L"container run --rm {} echo Hello from saved image!", DebianImage.NameAndTag()));
93 runResult.Verify({.Stdout = L"Hello from saved image!\n", .Stderr = L"", .ExitCode = 0});
94 }
95
96 WSLC_TEST_METHOD(WSLCE2E_Image_Save_ToStdout_Success)
97 {
98 const auto result = RunWslcAndRedirectToFile(std::format(L"image save {}", DebianImage.NameAndTag()), SavedArchivePath);
99 result.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
100
101 VERIFY_IS_TRUE(std::filesystem::exists(SavedArchivePath));
102 auto sourceFileSize = std::filesystem::file_size(DebianImage.Path);
103 auto archiveFileSize = std::filesystem::file_size(SavedArchivePath);
104 VERIFY_ARE_EQUAL(sourceFileSize, archiveFileSize);
105 }
106
107 WSLC_TEST_METHOD(WSLCE2E_Image_Save_ToTerminal_Fail)
108 {
109 // A pseudo console gives wslc a console stdout, which image save must reject.
110 const auto commandLine = std::format(L"image save {}", DebianImage.NameAndTag());
111 auto session = RunWslcInteractive(commandLine, ElevationType::Elevated, PseudoConsole{200, 50});
112
113 WaitForPseudoConsoleOutput(session, string::WideToMultiByte(Localization::WSLCCLI_ImageSaveStdoutIsTerminalError()));
114 VERIFY_ARE_EQUAL(1, session.Wait());
115 }
116
117 WSLC_TEST_METHOD(WSLCE2E_Image_Save_ToStdout_Load)
118 {
119 // Save source image
120 auto saveResult = RunWslcAndRedirectToFile(std::format(L"image save {}", DebianImage.NameAndTag()), SavedArchivePath);
121 saveResult.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
122
123 // Delete source image
124 TestImageRegistry::Instance().Delete(DebianImage);
125
126 // Load from saved archive
127 auto loadResult = RunWslc(std::format(L"image load --input \"{}\"", SavedArchivePath.wstring()));
128 loadResult.Verify({.Stderr = L"", .ExitCode = 0});
129
130 // Run a container from the loaded image to verify it works
131 auto runResult = RunWslc(std::format(L"container run --rm {} echo Hello from saved image!", DebianImage.NameAndTag()));
132 runResult.Verify({.Stdout = L"Hello from saved image!\n", .Stderr = L"", .ExitCode = 0});
133 }
134 WSLC_TEST_METHOD(WSLCE2E_Image_Save_MultipleImages_Load)
135 {
136 TestImageRegistry::Instance().EnsureLoaded(AlpineImage);
137
138 // Force a pristine re-load of DebianImage at the end so subsequent tests (in fast mode)
139 // see the same on-disk tar as DebianImage.Path. Without this, reloading from a
140 // multi-image archive can produce a slightly different on-disk representation that
141 // breaks byte-exact size checks in WSLCE2E_Image_Save_Success.
142 auto restoreDebian = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { TestImageRegistry::Instance().Delete(DebianImage); });
143
144 // Save both images into a single archive.
145 const auto saveResult = RunWslc(std::format(
146 L"image save --output \"{}\" {} {}", SavedArchivePath.wstring(), DebianImage.NameAndTag(), AlpineImage.NameAndTag()));
147 saveResult.Verify({.Stdout = L"", .Stderr = L"", .ExitCode = 0});
148
149 // Delete both source images.
150 TestImageRegistry::Instance().Delete(DebianImage);
151 TestImageRegistry::Instance().Delete(AlpineImage);
152
153 // Load both images back from the single archive.
154 const auto loadResult = RunWslc(std::format(L"image load --input \"{}\"", SavedArchivePath.wstring()));
155 loadResult.Verify({.Stderr = L"", .ExitCode = 0});
156
157 auto loadedImages = wsl::shared::string::Split(loadResult.Stdout.value(), L'\n');
158
159 VERIFY_IS_TRUE(
160 std::ranges::find(loadedImages, std::format(L"Loaded image: {}\r", DebianImage.NameAndTag())) != loadedImages.end());
161 VERIFY_IS_TRUE(
162 std::ranges::find(loadedImages, std::format(L"Loaded image: {}\r", AlpineImage.NameAndTag())) != loadedImages.end());
163
164 // Run a container from each loaded image to confirm both are restored and runnable.
165 const auto runDebian = RunWslc(std::format(L"container run --rm {} echo ok!", DebianImage.NameAndTag()));
166 runDebian.Verify({.Stdout = std::format(L"ok!\n"), .Stderr = L"", .ExitCode = 0});
167
168 const auto runAlpine = RunWslc(std::format(L"container run --rm {} echo ok!", AlpineImage.NameAndTag()));
169 runAlpine.Verify({.Stdout = std::format(L"ok!\n"), .Stderr = L"", .ExitCode = 0});
170 }
171
172 WSLC_TEST_METHOD(WSLCE2E_Image_Save_MultipleImages_InvalidImage)
173 {
174 const auto result = RunWslc(std::format(
175 L"image save --output \"{}\" {} {}", SavedArchivePath.wstring(), DebianImage.NameAndTag(), InvalidImage.NameAndTag()));
176 VERIFY_IS_TRUE(result.ExitCode.has_value());
177 VERIFY_ARE_EQUAL(1u, result.ExitCode.value());
178 VERIFY_IS_TRUE(result.Stderr.has_value());
179 VERIFY_ARE_NOT_EQUAL(0u, result.Stderr.value().size());
180 }
181
182 private:
183 const TestImage DebianImage = DebianTestImage();
184 const TestImage& AlpineImage = AlpineTestImage();
185 const TestImage& InvalidImage = InvalidTestImage();
186
187 std::filesystem::path SavedArchivePath{};
188 };
189 } // namespace WSLCE2ETests