WSL/test/windows/SimpleTests.cpp
WSL Team 697572d664 Initial open source commit for WSL.
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016.

The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021:

Number of commits on the main branch: 2930
Number of contributors: 31

Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
2025-05-15 12:09:45 -07:00

266 lines
No EOL
11 KiB
C++

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
SimpleTests.cpp
Abstract:
This file contains smoke tests for WSL.
--*/
#include "precomp.h"
#include "Common.h"
namespace SimpleTests {
class SimpleTests
{
WSL_TEST_CLASS(SimpleTests)
// Initialize the tests
TEST_CLASS_SETUP(TestClassSetup)
{
VERIFY_ARE_EQUAL(LxsstuInitialize(FALSE), TRUE);
return true;
}
TEST_CLASS_CLEANUP(TestClassCleanup)
{
LxsstuUninitialize(FALSE);
return true;
}
TEST_METHOD(EchoTest)
{
const std::wstring echoExpected = L"LOW!\n";
auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L"echo LOW!");
VERIFY_ARE_EQUAL(output, echoExpected);
}
TEST_METHOD(WhoamiTest)
{
const std::wstring whoamiExpected = L"root\n";
auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L"-u root whoami");
VERIFY_ARE_EQUAL(output, whoamiExpected);
}
TEST_METHOD(ChangeDirTest)
{
const std::wstring cdExpected = L"/root\n";
auto [output, __] = LxsstuLaunchWslAndCaptureOutput(L"--cd ~ --user root pwd");
VERIFY_ARE_EQUAL(output, cdExpected);
}
TEST_METHOD(Daemonize)
{
WslConfigChange config(LxssGenerateTestConfig({.vmIdleTimeout = 0}));
WslShutdown();
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"-- eval \"touch /dev/shm/backgroundmagic; daemonize $(which sleep) 30\""), (DWORD)0);
std::this_thread::sleep_for(std::chrono::seconds(20));
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"-- ls /dev/shm/backgroundmagic"), (DWORD)0);
}
static void VerifySparse(wchar_t const* path, bool sparse)
{
DWORD attributes = ::GetFileAttributesW(path);
VERIFY_IS_FALSE(attributes == INVALID_FILE_ATTRIBUTES);
VERIFY_IS_TRUE(WI_IsFlagSet(attributes, FILE_ATTRIBUTE_SPARSE_FILE) == sparse);
}
TEST_METHOD(CheckSparse)
{
WSL2_TEST_ONLY();
WslConfigChange config(LxssGenerateTestConfig({.sparse = true}));
std::filesystem::path tar = std::tmpnam(nullptr);
tar += ".tar";
LogInfo("tar %ls", tar.c_str());
auto cleanupTar = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
try
{
std::filesystem::remove(tar);
}
CATCH_LOG()
});
const std::wstring tempDistro = L"temp_distro";
const std::filesystem::path vhdDir = std::tmpnam(nullptr);
LogInfo("vhdDir %ls", vhdDir.c_str());
VERIFY_IS_TRUE(std::filesystem::create_directory(vhdDir));
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
try
{
LxsstuLaunchWsl(std::format(L"{} {}", WSL_UNREGISTER_ARG, tempDistro).c_str());
std::filesystem::remove_all(vhdDir);
}
CATCH_LOG()
});
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(std::format(L"{} {} {}", WSL_EXPORT_ARG, LXSS_DISTRO_NAME_TEST, tar.wstring()).c_str()), (DWORD)0);
LxsstuLaunchWsl(std::format(L"{} {}", WSL_UNREGISTER_ARG, tempDistro).c_str());
ValidateOutput(
std::format(L"{} {} {} {}", WSL_IMPORT_ARG, tempDistro, vhdDir.wstring(), tar.wstring()).c_str(),
L"The operation completed successfully. \r\n",
L"wsl: Sparse VHD support is currently disabled due to potential data corruption.\r\n"
L"To force a distribution to use a sparse vhd, please run:\r\n"
L"wsl.exe --manage <DistributionName> --set-sparse --allow-unsafe\r\n",
0);
std::filesystem::path vhdPath = vhdDir / LXSS_VM_MODE_VHD_NAME;
VerifySparse(vhdPath.c_str(), false);
WslShutdown();
// Setting a distro VHD to sparse requires the allow unsafe flag.
ValidateOutput(
std::format(L"{} {} {} {}", WSL_MANAGE_ARG, tempDistro, WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, L"true").c_str(),
L"Sparse VHD support is currently disabled due to potential data corruption.\r\n"
L"To force a distribution to use a sparse vhd, please run:\r\n"
L"wsl.exe --manage <DistributionName> --set-sparse --allow-unsafe\r\nError code: Wsl/Service/E_INVALIDARG\r\n",
L"",
-1);
VerifySparse(vhdPath.c_str(), false);
ValidateOutput(
std::format(L"{} {} {} {} {}", WSL_MANAGE_ARG, tempDistro, WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, L"true", WSL_MANAGE_ARG_ALLOW_UNSAFE)
.c_str(),
L"The operation completed successfully. \r\n",
L"",
0);
VerifySparse(vhdPath.c_str(), true);
// Disabling sparse on a VHD does not require the allow unsafe flag.
ValidateOutput(
std::format(L"{} {} {} {}", WSL_MANAGE_ARG, tempDistro, WSL_MANAGE_ARG_SET_SPARSE_OPTION_LONG, L"false").c_str(),
L"The operation completed successfully. \r\n",
L"",
0);
VerifySparse(vhdPath.c_str(), false);
}
TEST_METHOD(StringHelpers)
{
std::string string1 = "aaaBBB";
std::string string2 = "aaabbb";
VERIFY_IS_TRUE(wsl::shared::string::IsEqual(string1, string2, true));
VERIFY_IS_FALSE(wsl::shared::string::IsEqual(string1, string2, false));
VERIFY_IS_TRUE(wsl::shared::string::IsEqual(string1.c_str(), string2.c_str(), true));
VERIFY_IS_FALSE(wsl::shared::string::IsEqual(string1.c_str(), string2.c_str(), false));
VERIFY_IS_TRUE(wsl::shared::string::StartsWith(string1, string2.substr(0, 3), true));
VERIFY_IS_FALSE(wsl::shared::string::StartsWith(string1, string2, false));
std::wstring wstring1 = L"aaaBBB";
std::wstring wstring2 = L"aaabbb";
VERIFY_IS_TRUE(wsl::shared::string::IsEqual(wstring1, wstring2, true));
VERIFY_IS_FALSE(wsl::shared::string::IsEqual(wstring1, wstring2, false));
VERIFY_IS_TRUE(wsl::shared::string::IsEqual(wstring1.c_str(), wstring2.c_str(), true));
VERIFY_IS_FALSE(wsl::shared::string::IsEqual(wstring1.c_str(), wstring2.c_str(), false));
VERIFY_IS_TRUE(wsl::shared::string::StartsWith(wstring1, wstring2.substr(0, 3), true));
VERIFY_IS_FALSE(wsl::shared::string::StartsWith(wstring1, wstring2, false));
// Test wsl::shared::string::ParseBool
std::vector<std::pair<LPCSTR, std::optional<bool>>> boolTests = {
{"1", true},
{"0", false},
{"true", true},
{"false", false},
{"True", true},
{"False", false},
{"t", std::nullopt},
{"f", std::nullopt},
{"T", std::nullopt},
{"F", std::nullopt},
{nullptr, std::nullopt},
{"", std::nullopt},
{"2", std::nullopt},
{"tru", std::nullopt},
{"fals", std::nullopt},
};
for (const auto& [input, expected] : boolTests)
{
VERIFY_ARE_EQUAL(expected, wsl::shared::string::ParseBool(input));
std::wstring wideString = wsl::shared::string::MultiByteToWide(input);
VERIFY_ARE_EQUAL(expected, wsl::shared::string::ParseBool(wideString.c_str()));
}
// Test wsl::shared::string::ParseMemoryString
const std::vector<std::pair<LPCSTR, std::optional<uint64_t>>> testCases{
{"0", 0},
{"1", 1},
{" 1", 1},
{"1B", 1},
{"1K", 1024},
{"1KB", 1024},
{"2M", 2 * 1024 * 1024},
{"100MB", 100 * 1024 * 1024},
{"9G", 9 * 1024ULL * 1024ULL * 1024ULL},
{"44GB", 44 * 1024ULL * 1024ULL * 1024ULL},
{"1TB", 1ULL << 40},
{"2T", 2ULL << 40},
{"1 B", std::nullopt},
{nullptr, std::nullopt},
{"", std::nullopt},
{"foo", std::nullopt}};
for (const auto& [input, expected] : testCases)
{
VERIFY_ARE_EQUAL(wsl::shared::string::ParseMemorySize(input), expected);
const auto wideInput = wsl::shared::string::MultiByteToWide(input);
VERIFY_ARE_EQUAL(wsl::shared::string::ParseMemorySize(wideInput.c_str()), expected);
}
// Test wsl::shared::string GUID helpers
const GUID guid = {0x1234567a, 0x1234, 0x5678, {0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78}};
const std::string guidString = "{1234567a-1234-5678-1234-567812345678}";
const std::string guidStringNoBraces = "1234567a-1234-5678-1234-567812345678";
const std::vector<std::pair<LPCSTR, std::optional<GUID>>> guidTestCases{
{guidString.c_str(), guid},
{guidStringNoBraces.c_str(), guid},
{nullptr, std::nullopt},
{"", std::nullopt},
{"foo", std::nullopt},
{"1234567G-1234-5678-1234-5678123456789", std::nullopt},
{"{1234567a-1234-5678-1234-567812345678", std::nullopt},
{"{1234567aB-1234-5678-1234-567812345678}", std::nullopt}};
for (const auto& [input, expected] : guidTestCases)
{
VERIFY_ARE_EQUAL(expected, wsl::shared::string::ToGuid(input));
const auto wideInput = wsl::shared::string::MultiByteToWide(input);
VERIFY_ARE_EQUAL(expected, wsl::shared::string::ToGuid(wideInput));
}
VERIFY_ARE_EQUAL(guidString, wsl::shared::string::GuidToString<char>(guid));
VERIFY_ARE_EQUAL(guidString, wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::AddBraces));
VERIFY_ARE_EQUAL(guidStringNoBraces, wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::None));
auto upperCaseGuidString = guidStringNoBraces;
std::transform(upperCaseGuidString.begin(), upperCaseGuidString.end(), upperCaseGuidString.begin(), toupper);
VERIFY_ARE_EQUAL(upperCaseGuidString, wsl::shared::string::GuidToString<char>(guid, wsl::shared::string::GuidToStringFlags::Uppercase));
const auto wideGuidString = wsl::shared::string::MultiByteToWide(guidString);
VERIFY_ARE_EQUAL(wideGuidString, wsl::shared::string::GuidToString<wchar_t>(guid));
VERIFY_ARE_EQUAL(wideGuidString, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::AddBraces));
const auto wideGuidStringNoBraces = wsl::shared::string::MultiByteToWide(guidStringNoBraces);
VERIFY_ARE_EQUAL(wideGuidStringNoBraces, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::None));
auto upperCaseGuidStringWide = wideGuidStringNoBraces;
std::transform(upperCaseGuidStringWide.begin(), upperCaseGuidStringWide.end(), upperCaseGuidStringWide.begin(), toupper);
VERIFY_ARE_EQUAL(upperCaseGuidStringWide, wsl::shared::string::GuidToString<wchar_t>(guid, wsl::shared::string::GuidToStringFlags::Uppercase));
}
};
} // namespace SimpleTests