Initial Image pull, load and import support (#13871)

This commit is contained in:
yao-msft
2025-12-19 22:31:40 -08:00
committed by GitHub
parent 98ba3c4116
commit 01b1d9456f
14 changed files with 222 additions and 14 deletions

View File

@@ -407,6 +407,9 @@ stages:
$TestDistroVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.WSL.TestDistro'']/@version').Node.Value
Copy-Item "packages\Microsoft.WSL.TestDistro.$TestDistroVersion\test_distro.tar.xz" "$(ob_outputDirectory)\testbin\x64"
$TestDataVersion = (Select-Xml -Path packages.config -XPath '/packages/package[@id=''Microsoft.WSL.TestData'']/@version').Node.Value
Copy-Item "packages\Microsoft.WSL.TestData.$TestDataVersion\build\native\bin\x64" "$(ob_outputDirectory)\testbin\x64\test_data" -Recurse -Force
displayName: Move artifacts to drop directory
- task: PublishSymbols@2

View File

@@ -65,6 +65,7 @@ find_nuget_package(Microsoft.WSL.Kernel KERNEL /build/native)
find_nuget_package(Microsoft.WSL.bsdtar BSDTARD /build/native/bin)
find_nuget_package(Microsoft.WSL.LinuxSdk LINUXSDK /)
find_nuget_package(Microsoft.WSL.TestDistro TEST_DISTRO /)
find_nuget_package(Microsoft.WSL.TestData WSL_TEST_DATA /build/native/bin/${TARGET_PLATFORM})
find_nuget_package(Microsoft.WSL.WslaRootfs WSLA_TEST_DISTRO /build/native/bin/${TARGET_PLATFORM})
find_nuget_package(Microsoft.WSLg WSLG /build/native/bin)
find_nuget_package(StrawberryPerl PERL /)

View File

@@ -8,6 +8,7 @@
<Copy Src="[drop]\bundle\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle" Dest="[WorkingDirectory]" IsRecursive="false"/>
<Copy Src="[drop]\testbin\${TARGET_PLATFORM}\release\*" Dest="[WorkingDirectory]\" IsRecursive="true" Writable="true"/>
<Copy Src="[drop]\testbin\${TARGET_PLATFORM}\test_distro.tar.xz" Dest="[WorkingDirectory]" IsRecursive="false" Writable="true"/>
<Copy Src="[drop]\testbin\${TARGET_PLATFORM}\test_data\*" Dest="[WorkingDirectory]\test_data" IsRecursive="true" Writable="true"/>
<Copy Src="[drop]\testbin\test-setup.ps1" Dest="[WorkingDirectory]\" IsRecursive="false" />
<Copy Src="[drop]\testbin\CloudTest-Setup.bat" Dest="[WorkingDirectory]\" IsRecursive="false" />
<Copy Src="[drop]\testbin\wsl.wprp" Dest="[WorkingDirectory]\" IsRecursive="false" />
@@ -21,6 +22,6 @@
</Setup>
<TestJob Name="CloudTest.Taef" TimeoutMins="240">
<Execution Type="TAEF" Path="[WorkingDirectory]\wsltests.dll" Args="/p:bugReportDirectory=[LoggingDirectory]\BugReportOutput /errorOnCrash /testmode:etwlogger /EtwLogger:WPRProfileFile=[WorkingDirectory]\wsl.wprp /EtwLogger:WPRProfile=WSL /EtwLogger:SavePoint=ExecutionComplete /EtwLogger:RecordingScope=Execution /p:SetupScript=.\test-setup.ps1 /p:Package=[WorkingDirectory]\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle /p:Version=${version} /p:AllowUnsigned=${ALLOW_UNSIGNED_PACKAGE} /p:UnitTestsPath=[WorkingDirectory]\unit_tests /p:DistroPath=[WorkingDirectory]\test_distro.tar.xz /p:DistroName=test_distro /logOutput:High /p:RedirectStdout=[LoggingDirectory]\stdout.txt /p:RedirectStderr=[LoggingDirectory]\stderr.txt /p:KernelLogs=[LoggingDirectory]\dmesg.txt /p:DumpFolder=[LoggingDirectory] /p:WerReport /p:LogDmesg /p:PipelineBuildId=${PIPELINE_BUILD_ID} /p:DumpTool=DumpTool.exe" />
<Execution Type="TAEF" Path="[WorkingDirectory]\wsltests.dll" Args="/p:bugReportDirectory=[LoggingDirectory]\BugReportOutput /errorOnCrash /testmode:etwlogger /EtwLogger:WPRProfileFile=[WorkingDirectory]\wsl.wprp /EtwLogger:WPRProfile=WSL /EtwLogger:SavePoint=ExecutionComplete /EtwLogger:RecordingScope=Execution /p:SetupScript=.\test-setup.ps1 /p:Package=[WorkingDirectory]\Microsoft.WSL_${PACKAGE_VERSION}_x64_ARM64.msixbundle /p:Version=${version} /p:AllowUnsigned=${ALLOW_UNSIGNED_PACKAGE} /p:UnitTestsPath=[WorkingDirectory]\unit_tests /p:DistroPath=[WorkingDirectory]\test_distro.tar.xz /p:TestDataPath=[WorkingDirectory]\test_data /p:DistroName=test_distro /logOutput:High /p:RedirectStdout=[LoggingDirectory]\stdout.txt /p:RedirectStderr=[LoggingDirectory]\stderr.txt /p:KernelLogs=[LoggingDirectory]\dmesg.txt /p:DumpFolder=[LoggingDirectory] /p:WerReport /p:LogDmesg /p:PipelineBuildId=${PIPELINE_BUILD_ID} /p:DumpTool=DumpTool.exe" />
</TestJob>
</TestJobGroup>

