Repository: containers/podman (Go)

Issue:
Command `machine init` fails to check if WSL is installed
### Issue Description

Podman tries to import the machine-os into WSL even when it's impossible because the Windows features `Microsoft-Windows-Subsystem-Linux` and `VirtualMachinePlatform` aren't enabled.

This is a regression observed with the latest version of the [WSL client](https://github.com/microsoft/WSL) (2.3.26.0 or later).

### Steps to reproduce the issue

Steps to reproduce the issue
1. Make sure to have latest version WSL (`wsl --update`)
2. Disable the Windows features `Microsoft-Windows-Subsystem-Linux` and `VirtualMachinePlatform`
3. Run `podman machine init`


### Describe the results you received

```log
Extracting compressed file: podman-machine-default-amd64: done
Importing operating system into WSL (this may take a few minutes on a new WSL install)...
Import in progress, this may take a few minutes.
The operation could not be started because a required feature is not installed.
Error code: Wsl/Service/RegisterDistro/CreateVm/HCS/HCS_E_SERVICE_NOT_AVAILABLE
Error: the WSL import of guest OS failed: command C:\Users\mario\AppData\Local\Microsoft\WindowsApps\wsl.exe [--import podman-machine-default C:\Users\mario\.local\share\containers\podman\machine\wsl\wsldist\podman-machine-default C:\Users\mario\.local\share\containers\podman\machine\wsl\podman-machine-default-amd64 --version 2] failed: exit status 0xffffffff
```

### Describe the results you expected

The command should NOT try to import the machine OS in WSL and [propose to install WSL to the user](https://github.com/containers/podman/blob/4bd6bc9f3ccac4d3bd79c4a3eb8fb09e05604a06/pkg/machine/wsl/machine.go#L356):

```log
WSL is not installed on this system, installing it.

NOTE: A system reboot will be required as part of this process. 
If you prefer, you may abort now, and perform a manual installation using the "wsl --install" command.
```

### podman info output

```yaml
OS: windows/amd64
provider: wsl
version: 5.4.0-dev
```

### Podman in a container

No

### Privileged Or Rootless

None

### Upstream Latest Release

Yes

### Additional environment details

Additional environment details

### Additional information

Additional information like issue happens only occasionally or issue happens with a particular architecture or on a particular setting


Discussion:
Currently, there is only one way to directly determine whether a certain feature is enabled in Windows: 

```shell
Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
```

However, this method requires administrative privileges, and asking users for administrative permissions at every startup is generally unacceptable.

We encountered this in our requirements as well. After testing, we found a workaround to make the judgment, and the relevant code is:

https://github.com/oomol-lab/ovm-win/blob/0c27841224d2daa8f96d789ee3e1a32105858f7d/pkg/wsl/check.go#L243-L294

I'm not sure if podman needs a check like this. If it does, I can submit a related PR.

By the way, just checking the Feature is not enough. It's also necessary to check if BIOS virtualization is enabled, if the WSL version is too low, and if the user has modified the kernel configuration in .wslconfig.


Thank you @BlackHole1. I am about to submit a PR that uses a similar approach (look for sentences in `wsl --status`), and I will cc you. I would be happy if you could look at it. My approach has one main difference: from my tests, the `VirtualMachinePlatform` is sufficient to run WSL2, and the `Microsoft-Windows-Subsystem-Linux` is no longer required.



Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index 00046172e34..d62e8d3e678 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -558,6 +558,44 @@ unit_test_task:
     always: *logs_artifacts
 
 
+unit_test_windows_task:
+    name: "Unit tests on Windows"
+    alias: unit_test_windows
+    # Docs: ./contrib/cirrus/CIModes.md (Cirrus Task contexts and runtime modes)
+    # only when: - main rules (see doc above); or
+    #            - unit test files are changed (contains a false positves such as test/e2e/
+    #              but that should not be an issue, it only runs when it doesn't have to)
+    #            - actual source code changed
+    only_if: >-
+        $CIRRUS_PR == '' ||
+        $CIRRUS_CHANGE_TITLE =~ '.*CI:ALL.*' ||
+        changesInclude('.cirrus.yml', 'Makefile', 'contrib/cirrus/**', 'vendor/**', 'test/tools/**', 'test/registries*.conf', 'hack/**', 'version/rawversion/*') ||
+        changesInclude('winmake.ps1') ||
+        changesInclude('**/*_test.go') ||
+        (changesInclude('**/*.go', '**/*.c', '**/*.h') && !changesIncludeOnly('test/**', 'pkg/machine/e2e/**'))
+    # Special case, we do not run macos/windows builds on rhel branches.
+    # Thus the machine task should not be run too, while we use only_if
+    # everywhere to do so here it would mean we would need duplicate the
+    # full big only_if condition which is more difficult to maintain so
+    # use the skip here.
+    skip: &skip_rhel_release |
+        $CIRRUS_BRANCH =~ 'v[0-9\.]+-rhel' ||
+        $CIRRUS_BASE_BRANCH =~ 'v[0-9\.]+-rhel'
+    depends_on: *build
+    ec2_instance: *windows
+    timeout_in: 20m
+    env:
+         <<: *winenv
+         TEST_FLAVOR: unit
+    clone_script: *winclone
+    main_script: ".\\repo\\contrib\\cirrus\\win-unit-main.ps1"
+    always:
+        # Required for `contrib/cirrus/logformatter` to work properly
+        html_artifacts:
+            path: ./*.html
+            type: text/html
+
+
 apiv2_test_task:
     name: "APIv2 test on $DISTRO_NV ($PRIV_NAME)"
     alias: apiv2_test
@@ -760,9 +798,7 @@ podman_machine_windows_task:
     # everywhere to do so here it would mean we would need duplicate the
     # full big only_if condition which is more difficult to maintain so
     # use the skip here.
-    skip: &skip_rhel_release |
-        $CIRRUS_BRANCH =~ 'v[0-9\.]+-rhel' ||
-        $CIRRUS_BASE_BRANCH =~ 'v[0-9\.]+-rhel'
+    skip: *skip_rhel_release
     depends_on: *build
     ec2_instance:
         <<: *windows
@@ -1060,6 +1096,7 @@ success_task:
         - win_installer
         - docker-py_test
         - unit_test
+        - unit_test_windows
         - apiv2_test
         - compose_test
         - local_integration_test
diff --git a/Makefile b/Makefile
index 36be6988ee8..44706ee8fee 100644
--- a/Makefile
+++ b/Makefile
@@ -652,7 +652,7 @@ localunit: test/goecho/goecho test/version/version
 	UNIT=1 $(GINKGO) \
 		-r \
 		$(TESTFLAGS) \
-		--skip-package test/e2e,pkg/bindings,hack,pkg/machine/e2e \
+		--skip-package test/e2e,pkg/bindings,hack,pkg/machine/e2e,pkg/machine/wsl \
 		--cover \
 		--covermode atomic \
 		--coverprofile coverprofile \
diff --git a/contrib/cirrus/win-unit-main.ps1 b/contrib/cirrus/win-unit-main.ps1
new file mode 100644
index 00000000000..b945f4a1550
--- /dev/null
+++ b/contrib/cirrus/win-unit-main.ps1
@@ -0,0 +1,13 @@
+#!/usr/bin/env powershell
+
+. $PSScriptRoot\win-lib.ps1
+
+if ($Env:CI -eq "true") {
+    Push-Location "$ENV:CIRRUS_WORKING_DIR\repo"
+} else {
+    Push-Location $PSScriptRoot\..\..
+}
+
+Run-Command ".\winmake.ps1 localunit"
+
+Pop-Location
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 3355f6b2b5d..73650c18eae 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -317,12 +317,12 @@ func checkAndInstallWSL(reExec bool) (bool, error) {
 
 	admin := HasAdminRights()
 
-	if !IsWSLFeatureEnabled() {
+	if !wutil.IsWSLFeatureEnabled() {
 		return false, attemptFeatureInstall(reExec, admin)
 	}
 
 	skip := false
-	if reExec && !admin {
+	if !reExec && !admin {
 		fmt.Println("Launching WSL Kernel Install...")
 		if err := launchElevate(wslInstallKernel); err != nil {
 			return false, err
@@ -363,11 +363,11 @@ func attemptFeatureInstall(reExec, admin bool) error {
 	message += "NOTE: A system reboot will be required as part of this process. " +
 		"If you prefer, you may abort now, and perform a manual installation using the \"wsl --install\" command."
 
-	if reExec && MessageBox(message, "Podman Machine", false) != 1 {
+	if !reExec && MessageBox(message, "Podman Machine", false) != 1 {
 		return errors.New("the WSL installation aborted")
 	}
 
-	if reExec && !admin {
+	if !reExec && !admin {
 		return launchElevate("install the Windows WSL Features")
 	}
 
@@ -622,10 +622,6 @@ func obtainGlobalConfigLock() (*fileLock, error) {
 	return lockFile(filepath.Join(lockDir, "podman-config.lck"))
 }
 
-func IsWSLFeatureEnabled() bool {
-	return wutil.SilentExec(wutil.FindWSL(), "--set-default-version", "2") == nil
-}
-
 func isWSLRunning(dist string) (bool, error) {
 	return wslCheckExists(dist, true)
 }
diff --git a/pkg/machine/wsl/wutil/wutil.go b/pkg/machine/wsl/wutil/wutil.go
index bc07ffa2a93..49bc638ac42 100644
--- a/pkg/machine/wsl/wutil/wutil.go
+++ b/pkg/machine/wsl/wutil/wutil.go
@@ -19,16 +19,26 @@ import (
 )
 
 var (
-	once    sync.Once
-	wslPath string
+	onceFind, onceStatus    sync.Once
+	wslPath                 string
+	status                  wslStatus
+	wslNotInstalledMessages = []string{"kernel file is not found", "The Windows Subsystem for Linux is not installed"}
+	vmpDisabledMessages     = []string{"enable the Virtual Machine Platform Windows feature", "Enable \"Virtual Machine Platform\""}
+	wslDisabledMessages     = []string{"enable the \"Windows Subsystem for Linux\" optional component"}
 )
 
+type wslStatus struct {
+	installed         bool
+	vmpFeatureEnabled bool
+	wslFeatureEnabled bool
+}
+
 func FindWSL() string {
 	// At the time of this writing, a defect appeared in the OS preinstalled WSL executable
 	// where it no longer reliably locates the preferred Windows App Store variant.
 	//
 	// Manually discover (and cache) the wsl.exe location to bypass the problem
-	once.Do(func() {
+	onceFind.Do(func() {
 		var locs []string
 
 		// Prefer Windows App Store version
@@ -87,24 +97,44 @@ func SilentExecCmd(command string, args ...string) *exec.Cmd {
 	return cmd
 }
 
-func IsWSLInstalled() bool {
-	cmd := SilentExecCmd(FindWSL(), "--status")
-	out, err := cmd.StdoutPipe()
-	cmd.Stderr = nil
-	if err != nil {
-		return false
-	}
-	if err = cmd.Start(); err != nil {
-		return false
-	}
+func parseWSLStatus() wslStatus {
+	onceStatus.Do(func() {
+		status = wslStatus{
+			installed:         false,
+			vmpFeatureEnabled: false,
+			wslFeatureEnabled: false,
+		}
+		cmd := SilentExecCmd(FindWSL(), "--status")
+		out, err := cmd.StdoutPipe()
+		cmd.Stderr = nil
+		if err != nil {
+			return
+		}
+		if err = cmd.Start(); err != nil {
+			return
+		}
+
+		status = matchOutputLine(out)
+
+		if err := cmd.Wait(); err != nil {
+			return
+		}
+	})
 
-	kernelNotFound := matchOutputLine(out, "kernel file is not found")
+	return status
+}
 
-	if err := cmd.Wait(); err != nil {
+func IsWSLInstalled() bool {
+	status := parseWSLStatus()
+	return status.installed && status.vmpFeatureEnabled
+}
+
+func IsWSLFeatureEnabled() bool {
+	if SilentExec(FindWSL(), "--set-default-version", "2") != nil {
 		return false
 	}
-
-	return !kernelNotFound
+	status := parseWSLStatus()
+	return status.vmpFeatureEnabled
 }
 
 func IsWSLStoreVersionInstalled() bool {
@@ -118,13 +148,30 @@ func IsWSLStoreVersionInstalled() bool {
 	return true
 }
 
-func matchOutputLine(output io.ReadCloser, match string) bool {
+func matchOutputLine(output io.ReadCloser) wslStatus {
+	status := wslStatus{
+		installed:         true,
+		vmpFeatureEnabled: true,
+		wslFeatureEnabled: true,
+	}
 	scanner := bufio.NewScanner(transform.NewReader(output, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
 	for scanner.Scan() {
 		line := scanner.Text()
-		if strings.Contains(line, match) {
-			return true
+		for _, match := range wslNotInstalledMessages {
+			if strings.Contains(line, match) {
+				status.installed = false
+			}
+		}
+		for _, match := range vmpDisabledMessages {
+			if strings.Contains(line, match) {
+				status.vmpFeatureEnabled = false
+			}
+		}
+		for _, match := range wslDisabledMessages {
+			if strings.Contains(line, match) {
+				status.wslFeatureEnabled = false
+			}
 		}
 	}
-	return false
+	return status
 }
diff --git a/winmake.ps1 b/winmake.ps1
index 2491b2cb1f2..3b37c8a2d74 100644
--- a/winmake.ps1
+++ b/winmake.ps1
@@ -44,6 +44,14 @@ function Make-Clean{
     }
 }
 
+function Local-Unit {
+    Build-Ginkgo
+    $skippackages="hack,internal\domain\infra\abi,internal\domain\infra\tunnel,libpod\lock\shm,pkg\api\handlers\libpod,pkg\api\handlers\utils,pkg\bindings,"
+    $skippackages+="pkg\domain\infra\abi,pkg\emulation,pkg\machine\apple,pkg\machine\applehv,pkg\machine\e2e,pkg\machine\libkrun,"
+    $skippackages+="pkg\machine\provider,pkg\machine\proxyenv,pkg\machine\qemu,pkg\specgen\generate,pkg\systemd,test\e2e,test\utils"
+    Run-Command "./test/tools/build/ginkgo.exe -vv -r --tags `"$remotetags`" --timeout=15m --trace --no-color --skip-package `"$skippackages`""
+}
+
 function Local-Machine {
     param (
     [string]$files
@@ -53,7 +61,7 @@ function Local-Machine {
          $files = " --focus-file $files "
     }
 
-    Run-Command "./test/tools/build/ginkgo.exe -vv  --tags `"$remotetags`" -timeout=90m --trace --no-color $files pkg/machine/e2e/."
+    Run-Command "./test/tools/build/ginkgo.exe -vv  --tags `"$remotetags`" --timeout=90m --trace --no-color $files pkg/machine/e2e/."
 }
 
 # Expect starting directory to be /podman
@@ -219,9 +227,7 @@ function Build-Ginkgo{
         return
     }
     Write-Host "Building Ginkgo"
-    Push-Location ./test/tools
-    Run-Command "go build -o build/ginkgo.exe ./vendor/github.com/onsi/ginkgo/v2/ginkgo"
-    Pop-Location
+    Run-Command "go build -o ./test/tools/build/ginkgo.exe ./vendor/github.com/onsi/ginkgo/v2/ginkgo"
 }
 
 function Git-Commit{
@@ -287,6 +293,9 @@ switch ($target) {
     {$_ -in '', 'podman-remote', 'podman'} {
         Podman-Remote
     }
+    'localunit' {
+        Local-Unit
+    }
     'localmachine' {
         if ($args.Count -gt 1) {
             $files = $args[1]
@@ -331,6 +340,9 @@ switch ($target) {
         Write-Host "Example: Build podman-remote "
         Write-Host " .\winmake podman-remote"
         Write-Host
+        Write-Host "Example: Run all unit tests "
+        Write-Host " .\winmake localunit"
+        Write-Host
         Write-Host "Example: Run all machine tests "
         Write-Host " .\winmake localmachine"
         Write-Host


Test patch:
diff --git a/cmd/podman/parse/net_test.go b/cmd/podman/parse/net_test.go
index 22421732689..f71dc6ba1fe 100644
--- a/cmd/podman/parse/net_test.go
+++ b/cmd/podman/parse/net_test.go
@@ -1,3 +1,5 @@
+//go:build !windows
+
 // most of these validate and parse functions have been taken from projectatomic/docker
 // and modified for cri-o
 package parse
diff --git a/pkg/auth/auth_test.go b/pkg/auth/auth_test.go
index 37cf817917d..ccb205c18e1 100644
--- a/pkg/auth/auth_test.go
+++ b/pkg/auth/auth_test.go
@@ -1,3 +1,5 @@
+//go:build !windows
+
 package auth
 
 import (
diff --git a/pkg/machine/machine_windows_test.go b/pkg/machine/machine_windows_test.go
index 48f3eafd536..5255a7502b4 100644
--- a/pkg/machine/machine_windows_test.go
+++ b/pkg/machine/machine_windows_test.go
@@ -3,15 +3,41 @@
 package machine
 
 import (
+	"fmt"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"testing"
 
+	"golang.org/x/sys/windows"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
+// shortPathToLongPath converts a Windows short path (C:\PROGRA~1) to its
+// long path equivalent (C:\Program Files). It returns an error if shortPath
+// doesn't exist.
+func shortPathToLongPath(shortPath string) (string, error) {
+	shortPathPtr, err := windows.UTF16PtrFromString(shortPath)
+	if err != nil {
+		return "", err
+	}
+	len, err := windows.GetLongPathName(shortPathPtr, nil, 0)
+	if err != nil {
+		return "", err
+	}
+	if len == 0 {
+		return "", fmt.Errorf("failed to get buffer size for path: %s", shortPath)
+	}
+	longPathPtr := &(make([]uint16, len)[0])
+	_, err = windows.GetLongPathName(shortPathPtr, longPathPtr, len)
+	if err != nil {
+		return "", err
+	}
+	return windows.UTF16PtrToString(longPathPtr), nil
+}
+
 // CreateNewItemWithPowerShell creates a new item using PowerShell.
 // It's an helper to easily create junctions on Windows (as well as other file types).
 // It constructs a PowerShell command to create a new item at the specified path with the given item type.
@@ -23,15 +49,21 @@ import (
 //   - itemType: The type of the item to be created (e.g., "File", "SymbolicLink", "Junction").
 //   - target: The target for the new item, if applicable.
 func CreateNewItemWithPowerShell(t *testing.T, path string, itemType string, target string) {
-	var pwshCmd string
+	var pwshCmd, pwshPath string
+	// Look for Powershell 7 first as it allow Symlink creation for non-admins too
+	pwshPath, err := exec.LookPath("pwsh.exe")
+	if err != nil {
+		// Use Powershell 5 that is always present
+		pwshPath = "powershell.exe"
+	}
 	pwshCmd = "New-Item -Path " + path + " -ItemType " + itemType
 	if target != "" {
 		pwshCmd += " -Target " + target
 	}
-	cmd := exec.Command("pwsh", "-Command", pwshCmd)
+	cmd := exec.Command(pwshPath, "-Command", pwshCmd)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
-	err := cmd.Run()
+	err = cmd.Run()
 	require.NoError(t, err)
 }
 
@@ -45,13 +77,16 @@ func CreateNewItemWithPowerShell(t *testing.T, path string, itemType string, tar
 // with filepath.EvalSymlink().
 func TestEvalSymlinksOrClean(t *testing.T) {
 	// Create a temporary directory to store the normal file
-	normalFileDir := t.TempDir()
+	normalFileDir, err := shortPathToLongPath(t.TempDir())
+	require.NoError(t, err)
 
 	// Create a temporary directory to store the (hard/sym)link files
-	linkFilesDir := t.TempDir()
+	linkFilesDir, err := shortPathToLongPath(t.TempDir())
+	require.NoError(t, err)
 
 	// Create a temporary directory where the mount point will be created
-	mountPointDir := t.TempDir()
+	mountPointDir, err := shortPathToLongPath(t.TempDir())
+	require.NoError(t, err)
 
 	// Create a normal file
 	normalFile := filepath.Join(normalFileDir, "testFile")
diff --git a/pkg/machine/wsl/wutil/wutil_test.go b/pkg/machine/wsl/wutil/wutil_test.go
new file mode 100644
index 00000000000..4b7bd6af479
--- /dev/null
+++ b/pkg/machine/wsl/wutil/wutil_test.go
@@ -0,0 +1,145 @@
+//go:build windows
+
+package wutil
+
+import (
+	"github.com/stretchr/testify/assert"
+	"golang.org/x/text/encoding/unicode"
+	"io"
+	"strings"
+	"testing"
+)
+
+const (
+	WSL1InstalledWithWSLAndVMPEnabled = `Default Version: 1`
+	WSL2InstalledWithWSLAndVMPEnabled = `Default Version: 2`
+	WSL1NotInstalled                  = `Default Version: 1
+
+The Windows Subsystem for Linux kernel can be manually updated with 'wsl --update', but automatic updates cannot occur due to your system settings.
+To receive automatic kernel updates, please enable the Windows Update setting: 'Receive updates for other Microsoft products when you update Windows'.
+For more information please visit https://aka.ms/wsl2kernel.
+
+The WSL 2 kernel file is not found. To update or restore the kernel please run 'wsl --update'.`
+	WSL2NotInstalled = `The Windows Subsystem for Linux is not installed. You can install by running 'wsl.exe --install'.
+For more information please visit https://aka.ms/wslinstall`
+	WSL2InstalledWithWSLDisabled = `Default Version: 2
+WSL1 is not supported with your current machine configuration.
+Please enable the "Windows Subsystem for Linux" optional component to use WSL1.`
+	WSL2InstalledWithVMPDisabled = `Default Version: 2
+WSL2 is not supported with your current machine configuration.
+Please enable the "Virtual Machine Platform" optional component and ensure virtualization is enabled in the BIOS.
+Enable "Virtual Machine Platform" by running: wsl.exe --install --no-distribution
+For information please visit https://aka.ms/enablevirtualization`
+	WSL2InstalledWithWSLAndVMPDisabled = `Default Version: 2
+WSL1 is not supported with your current machine configuration.
+Please enable the "Windows Subsystem for Linux" optional component to use WSL1.
+WSL2 is not supported with your current machine configuration.
+Please enable the "Virtual Machine Platform" optional component and ensure virtualization is enabled in the BIOS.
+Enable "Virtual Machine Platform" by running: wsl.exe --install --no-distribution
+For information please visit https://aka.ms/enablevirtualization`
+	WSL1InstalledWithVMPDisabled = `Default Version: 1
+Please enable the Virtual Machine Platform Windows feature and ensure virtualization is enabled in the BIOS.
+For information please visit https://aka.ms/enablevirtualization`
+	WSL1InstalledWithWSLDisabled = `Default Version: 1
+WSL1 is not supported with your current machine configuration.
+Please enable the "Windows Subsystem for Linux" optional component to use WSL1.`
+)
+
+func TestMatchOutputLine(t *testing.T) {
+	tests := []struct {
+		winVariant   string
+		statusOutput string
+		want         wslStatus
+	}{
+		{
+			"WSL1 configured and both Virtual Machine Platform enabled and Windows Subsystem for Linux are enabled",
+			WSL1InstalledWithWSLAndVMPEnabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: true,
+				wslFeatureEnabled: true,
+			},
+		},
+		{
+			"WSL2 configured and both Virtual Machine Platform enabled and Windows Subsystem for Linux enabled",
+			WSL2InstalledWithWSLAndVMPEnabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: true,
+				wslFeatureEnabled: true,
+			},
+		},
+		{
+			"WSL not installed (was previously configured as version 1)",
+			WSL1NotInstalled,
+			wslStatus{
+				installed:         false,
+				vmpFeatureEnabled: true,
+				wslFeatureEnabled: true,
+			},
+		},
+		{
+			"WSL not installed (was previously configured as version 2)",
+			WSL2NotInstalled,
+			wslStatus{
+				installed:         false,
+				vmpFeatureEnabled: true,
+				wslFeatureEnabled: true,
+			},
+		},
+		{
+			"WSL2 configured and Virtual Machine Platform is enabled but Windows Subsystem for Linux is disabled",
+			WSL2InstalledWithWSLDisabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: true,
+				wslFeatureEnabled: false,
+			},
+		},
+		{
+			"WSL2 configured and Virtual Machine Platform is disabled but Windows Subsystem for Linux is enabled",
+			WSL2InstalledWithVMPDisabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: false,
+				wslFeatureEnabled: true,
+			},
+		},
+		{
+			"WSL2 configured and both Virtual Machine Platform and Windows Subsystem for Linux are disabled",
+			WSL2InstalledWithWSLAndVMPDisabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: false,
+				wslFeatureEnabled: false,
+			},
+		},
+		{
+			"WSL1 configured and Virtual Machine Platform is disabled but Windows Subsystem for Linux is enabled",
+			WSL1InstalledWithVMPDisabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: false,
+				wslFeatureEnabled: true,
+			},
+		},
+		{
+			"WSL1 configured and Virtual Machine Platform is enabled but Windows Subsystem for Linux is disabled",
+			WSL1InstalledWithWSLDisabled,
+			wslStatus{
+				installed:         true,
+				vmpFeatureEnabled: true,
+				wslFeatureEnabled: false,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.winVariant, func(t *testing.T) {
+			encoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewEncoder()
+			encodedOutput, err := encoder.String(tt.statusOutput)
+			assert.Nil(t, err)
+			reader := io.NopCloser(strings.NewReader(encodedOutput))
+			assert.Equal(t, tt.want, matchOutputLine(reader))
+		})
+	}
+}
diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go
index 37795d31111..b582b51ecc9 100644
--- a/pkg/util/utils_test.go
+++ b/pkg/util/utils_test.go
@@ -3,6 +3,7 @@ package util
 import (
 	"fmt"
 	"math"
+	"runtime"
 	"sort"
 	"testing"
 	"time"
@@ -827,6 +828,9 @@ func TestProcessOptions(t *testing.T) {
 }
 
 func TestGetRootlessPauseProcessPidPath(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		t.Skip("Not implemented on Windows")
+	}
 	dir, err := GetRootlessPauseProcessPidPath()
 	assert.NoError(t, err)
 	assert.NotEqual(t, dir, "libpod/tmp/pause.pid")