Update CI to run Windows unit tests

Add a new target in winmake.ps1 to run unit tests and use
use it in a new cirrus task.

Fix machine_windows_test.go to make it work in CI machine.

Add the `!windows` tag on tests files that fail on Windows.

Signed-off-by: Mario Loriedo <mario.loriedo@gmail.com>
This commit is contained in:
Mario Loriedo
2025-03-10 18:11:12 +01:00
parent 87421d9508
commit af29bb5b6e
7 changed files with 118 additions and 13 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,5 @@
//go:build !windows
package auth
import (

View File

@ -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")

View File

@ -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")

View File

@ -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