master
cpp 146 lines 6.41 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WSLCVolumeMountUnitTests.cpp
8
9 Abstract:
10
11 This file contains unit tests for WSLC volume mount argument parsing and validation.
12
13 --*/
14
15 #include "precomp.h"
16 #include "windows/Common.h"
17 #include "ContainerModel.h"
18
19 using namespace wsl::windows::wslc::models;
20
21 namespace WSLCVolumeMount {
22
23 class WSLCVolumeMountUnitTests
24 {
25 WSLC_TEST_CLASS(WSLCVolumeMountUnitTests)
26
27 TEST_METHOD(VolumeMount_Parse_ReturnExpectedResult)
28 {
29 const auto cwd = std::filesystem::current_path();
30
31 // Volume value => host, container, readonly
32 std::vector<std::tuple<std::wstring, std::wstring, std::string, bool>> validVolumeArgs = {
33 {LR"(C:\hostPath:/containerPath)", LR"(C:\hostPath)", R"(/containerPath)", false},
34 {LR"(C:\hostPath:/containerPath:ro)", LR"(C:\hostPath)", R"(/containerPath)", true},
35 {LR"(C:\hostPath:/containerPath:rw)", LR"(C:\hostPath)", R"(/containerPath)", false},
36 {LR"(C:\host Path:/container Path:ro)", LR"(C:\host Path)", R"(/container Path)", true},
37 {LR"(C:\host Path:/container Path:rw)", LR"(C:\host Path)", R"(/container Path)", false},
38
39 // Relative paths. Expected host is CWD + the normalized relative component.
40 // Windows will convert forward slashes to backslashes in the host path.
41 {L"./foo:/data", (cwd / L"foo").wstring(), R"(/data)", false},
42 {L".\\foo:/data", (cwd / L"foo").wstring(), R"(/data)", false},
43 {L"./foo:/data:ro", (cwd / L"foo").wstring(), R"(/data)", true},
44 {L"../bar:/data:rw", (cwd.parent_path() / L"bar").wstring(), R"(/data)", false},
45 {L"..\\bar:/data:rw", (cwd.parent_path() / L"bar").wstring(), R"(/data)", false},
46 {L"sub/dir:/data", (cwd / L"sub" / L"dir").wstring(), R"(/data)", false},
47 {L"sub\\dir:/data", (cwd / L"sub" / L"dir").wstring(), R"(/data)", false},
48 };
49
50 for (const auto& arg : validVolumeArgs)
51 {
52 WEX::Logging::Log::Comment(std::format(L"Testing volume argument: '{}'", std::get<0>(arg)).c_str());
53 auto result = VolumeMount::Parse(std::get<0>(arg));
54 VERIFY_ARE_EQUAL(std::get<1>(arg), result.Host());
55 VERIFY_ARE_EQUAL(std::get<2>(arg), result.ContainerPath());
56 VERIFY_ARE_EQUAL(std::get<3>(arg), result.IsReadOnly());
57 }
58 }
59
60 TEST_METHOD(VolumeMount_Parse_InvalidArgs)
61 {
62 std::vector<std::wstring> invalidCases = {
63 LR"(:/containerPath)", // Empty host path
64 LR"(:/containerPath:ro)", // Empty host path
65 LR"(:)", // Empty container path
66 LR"(::)", // Empty container path
67 LR"(C:\hostPath::ro)", // Empty container path
68 LR"(C:\hostPath:)", // Empty container path
69 LR"(C:\hostPath::rw)", // Empty container path
70 LR"(C:\hostPath:/containerPath:)", // Empty container path
71 };
72
73 for (const auto& value : invalidCases)
74 {
75 WEX::Logging::Log::Comment(std::format(L"Testing invalid volume argument: '{}'", value).c_str());
76 VERIFY_THROWS(VolumeMount::Parse(value), wil::ResultException);
77 }
78 }
79
80 TEST_METHOD(VolumeMount_Parse_InvalidContainerPath)
81 {
82 // Drive colon gets misinterpreted as the host:container separator,
83 // producing a non-absolute container path. Caught by container path validation.
84 std::vector<std::wstring> invalidPathCases = {
85 LR"(C:\hostPath:ro)", // host='C', container=\hostPath (not absolute)
86 LR"(C:\hostPath)", // host='C', container=\hostPath (not absolute)
87 LR"(C:\hostPath:/containerPath:invalid_mode)", // container=invalid_mode (not absolute)
88 LR"(C:\hostPath:/containerPath:ro:extra)", // container=extra (not absolute)
89 };
90
91 for (const auto& value : invalidPathCases)
92 {
93 WEX::Logging::Log::Comment(std::format(L"Testing invalid path: '{}'", value).c_str());
94 VERIFY_THROWS(VolumeMount::Parse(value), wil::ResultException);
95 }
96 }
97
98 TEST_METHOD(VolumeMount_IsValidNamedVolumeName_ValidNames)
99 {
100 // These should all be recognised as valid Docker named volume names.
101 std::vector<std::wstring> validNames = {
102 L"myvolume", // simple lowercase
103 L"MyVolume", // mixed case
104 L"my-volume", // hyphen
105 L"my_volume", // underscore
106 L"my.volume", // dot
107 L"v1", // minimum length (2 chars)
108 L"volume123", // trailing digits
109 L"my-vol_1.0", // combination of all allowed characters
110 };
111
112 for (const auto& name : validNames)
113 {
114 WEX::Logging::Log::Comment(std::format(L"Testing valid named volume name: '{}'", name).c_str());
115 VERIFY_IS_TRUE(VolumeMount::IsValidNamedVolumeName(name));
116 }
117 }
118
119 TEST_METHOD(VolumeMount_IsValidNamedVolumeName_InvalidNames)
120 {
121 // These should all be rejected. They are either paths or otherwise invalid.
122 std::vector<std::wstring> invalidNames = {
123 L"./foo", // relative path with ./
124 L"../foo", // relative path with ../
125 L".hidden", // starts with '.' (relative path indicator)
126 L"foo/bar", // contains forward slash (path separator)
127 L"foo\\bar", // contains backslash (path separator)
128 L"C:\\path\\to\\dir", // absolute Windows path
129 L"/absolute/path", // absolute Unix-style path
130 L"a", // too short (single character)
131 L"", // empty
132 L":volume", // starts with invalid character
133 L"-volume", // starts with hyphen (not alphanumeric)
134 L"_volume", // starts with underscore (not alphanumeric)
135 L"vol ume", // contains space
136 L"vol:ume", // contains colon
137 };
138
139 for (const auto& name : invalidNames)
140 {
141 WEX::Logging::Log::Comment(std::format(L"Testing invalid named volume name: '{}'", name).c_str());
142 VERIFY_IS_FALSE(VolumeMount::IsValidNamedVolumeName(name));
143 }
144 }
145 };
146 } // namespace WSLCVolumeMount