View File

@@ -21,6 +21,7 @@
<package id="Microsoft.WSL.DeviceHost" version="1.1.8-0" />
<package id="Microsoft.WSL.Kernel" version="6.6.114.1-1" targetFramework="native" />
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
<package id="Microsoft.WSL.TestData" version="0.1.0" />
<package id="Microsoft.WSL.TestDistro" version="2.5.7-47" />
<package id="Microsoft.WSL.WslaRootfs" version="0.1.2" />
<package id="Microsoft.WSLg" version="1.0.71" />

View File

@@ -19,6 +19,7 @@ Abstract:
#include "ServiceProcessLauncher.h"
#include "WslCoreFilesystem.h"
using namespace wsl::windows::common;
using wsl::windows::service::wsla::WSLASession;
using wsl::windows::service::wsla::WSLAVirtualMachine;
@@ -292,15 +293,84 @@ try
}
CATCH_LOG();
HRESULT WSLASession::PullImage(LPCWSTR Image, const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, IProgressCallback* ProgressCallback)
HRESULT WSLASession::PullImage(LPCSTR ImageUri, const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryAuthenticationInformation, IProgressCallback* ProgressCallback)
try
{
return E_NOTIMPL;
}
UNREFERENCED_PARAMETER(RegistryAuthenticationInformation);
UNREFERENCED_PARAMETER(ProgressCallback);
HRESULT WSLASession::ImportImage(ULONG Handle, LPCWSTR Image, IProgressCallback* ProgressCallback)
{
return E_NOTIMPL;
RETURN_HR_IF_NULL(E_POINTER, ImageUri);
std::lock_guard lock{m_lock};
ServiceProcessLauncher launcher{nerdctlPath, {nerdctlPath, "pull", ImageUri}};
auto result = launcher.Launch(*m_virtualMachine.Get()).WaitAndCaptureOutput();
RETURN_HR_IF_MSG(E_FAIL, result.Code != 0, "Pull image failed: %hs", launcher.FormatResult(result).c_str());
return S_OK;
}
CATCH_RETURN();
HRESULT WSLASession::LoadImage(ULONG ImageHandle, IProgressCallback* ProgressCallback)
try
{
UNREFERENCED_PARAMETER(ProgressCallback);
wil::unique_handle imageFileHandle{wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(ImageHandle))};
RETURN_HR_IF(E_INVALIDARG, INVALID_HANDLE_VALUE == imageFileHandle.get());
std::lock_guard lock{m_lock};
// Directly invoking "nerdctl load" will immediately return with failure
// "stdin is empty and input flag is not specified".
// TODO: Change the workaround when nerdctl has a fix.
ServiceProcessLauncher launcher{
"/bin/sh", {"/bin/sh", "-c", "cat | /usr/bin/nerdctl load"}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout | ProcessFlags::Stderr};
auto loadProcess = launcher.Launch(*m_virtualMachine.Get());
auto loadProcessStdin = loadProcess.GetStdHandle(0);
// TODO: Create a new OverlappedIOHandle that relays a handle to process's stdin.
wsl::windows::common::relay::InterruptableRelay(
imageFileHandle.get(), loadProcessStdin.get(), m_sessionTerminatingEvent.get(), 4 * 1024 * 1024 /* 4MB buffer */);
loadProcessStdin.reset();
auto result = loadProcess.WaitAndCaptureOutput();
RETURN_HR_IF_MSG(E_FAIL, result.Code != 0, "Load image failed: %hs", launcher.FormatResult(result).c_str());
return S_OK;
}
CATCH_RETURN();
HRESULT WSLASession::ImportImage(ULONG ImageHandle, LPCSTR ImageName, IProgressCallback* ProgressCallback)
try
{
UNREFERENCED_PARAMETER(ProgressCallback);
wil::unique_handle imageFileHandle{wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(ImageHandle))};
RETURN_HR_IF(E_INVALIDARG, INVALID_HANDLE_VALUE == imageFileHandle.get());
RETURN_HR_IF_NULL(E_POINTER, ImageName);
std::lock_guard lock{m_lock};
ServiceProcessLauncher launcher{
nerdctlPath, {nerdctlPath, "import", "-", ImageName}, {}, ProcessFlags::Stdin | ProcessFlags::Stdout | ProcessFlags::Stderr};
auto importProcess = launcher.Launch(*m_virtualMachine.Get());
auto importProcessStdin = importProcess.GetStdHandle(0);
// TODO: Create a new OverlappedIOHandle that relays a handle to process's stdin.
wsl::windows::common::relay::InterruptableRelay(
imageFileHandle.get(), importProcessStdin.get(), m_sessionTerminatingEvent.get(), 4 * 1024 * 1024 /* 4MB buffer */);
importProcessStdin.reset();
auto result = importProcess.WaitAndCaptureOutput();
RETURN_HR_IF_MSG(E_FAIL, result.Code != 0, "Import image failed: %hs", launcher.FormatResult(result).c_str());
return S_OK;
}
CATCH_RETURN();
HRESULT WSLASession::ListImages(WSLA_IMAGE_INFORMATION** Images, ULONG* Count)
{

View File

@@ -36,8 +36,12 @@ public:
void CopyDisplayName(_Out_writes_z_(bufferLength) PWSTR buffer, size_t bufferLength) const;
// Image management.
IFACEMETHOD(PullImage)(_In_ LPCWSTR Image, _In_ const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, _In_ IProgressCallback* ProgressCallback) override;
IFACEMETHOD(ImportImage)(_In_ ULONG Handle, _In_ LPCWSTR Image, _In_ IProgressCallback* ProgressCallback) override;
IFACEMETHOD(PullImage)(
_In_ LPCSTR ImageUri,
_In_ const WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryAuthenticationInformation,
_In_ IProgressCallback* ProgressCallback) override;
IFACEMETHOD(LoadImage)(_In_ ULONG ImageHandle, _In_ IProgressCallback* ProgressCallback) override;
IFACEMETHOD(ImportImage)(_In_ ULONG ImageHandle, _In_ LPCSTR ImageName, _In_ IProgressCallback* ProgressCallback) override;
IFACEMETHOD(ListImages)(_Out_ WSLA_IMAGE_INFORMATION** Images, _Out_ ULONG* Count) override;
IFACEMETHOD(DeleteImage)(_In_ LPCWSTR Image) override;

View File

@@ -294,8 +294,9 @@ interface IWSLAContainer : IUnknown
interface IWSLASession : IUnknown
{
// Image management.
HRESULT PullImage([in] LPCWSTR Image, [in, unique] const struct WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryInformation, [in, unique] IProgressCallback* ProgressCallback);
HRESULT ImportImage([in] ULONG Handle, [in] LPCWSTR Image, [in, unique] IProgressCallback* ProgressCallback);
HRESULT PullImage([in] LPCSTR ImageUri, [in, unique] const struct WSLA_REGISTRY_AUTHENTICATION_INFORMATION* RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback);
HRESULT LoadImage([in] ULONG ImageHandle, [in, unique] IProgressCallback* ProgressCallback);
HRESULT ImportImage([in] ULONG ImageHandle, [in] LPCSTR ImageName, [in, unique] IProgressCallback* ProgressCallback);
HRESULT ListImages([out, size_is(, *Count)] struct WSLA_IMAGE_INFORMATION** Images, [out] ULONG* Count);
HRESULT DeleteImage([in] LPCWSTR Image);

View File

@@ -65,6 +65,7 @@ std::optional<std::wstring> g_dumpToolPath;
static bool g_enableWerReport = false;
static std::wstring g_pipelineBuildId;
std::wstring g_testDistroPath;
std::wstring g_testDataPath;
std::pair<wil::unique_handle, wil::unique_handle> CreateSubprocessPipe(bool inheritRead, bool inheritWrite, DWORD bufferSize, _In_opt_ SECURITY_ATTRIBUTES* sa)
{
@@ -2064,6 +2065,8 @@ Return Value:
g_testDistroPath = getTestParam(L"DistroPath");
g_testDataPath = getTestParam(L"TestDataPath");
const auto setupScript = getOptionalTestParam(L"SetupScript");
if (!setupScript.has_value())
{

View File

@@ -33,6 +33,8 @@ DEFINE_ENUM_FLAG_OPERATORS(WSLAFeatureFlags);
static std::filesystem::path storagePath;
extern std::wstring g_testDataPath;
class WSLATests
{
WSL_TEST_CLASS(WSLATests)
@@ -269,6 +271,71 @@ class WSLATests
VERIFY_ARE_EQUAL(hr, HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
}
TEST_METHOD(PullImage)
{
WSL2_TEST_ONLY();
auto settings = GetDefaultSessionSettings();
settings.DisplayName = L"wsla-pull-image-test";
settings.NetworkingMode = WSLANetworkingModeNAT;
auto session = CreateSession(settings);
VERIFY_SUCCEEDED(session->PullImage("hello-world", nullptr, nullptr));
// Verify that the image is in the list of images.
WSLAProcessLauncher launcher("/usr/bin/nerdctl", {"/usr/bin/nerdctl", "images"});
auto listImagesResult = launcher.Launch(*session).WaitAndCaptureOutput();
VERIFY_ARE_EQUAL(0, listImagesResult.Code);
VERIFY_IS_TRUE(listImagesResult.Output[1].find("hello-world") != std::string::npos);
}
TEST_METHOD(LoadImage)
{
WSL2_TEST_ONLY();
auto settings = GetDefaultSessionSettings();
settings.DisplayName = L"wsla-load-image-test";
auto session = CreateSession(settings);
std::filesystem::path imageTar = std::filesystem::path{g_testDataPath} / L"HelloWorldSaved.tar";
wil::unique_handle imageTarFileHandle{
CreateFileW(imageTar.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
VERIFY_IS_FALSE(INVALID_HANDLE_VALUE == imageTarFileHandle.get());
VERIFY_SUCCEEDED(session->LoadImage(HandleToULong(imageTarFileHandle.get()), nullptr));
// Verify that the image is in the list of images.
WSLAProcessLauncher launcher("/usr/bin/nerdctl", {"/usr/bin/nerdctl", "images"});
auto listImagesResult = launcher.Launch(*session).WaitAndCaptureOutput();
VERIFY_ARE_EQUAL(0, listImagesResult.Code);
VERIFY_IS_TRUE(listImagesResult.Output[1].find("hello-world") != std::string::npos);
}
TEST_METHOD(ImportImage)
{
WSL2_TEST_ONLY();
auto settings = GetDefaultSessionSettings();
settings.DisplayName = L"wsla-import-image-test";
auto session = CreateSession(settings);
std::filesystem::path imageTar = std::filesystem::path{g_testDataPath} / L"HelloWorldExported.tar";
wil::unique_handle imageTarFileHandle{
CreateFileW(imageTar.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)};
VERIFY_IS_FALSE(INVALID_HANDLE_VALUE == imageTarFileHandle.get());
VERIFY_SUCCEEDED(session->ImportImage(HandleToULong(imageTarFileHandle.get()), "my-hello-world:test", nullptr));
// Verify that the image is in the list of images.
WSLAProcessLauncher launcher("/usr/bin/nerdctl", {"/usr/bin/nerdctl", "images"});
auto listImagesResult = launcher.Launch(*session).WaitAndCaptureOutput();
VERIFY_ARE_EQUAL(0, listImagesResult.Code);
VERIFY_IS_TRUE(listImagesResult.Output[1].find("my-hello-world") != std::string::npos);
}
TEST_METHOD(CustomDmesgOutput)
{
WSL2_TEST_ONLY();

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
<metadata>
<id>Microsoft.WSL.TestData</id>
<version>$version$</version>
<authors>Microsoft</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<projectUrl>https://github.com/microsoft/WSL</projectUrl>
<description>WSL Test Data Files</description>
</metadata>
<files>
<file src="**\*.*" target="" />
</files>
</package>

View File

@@ -0,0 +1,30 @@
<#
.SYNOPSIS
Helper to pack WSL test data nuget.
.PARAMETER InputDirectory
Directory containing the test data to be packaged.
.PARAMETER Version
Nuget package version.
.PARAMETER OutputDirectory
Directory to place the packaged nuget file. Default to current working directory.
#>
[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='vm')]
param (
[Parameter(Mandatory = $true)][string]$InputDirectory,
[Parameter(Mandatory = $true)][string]$Version,
[string]$OutputDirectory = $PWD.Path
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
if (-not (Test-Path -Path $InputDirectory -PathType Container)) {
throw("The path '$InputDirectory' is not an existing directory.")
}
echo "Building test data nuget. Input: $InputDirectory. Version: $Version"
Copy-Item -Path "$PSScriptRoot\Microsoft.WSL.TestData.nuspec" -Destination "$InputDirectory" -Force
& "$PSScriptRoot\..\..\_deps\nuget.exe" pack "$InputDirectory\Microsoft.WSL.TestData.nuspec" -Properties "version=$Version" -OutputDirectory "$OutputDirectory"

View File

@@ -9,6 +9,8 @@
Path to a setup script to be run prior to running the tests. Defaults to ".\test-setup.ps1".
.PARAMETER DistroPath
Path to a .tar/.tar.gz file of the distro to be imported to run the tests with. Defaults to ".\test_distro.tar.gz".
.PARAMETER TestDataPath
Path to test data folder. Defaults to ".\test_data".
.PARAMETER Package
Path to the wsl.msix package to install. Defaults to ".\wsl.msix".
.PARAMETER UnitTestsPath
@@ -28,6 +30,7 @@ param (
[string]$Version = 2,
[string]$SetupScript = ".\test-setup.ps1",
[string]$DistroPath = ".\test_distro.tar.gz",
[string]$TestDataPath = ".\test_data",
[string]$Package = ".\installer.msix",
[string]$UnitTestsPath = ".\unit_tests",
[switch]$PullRequest = $false,
@@ -45,7 +48,7 @@ if ($Fast)
$SetupScript = $null
}
te.exe $TestDllPath /p:SetupScript=$SetupScript /p:Version=$Version /p:DistroPath=$DistroPath /p:Package=$Package /p:UnitTestsPath=$UnitTestsPath /p:PullRequest=$PullRequest /p:AllowUnsigned=1 @TeArgs
te.exe $TestDllPath /p:SetupScript=$SetupScript /p:Version=$Version /p:DistroPath=$DistroPath /p:TestDataPath=$TestDataPath /p:Package=$Package /p:UnitTestsPath=$UnitTestsPath /p:PullRequest=$PullRequest /p:AllowUnsigned=1 @TeArgs
if (!$?)
{

View File

@@ -29,6 +29,8 @@
Skip copying over the distro.
.PARAMETER TestDistroPath
Path to the distro image to import and use for testing, if needed. Auto filled if left empty.
.PARAMETER TestDataPath
Path to the test data folder to be copied to the VM, if needed. Auto filled if left empty.
#>
[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='vm')]
@@ -46,7 +48,8 @@ param (
[string]$RemoteFolder = "C:\Package",
[string]$TaefFolder = "C:\Taef",
[switch]$SkipDistro,
[string]$TestDistroPath
[string]$TestDistroPath,
[string]$TestDataPath
)
$ErrorActionPreference = "Stop"
@@ -98,6 +101,11 @@ if ([string]::IsNullOrEmpty($TestDistroPath)) {
$TestDistroPath = "$PSScriptRoot\..\..\packages\Microsoft.WSL.TestDistro.$TestDistroVersion\test_distro.tar.xz"
}
if ([string]::IsNullOrEmpty($TestDataPath)) {
$TestDataVersion = (Select-Xml -Path "$PSScriptRoot\..\..\packages.config" -XPath '/packages/package[@id=''Microsoft.WSL.TestData'']/@version').Node.Value
$TestDataPath = "$PSScriptRoot\..\..\packages\Microsoft.WSL.TestData.$TestDataVersion\build\native\bin\x64"
}
if ([string]::IsNullOrEmpty($ArtifactFolder)) {
$ArtifactFolder = "$PSScriptRoot/../.."
}
@@ -141,6 +149,8 @@ if (!$SkipDistro) {
Copy-Item -ToSession $Session -Path $TestDistroPath -Destination "$RemoteFolder/test_distro.tar.gz" -Force
}
Copy-Item -ToSession $Session -Path $TestDataPath -Destination "$RemoteFolder/test_data" -Recurse -Force
$taefVersion = (Select-Xml -Path "$PSScriptRoot\..\..\packages.config" -XPath '/packages/package[@id=''Microsoft.Taef'']/@version').Node.Value
$taefPackage = "$ArtifactFolder/packages/Microsoft.Taef.$taefVersion/build/Binaries/$Platform"
Copy-Item -ToSession $Session -Path "$taefPackage" -Destination $TaefFolder -Recurse -Force

View File

@@ -6,7 +6,7 @@ set "Bin=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}\${CMAKE_BUILD_TYPE}"
set "Tools=${CMAKE_CURRENT_LIST_DIR}\tools"
path %PATH%;${TAEF_SOURCE_DIR}\build\Binaries\${TARGET_PLATFORM}
powershell.exe -ExecutionPolicy Bypass "%tools%\test\run-tests.ps1" -SetupScript "%tools%\test\test-setup.ps1" -DistroPath "${TEST_DISTRO_SOURCE_DIR}test_distro.tar.xz" -TestDllPath "%bin%\wsltests.dll" -UnitTestsPath "${CMAKE_CURRENT_LIST_DIR}\test\linux\unit_tests" -Package "%bin%\installer.msix" %* || goto fail
powershell.exe -ExecutionPolicy Bypass "%tools%\test\run-tests.ps1" -SetupScript "%tools%\test\test-setup.ps1" -DistroPath "${TEST_DISTRO_SOURCE_DIR}test_distro.tar.xz" -TestDataPath "${WSL_TEST_DATA_SOURCE_DIR}" -TestDllPath "%bin%\wsltests.dll" -UnitTestsPath "${CMAKE_CURRENT_LIST_DIR}\test\linux\unit_tests" -Package "%bin%\installer.msix" %* || goto fail
exit /b 0