Files
WSL/test/windows/OpenVmmVirtualMachineBackendTests.cpp
T

432 lines
22 KiB
C++
Raw Normal View History

2026-09-28 14:55:51 -07:00
// Copyright (C) Microsoft Corporation. All rights reserved.
#include "precomp.h"
#include "Common.h"
2026-10-06 13:31:31 -07:00
#include "OpenVmmNatNetworking.h"
#include "OpenVmmVirtualMachineBackend.h"
2026-09-30 17:14:20 -07:00
#include "VirtualMachineBackendTestHelpers.h"
using wsl::windows::common::vm::openvmm::ValidateCreateRequest;
2026-09-30 17:14:20 -07:00
using namespace VirtualMachineBackendTestHelpers;
namespace {
constexpr HRESULT c_notSupported = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
VmBootDiskRequest CreateDisk(std::wstring Key)
{
VmBootDiskRequest disk;
disk.Key = std::move(Key);
disk.Disk.Source = VmVirtualDiskSource{L"C:\\images\\disk.vhdx", VmDiskFormat::Vhdx};
return disk;
}
2026-09-28 14:55:51 -07:00
VmNetworkAdapterRequest CreateNetworkRequest()
{
VmNetworkAdapterRequest request;
request.Tag = L"eth0";
2026-09-29 14:40:39 -07:00
VmUserModeNatNetwork configuration;
configuration.Configuration.clientIp.value = htonl(0x0a000002);
constexpr std::array<BYTE, 6> clientMac{0x00, 0x15, 0x5d, 0x01, 0x02, 0x03};
std::copy(clientMac.begin(), clientMac.end(), std::begin(configuration.Configuration.clientMac.bytes));
configuration.Configuration.gatewayIp.value = htonl(0x0a000001);
constexpr std::array<BYTE, 6> gatewayMac{0x52, 0x55, 0x0a, 0x00, 0x00, 0x01};
std::copy(gatewayMac.begin(), gatewayMac.end(), std::begin(configuration.Configuration.gatewayMac.bytes));
constexpr std::array<BYTE, 6> gatewayMacIpv6{0x52, 0x55, 0x0a, 0x00, 0x01, 0x02};
std::copy(gatewayMacIpv6.begin(), gatewayMacIpv6.end(), std::begin(configuration.Configuration.gatewayMacIpv6.bytes));
configuration.Configuration.netmask.value = htonl(0xffffff00);
request.Configuration = configuration;
2026-09-28 14:55:51 -07:00
return request;
}
std::uint16_t ReserveTcpPort()
{
wil::unique_socket socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)};
THROW_WIN32_IF(static_cast<DWORD>(WSAGetLastError()), !socket);
SOCKADDR_IN address{};
address.sin_family = AF_INET;
address.sin_addr.S_un.S_addr = htonl(INADDR_LOOPBACK);
THROW_WIN32_IF(static_cast<DWORD>(WSAGetLastError()), bind(socket.get(), reinterpret_cast<const sockaddr*>(&address), sizeof(address)) == SOCKET_ERROR);
int addressLength = sizeof(address);
THROW_WIN32_IF(static_cast<DWORD>(WSAGetLastError()), getsockname(socket.get(), reinterpret_cast<sockaddr*>(&address), &addressLength) == SOCKET_ERROR);
return ntohs(address.sin_port);
}
2026-09-30 17:14:20 -07:00
HRESULT DescribeResult(const VmCreateRequest& Request)
{
return wil::ResultFromException([&] { ValidateCreateRequest(Request); });
}
} // namespace
namespace OpenVmmVirtualMachineBackendTests {
class OpenVmmVirtualMachineBackendTests
{
WSL_TEST_CLASS(OpenVmmVirtualMachineBackendTests)
2026-10-02 18:57:05 -07:00
TEST_CLASS_SETUP(TestClassSetup)
{
WSADATA data{};
THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data));
return true;
}
TEST_CLASS_CLEANUP(TestClassCleanup)
{
VERIFY_ARE_EQUAL(0, WSACleanup());
return true;
}
TEST_METHOD(PreservesCallerIdentityAndBootInputs)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
2026-09-30 17:14:20 -07:00
request.Boot.KernelCommandLine = L"personality=caller console=hvc0 console=ttyS0 custom=value";
request.BootDisks.push_back(CreateDisk(L"arbitrary-key"));
request.BootDisks[0].Disk.ReadOnly = false;
const auto description = ValidateCreateRequest(request);
2026-09-30 17:14:20 -07:00
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, description.Identity.VmId));
VERIFY_ARE_EQUAL(request.Processor.Count, description.Processor.Count);
VERIFY_ARE_EQUAL(request.Memory.SizeBytes, description.Memory.SizeBytes);
VERIFY_ARE_EQUAL(VmBootMethod::LinuxDirect, description.Boot.Method);
2026-09-30 17:14:20 -07:00
VERIFY_ARE_EQUAL(request.Boot.KernelCommandLine, description.Boot.KernelCommandLine);
VERIFY_ARE_EQUAL(size_t{1}, description.BootDisks.size());
const auto& disk = description.BootDisks.at(L"arbitrary-key");
2026-09-30 17:14:20 -07:00
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, disk.Id.Owner.VmId));
VERIFY_ARE_EQUAL(UINT64{1}, disk.Id.Value);
VERIFY_ARE_EQUAL(UINT32{0}, disk.GuestAddress.Lun);
VERIFY_IS_FALSE(disk.ReadOnly);
request.Mmio.HighWindowSizeBytes = c_mib;
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
request.Mmio = {};
2026-10-06 12:25:45 -07:00
for (auto* feature :
{&request.Memory.AllowOvercommit, &request.Memory.DeferredCommit, &request.Memory.ColdDiscard, &request.Memory.SmallPageBacking})
{
*feature = VmFeatureRequest::Required;
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
*feature = VmFeatureRequest::Disabled;
}
2026-10-06 13:31:31 -07:00
2026-10-06 12:25:45 -07:00
request.Memory.HostingProcessNameSuffix = L"WSL";
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
}
2026-10-06 13:31:31 -07:00
TEST_METHOD(OpenVmmNatUsesCreationTimeAdapterAndGuestDhcp)
{
const auto request = wsl::core::CreateOpenVmmNatNetworkAdapterRequest();
VERIFY_ARE_EQUAL(L"eth0", request.Tag);
const auto& network = std::get<VmUserModeNatNetwork>(request.Configuration);
constexpr wsl::shared::string::MacAddress expectedMac{0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
VERIFY_IS_TRUE(network.ClientMacAddress() == expectedMac);
auto [client, server] = MakeSocketPair();
wsl::core::OpenVmmNatNetworking networking(wsl::core::GnsChannel(std::move(server)), true, 5000);
LX_MINI_INIT_NETWORKING_CONFIGURATION configuration{};
networking.FillInitialConfiguration(configuration);
VERIFY_ARE_EQUAL(LxMiniInitNetworkingModeNat, configuration.NetworkingMode);
VERIFY_IS_TRUE(configuration.DisableIpv6);
VERIFY_IS_TRUE(configuration.EnableDhcpClient);
VERIFY_ARE_EQUAL(5, configuration.DhcpTimeout);
VERIFY_ARE_EQUAL(LxMiniInitPortTrackerTypeRelay, configuration.PortTrackerType);
}
2026-10-06 12:25:45 -07:00
TEST_METHOD(AllowsGuestAndSavedStateCrashCapture)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
request.CrashCapture = VmCrashCaptureRequest{L"C:\\crashes"};
VERIFY_ARE_EQUAL(S_OK, DescribeResult(request));
request.CrashCapture->SavedStateFolder = L"C:\\saved-state";
VERIFY_ARE_EQUAL(S_OK, DescribeResult(request));
}
TEST_METHOD(ReservesExactPlacementsBeforeAutomaticDisks)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
request.BootDisks = {CreateDisk(L"automatic-1"), CreateDisk(L"automatic-2"), CreateDisk(L"exact")};
request.BootDisks[2].Disk.Placement = VmScsiPlacement{{0, 0}};
const auto description = ValidateCreateRequest(request);
VERIFY_ARE_EQUAL(UINT32{1}, description.BootDisks.at(L"automatic-1").GuestAddress.Lun);
VERIFY_ARE_EQUAL(UINT32{2}, description.BootDisks.at(L"automatic-2").GuestAddress.Lun);
VERIFY_ARE_EQUAL(UINT32{0}, description.BootDisks.at(L"exact").GuestAddress.Lun);
2026-09-30 17:14:20 -07:00
auto highestPlacementRequest = CreateRequest();
highestPlacementRequest.BootDisks.push_back(CreateDisk(L"highest"));
highestPlacementRequest.BootDisks[0].Disk.Placement = VmScsiPlacement{{0, 253}};
2026-09-25 23:05:16 -07:00
VERIFY_ARE_EQUAL(UINT32{253}, ValidateCreateRequest(highestPlacementRequest).BootDisks.at(L"highest").GuestAddress.Lun);
2026-09-30 17:14:20 -07:00
}
TEST_METHOD(DescribesCreationTimeNetworking)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
VERIFY_IS_TRUE(ValidateCreateRequest(request).NetworkAdapters.empty());
request.NetworkAdapters.push_back(CreateNetworkRequest());
const auto description = ValidateCreateRequest(request);
VERIFY_ARE_EQUAL(size_t{1}, description.NetworkAdapters.size());
const auto& adapter = description.NetworkAdapters.at(L"eth0");
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, adapter.Id.Owner.VmId));
VERIFY_ARE_EQUAL(UINT64{1}, adapter.Id.Value);
VERIFY_IS_TRUE(adapter.GuestInstanceId.has_value());
VERIFY_IS_FALSE(IsEqualGUID(GUID_NULL, adapter.GuestInstanceId.value()));
2026-09-29 14:40:39 -07:00
const auto& effectiveConfiguration = std::get<VmUserModeNatNetwork>(adapter.EffectiveConfiguration).Configuration;
const auto& requestedConfiguration = std::get<VmUserModeNatNetwork>(request.NetworkAdapters[0].Configuration).Configuration;
2026-09-30 15:52:35 -07:00
VERIFY_IS_TRUE(std::equal(
std::begin(effectiveConfiguration.clientMac.bytes),
std::end(effectiveConfiguration.clientMac.bytes),
std::begin(requestedConfiguration.clientMac.bytes)));
2026-09-30 17:14:20 -07:00
THROW_IF_FAILED(CoCreateGuid(&request.Identity.VmId));
const auto other = ValidateCreateRequest(request).NetworkAdapters.at(L"eth0");
VERIFY_ARE_EQUAL(adapter.Id.Value, other.Id.Value);
VERIFY_IS_FALSE(IsEqualGUID(adapter.Id.Owner.VmId, other.Id.Owner.VmId));
VERIFY_IS_FALSE(IsEqualGUID(adapter.GuestInstanceId.value(), other.GuestInstanceId.value()));
}
TEST_METHOD(EnforcesDiskLimitsAndKeepsIdsVmScoped)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
for (UINT32 index = 0; index < 254; ++index)
{
request.BootDisks.push_back(CreateDisk(std::to_wstring(index)));
}
const auto first = ValidateCreateRequest(request);
VERIFY_ARE_EQUAL(UINT32{253}, first.BootDisks.at(L"253").GuestAddress.Lun);
2026-09-30 17:14:20 -07:00
THROW_IF_FAILED(CoCreateGuid(&request.Identity.VmId));
const auto second = ValidateCreateRequest(request);
VERIFY_ARE_EQUAL(first.BootDisks.at(L"0").Id.Value, second.BootDisks.at(L"0").Id.Value);
VERIFY_IS_FALSE(IsEqualGUID(first.BootDisks.at(L"0").Id.Owner.VmId, second.BootDisks.at(L"0").Id.Owner.VmId));
2026-09-28 14:55:51 -07:00
request.BootDisks.push_back(CreateDisk(L"overflow"));
VERIFY_ARE_EQUAL(WSL_E_TOO_MANY_DISKS_ATTACHED, DescribeResult(request));
}
TEST_METHOD(ValidatesExplicitBootDiskPlacements)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
request.BootDisks.push_back(CreateDisk(L"exact"));
auto& disk = request.BootDisks[0].Disk;
disk.Placement = VmScsiPlacement{{0, 253}};
VERIFY_ARE_EQUAL(UINT32{253}, ValidateCreateRequest(request).BootDisks.at(L"exact").GuestAddress.Lun);
for (const UINT32 lun : {UINT32{254}, UINT32_MAX})
{
disk.Placement = VmScsiPlacement{{0, lun}};
2026-09-30 17:14:20 -07:00
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
2026-09-28 14:55:51 -07:00
}
for (const UINT32 lun : {UINT32{0}, UINT32{254}, UINT32_MAX})
{
disk.Placement = VmScsiPlacement{{1, lun}};
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
}
disk.Placement = VmScsiPlacement{{0, 253}};
auto duplicate = CreateDisk(L"duplicate");
duplicate.Disk.Placement = disk.Placement;
request.BootDisks.push_back(std::move(duplicate));
VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), DescribeResult(request));
}
2026-09-30 17:14:20 -07:00
TEST_METHOD(DescribesConsoleFamiliesIndependently)
{
SKIP_TEST_ARM64();
auto request = CreateRequest();
request.Consoles = {
{VmConsoleRole::EarlyBoot, VmSerialConsole{0, L"\\\\.\\pipe\\early"}},
2026-10-06 12:25:45 -07:00
{VmConsoleRole::KernelConsole, VmVirtioConsole{0, L"hvc0", L"\\\\.\\pipe\\console"}},
{VmConsoleRole::Telemetry, VmVirtioConsole{1, L"hvc1", L"\\\\.\\pipe\\telemetry"}}};
VERIFY_ARE_EQUAL(size_t{3}, ValidateCreateRequest(request).Boot.Consoles.size());
std::get<VmVirtioConsole>(request.Consoles[2].Device).GuestName = L"telemetry";
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
std::get<VmVirtioConsole>(request.Consoles[2].Device).GuestName = L"hvc1";
std::get<VmVirtioConsole>(request.Consoles[2].Device).Port = 2;
VERIFY_ARE_EQUAL(c_notSupported, DescribeResult(request));
std::get<VmVirtioConsole>(request.Consoles[2].Device).Port = 1;
request.Consoles.push_back({VmConsoleRole::DebugShell, VmVirtioConsole{2, L"hvc2", L"\\\\.\\pipe\\debug"}});
VERIFY_ARE_EQUAL(size_t{4}, ValidateCreateRequest(request).Boot.Consoles.size());
}
TEST_METHOD(BootsAndTerminates)
{
SKIP_TEST_ARM64();
2026-09-30 17:14:20 -07:00
VerifyBootsAndTerminates(OpenVmmVirtualMachineBackend::Create(CreateRunnableRequest()));
}
2026-10-06 12:25:45 -07:00
TEST_METHOD(GuestShutdownTerminatesProcess)
{
SKIP_TEST_ARM64();
wil::unique_event callbackEvent{wil::EventOptions::ManualReset};
GUID callbackVmId{};
2026-10-06 13:31:31 -07:00
auto backend = OpenVmmVirtualMachineBackend::Create(CreateRunnableRequest());
auto terminationEvent = backend->GetTerminationEvent();
2026-10-06 12:25:45 -07:00
backend->RegisterTerminationCallback([&](GUID VmId) {
callbackVmId = VmId;
callbackEvent.SetEvent();
});
auto [channel, notifications] = StartGuest(*backend);
channel.Close();
notifications.reset();
VERIFY_ARE_EQUAL(static_cast<DWORD>(WAIT_OBJECT_0), WaitForSingleObject(terminationEvent.get(), 30 * 1000));
VERIFY_IS_TRUE(callbackEvent.wait(30 * 1000));
VERIFY_IS_TRUE(IsEqualGUID(backend->GetDescription().Identity.VmId, callbackVmId));
VERIFY_ARE_EQUAL(VmState::Stopped, backend->GetState());
VERIFY_ARE_EQUAL(VmTerminationReason::Shutdown, backend->GetTerminationReason().Reason);
}
2026-09-28 14:55:51 -07:00
TEST_METHOD(ManagesFileSystemNetworkAndPortResources)
{
SKIP_TEST_ARM64();
auto request = CreateRunnableRequest();
const auto networkRequest = CreateNetworkRequest();
request.NetworkAdapters.push_back(networkRequest);
GUID directoryId{};
THROW_IF_FAILED(CoCreateGuid(&directoryId));
const auto sharePath =
wsl::windows::common::filesystem::GetTempFolderPath(GetCurrentProcessToken()) /
(L"OpenVmmBackendTest-" + wsl::shared::string::GuidToString<wchar_t>(directoryId, wsl::shared::string::GuidToStringFlags::None));
THROW_IF_WIN32_BOOL_FALSE(CreateDirectoryW(sharePath.c_str(), nullptr));
auto removeShareDirectory = wil::scope_exit([&] { LOG_IF_WIN32_BOOL_FALSE(RemoveDirectoryW(sharePath.c_str())); });
auto backend = OpenVmmVirtualMachineBackend::Create(request);
2026-09-25 23:40:28 -07:00
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->CreateFileSystemDevice({VmPlan9SocketDevice{{50000}}}); }));
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->CreateFileSystemDevice({VmPlan9VirtioDevice{L"plan9"}}); }));
VmFileSystemDeviceRequest fileSystemRequest{VmVirtioFsDevice{L"test-share", VmVirtioFsLayout::Aggregate}};
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->CreateFileSystemDevice(fileSystemRequest); }));
2026-09-25 23:05:16 -07:00
auto& fileSystemTransport = std::get<VmVirtioFsDevice>(fileSystemRequest.Transport);
fileSystemTransport.Layout = VmVirtioFsLayout::SingleShare;
2026-09-30 15:45:13 -07:00
fileSystemRequest.UserToken = request.Identity.UserToken;
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->CreateFileSystemDevice(fileSystemRequest); }));
fileSystemRequest.UserToken.emplace();
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->CreateFileSystemDevice(fileSystemRequest); }));
fileSystemRequest.UserToken.reset();
2026-09-28 14:55:51 -07:00
const auto fileSystemDevice = backend->CreateFileSystemDevice(fileSystemRequest);
2026-09-29 14:40:39 -07:00
VERIFY_ARE_EQUAL(UINT64{2}, fileSystemDevice.Id.Value);
2026-09-30 17:14:20 -07:00
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, fileSystemDevice.Id.Owner.VmId));
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(VmFileSystemDeviceState::Prepared, fileSystemDevice.State);
2026-09-30 15:45:13 -07:00
VERIFY_ARE_EQUAL(fileSystemTransport.Tag, std::get<VmVirtioFsDevice>(fileSystemDevice.Transport).Tag);
VERIFY_ARE_EQUAL(VmVirtioFsLayout::SingleShare, std::get<VmVirtioFsDevice>(fileSystemDevice.Transport).Layout);
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(
HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), OperationResult([&] { backend->CreateFileSystemDevice(fileSystemRequest); }));
VmFileSystemShareRequest shareRequest;
shareRequest.HostPath = sharePath;
2026-09-25 23:24:26 -07:00
shareRequest.Options = VmPlan9ShareOptions{};
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->AddFileSystemShare(fileSystemDevice.Id, shareRequest); }));
2026-09-25 23:24:26 -07:00
shareRequest.Options = VmVirtioFsShareOptions{};
auto& shareOptions = std::get<VmVirtioFsShareOptions>(shareRequest.Options);
shareOptions.MountOptions.emplace(L"unsupported", L"");
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->AddFileSystemShare(fileSystemDevice.Id, shareRequest); }));
shareOptions.MountOptions.clear();
2026-09-28 14:55:51 -07:00
const auto share = backend->AddFileSystemShare(fileSystemDevice.Id, shareRequest);
2026-09-30 15:45:13 -07:00
const auto servingDevice = backend->GetFileSystemDeviceStatus(fileSystemDevice.Id);
VERIFY_ARE_EQUAL(VmFileSystemDeviceState::Prepared, fileSystemDevice.State);
VERIFY_ARE_EQUAL(VmFileSystemDeviceState::Serving, servingDevice.State);
VERIFY_ARE_EQUAL(fileSystemTransport.Tag, std::get<VmVirtioFsDevice>(servingDevice.Transport).Tag);
2026-09-30 17:14:20 -07:00
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, share.Id.Owner.VmId));
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(fileSystemDevice.Id.Value, share.Device.Value);
2026-09-25 23:24:26 -07:00
VERIFY_ARE_EQUAL(fileSystemTransport.Tag, std::get<VmVirtioFsShareAddress>(share.GuestAddress).Tag);
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), OperationResult([&] {
backend->AddFileSystemShare(fileSystemDevice.Id, shareRequest);
}));
auto invalidShare = share.Id;
THROW_IF_FAILED(CoCreateGuid(&invalidShare.Owner.VmId));
VERIFY_ARE_EQUAL(E_INVALIDARG, OperationResult([&] { backend->RemoveFileSystemShare(invalidShare); }));
backend->RemoveFileSystemShare(share.Id);
2026-09-30 15:45:13 -07:00
VERIFY_ARE_EQUAL(VmFileSystemDeviceState::Prepared, backend->GetFileSystemDeviceStatus(fileSystemDevice.Id).State);
VERIFY_ARE_EQUAL(VmFileSystemDeviceState::Serving, servingDevice.State);
2026-09-28 14:55:51 -07:00
const auto replacementShare = backend->AddFileSystemShare(fileSystemDevice.Id, shareRequest);
VERIFY_ARE_NOT_EQUAL(share.Id.Value, replacementShare.Id.Value);
backend->RemoveFileSystemShare(replacementShare.Id);
const auto network = backend->GetDescription().NetworkAdapters.at(networkRequest.Tag);
2026-09-30 17:14:20 -07:00
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, network.Id.Owner.VmId));
2026-09-28 14:55:51 -07:00
VERIFY_IS_TRUE(network.GuestInstanceId.has_value());
VERIFY_ARE_EQUAL(networkRequest.Tag, network.Tag);
VERIFY_ARE_NOT_EQUAL(network.Id.Value, fileSystemDevice.Id.Value);
2026-09-29 14:40:39 -07:00
VERIFY_ARE_EQUAL(
std::get<VmUserModeNatNetwork>(networkRequest.Configuration).Configuration.clientIp.value,
std::get<VmUserModeNatNetwork>(network.EffectiveConfiguration).Configuration.clientIp.value);
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->AddNetworkAdapter(networkRequest); }));
// Consomme processes port requests once the guest has initialized its network queues.
2026-10-02 18:57:05 -07:00
auto guest = StartGuest(*backend);
2026-09-28 14:55:51 -07:00
VmPortBindingRequest bindingRequest;
2026-09-29 14:40:39 -07:00
bindingRequest.ListenAddress.family = IpAddressFamily_V4;
const auto loopbackAddress = htonl(INADDR_LOOPBACK);
std::memcpy(bindingRequest.ListenAddress.bytes, &loopbackAddress, sizeof(loopbackAddress));
bindingRequest.HostPort = ReserveTcpPort();
2026-09-28 14:55:51 -07:00
bindingRequest.GuestPort = 80;
VERIFY_ARE_EQUAL(
HRESULT_FROM_WIN32(ERROR_NOT_FOUND), OperationResult([&] { backend->BindPort(fileSystemDevice.Id, bindingRequest); }));
auto invalidNetwork = network.Id;
THROW_IF_FAILED(CoCreateGuid(&invalidNetwork.Owner.VmId));
VERIFY_ARE_EQUAL(E_INVALIDARG, OperationResult([&] { backend->BindPort(invalidNetwork, bindingRequest); }));
const auto binding = backend->BindPort(network.Id, bindingRequest);
2026-09-30 17:14:20 -07:00
VERIFY_IS_TRUE(IsEqualGUID(request.Identity.VmId, binding.Id.Owner.VmId));
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(network.Id.Value, binding.Device.Value);
2026-09-29 14:40:39 -07:00
VERIFY_ARE_EQUAL(bindingRequest.HostPort, binding.EffectiveHostPort);
2026-09-28 14:55:51 -07:00
auto dynamicBinding = bindingRequest;
2026-09-29 14:40:39 -07:00
dynamicBinding.HostPort = 0;
2026-09-28 14:55:51 -07:00
VERIFY_ARE_EQUAL(c_notSupported, OperationResult([&] { backend->BindPort(network.Id, dynamicBinding); }));
backend->UnbindPort(binding.Id);
VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), OperationResult([&] { backend->UnbindPort(binding.Id); }));
2026-09-30 17:14:20 -07:00
backend->BindPort(network.Id, bindingRequest);
VERIFY_ARE_EQUAL(E_FAIL, OperationResult([&] { backend->BindPort(network.Id, bindingRequest); }));
2026-09-28 14:55:51 -07:00
backend->Terminate();
}
TEST_METHOD(ManagesGuestListenerLifetime)
{
SKIP_TEST_ARM64();
auto backend = OpenVmmVirtualMachineBackend::Create(CreateRunnableRequest());
const auto listener = backend->CreateGuestListener({50000});
VERIFY_ARE_EQUAL(
HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS), OperationResult([&] { backend->CreateGuestListener(listener.Port); }));
backend->CloseGuestListener(listener.Id);
VERIFY_ARE_EQUAL(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), OperationResult([&] { backend->CloseGuestListener(listener.Id); }));
const auto replacement = backend->CreateGuestListener(listener.Port);
VERIFY_ARE_NOT_EQUAL(listener.Id.Value, replacement.Id.Value);
backend->CloseGuestListener(replacement.Id);
backend->Terminate();
}
TEST_METHOD(CapabilitiesReflectVmServiceProtocol)
{
const auto capabilities = OpenVmmVirtualMachineBackend::QueryCapabilities();
VERIFY_ARE_EQUAL(BackendKind::OpenVmm, capabilities.Backend);
decltype(capabilities.Features) expectedFeatures;
for (const auto feature :
2026-10-06 06:20:56 -07:00
{VmFeature::SerialConsole,
VmFeature::VirtioFsFileBacked,
VmFeature::UserModeNatNetwork,
VmFeature::TcpPortBinding,
VmFeature::UdpPortBinding,
VmFeature::Ipv6PortBinding,
VmFeature::ScopedIpv6PortBinding})
{
expectedFeatures.set(static_cast<size_t>(feature));
}
VERIFY_IS_TRUE(capabilities.Features == expectedFeatures);
}
};
2026-09-28 14:55:51 -07:00
} // namespace OpenVmmVirtualMachineBackendTests