diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index c3ef9497..eb1a6989 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -27,7 +27,7 @@ jobs: - name: Build uses: ./php with: - php-version: ${{ github.event.inputs.php-version }} + php-version: ${{ inputs.php-version }} arch: ${{ matrix.arch }} ts: ${{ matrix.ts }} @@ -38,14 +38,48 @@ jobs: artifact-id: ${{ steps.artifacts.outputs.artifact-id }} steps: - name: Upload artifacts - uses: actions/upload-artifact/merge@v4 + uses: actions/upload-artifact/merge@v5 id: artifacts with: name: artifacts delete-merged: true + tests: + strategy: + matrix: + arch: [x64, x86] + ts: [nts, ts] + opcache: [opcache, nocache] + runs-on: windows-2022 + needs: artifacts + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download artifacts + uses: actions/download-artifact@v5 + id: artifacts + with: + name: artifacts + + - name: Test PHP + shell: pwsh + continue-on-error: true + run: | + Import-Module (Join-Path $(pwd).Path '\php\BuildPhp') -Force + Invoke-PhpTests -PhpVersion ${{inputs.php-version}} ` + -Arch ${{matrix.arch}} ` + -Ts ${{matrix.ts}} ` + -Opcache ${{matrix.opcache}} + + - name: Upload artifacts + uses: actions/upload-artifact@v5 + continue-on-error: true + with: + name: test-results-${{matrix.arch}}-${{matrix.ts}}-${{matrix.opcache}} + path: test-${{matrix.arch}}-${{matrix.ts}}-${{matrix.opcache}}.xml upload: runs-on: ubuntu-latest - needs: artifacts + needs: tests if: ${{ github.event.inputs.upload == 'true' }} steps: - name: Upload to downloads server diff --git a/php/BuildPhp/BuildPhp.psd1 b/php/BuildPhp/BuildPhp.psd1 index 0420a04b..bf78f8c8 100644 --- a/php/BuildPhp/BuildPhp.psd1 +++ b/php/BuildPhp/BuildPhp.psd1 @@ -61,10 +61,10 @@ # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @( - # Private functions + # Private functions (sorted) 'Add-BuildRequirements', - 'Add-TestRequirements', 'Add-Path', + 'Add-TestRequirements', 'Add-Vs', 'Get-File', 'Get-OciSdk', @@ -74,11 +74,18 @@ 'Get-PhpTestPack', 'Get-SourcePhpVersion', 'Get-TestSettings', - 'Get-TestsList' - 'Get-VsVersionHelper', + 'Get-TestsList', 'Get-VsVersion', + 'Get-VsVersionHelper', + 'Set-EnchantTestEnvironment', + 'Set-FirebirdTestEnvironment', + 'Set-MsSqlTestEnvironment', + 'Set-MySqlTestEnvironment', 'Set-NetSecurityProtocolType', - 'Set-PhpIniForTests' + 'Set-OdbcTestEnvironment', + 'Set-PgSqlTestEnvironment', + 'Set-PhpIniForTests', + 'Set-SnmpTestEnvironment', # Public functions 'Invoke-PhpBuild', @@ -139,4 +146,3 @@ # DefaultCommandPrefix = '' } - diff --git a/php/BuildPhp/private/Add-TestRequirements.ps1 b/php/BuildPhp/private/Add-TestRequirements.ps1 index 503f830f..55aa150c 100644 --- a/php/BuildPhp/private/Add-TestRequirements.ps1 +++ b/php/BuildPhp/private/Add-TestRequirements.ps1 @@ -32,13 +32,67 @@ function Add-TestRequirements { [string] $VsVersion, [Parameter(Mandatory = $false, Position=4, HelpMessage='Tests Directory')] [ValidateLength(1, [int]::MaxValue)] - [string] $TestsDirectory + [string] $TestsDirectory, + [Parameter(Mandatory = $true, Position=5, HelpMessage='Artifacts Directory')] + [ValidateNotNull()] + [string] $ArtifactsDirectory ) begin { } process { - Get-PhpBuild -PhpVersion $PhpVersion -Arch $Arch -Ts $Ts -VsVersion $VsVersion - Get-PhpTestPack -PhpVersion $PhpVersion -TestsDirectory $TestsDirectory + Add-Type -Assembly "System.IO.Compression.Filesystem" + $versionInUrl = $PhpVersion + if($PhpVersion -eq 'master') { + $versionInUrl = "master" + } + $tsPart = if ($Ts -eq "nts") {"nts-Win32"} else {"Win32"} + $binZipFile = "php-$versionInUrl-$tsPart-$VsVersion-$Arch.zip" + $testZipFile = "php-test-pack-$versionInUrl.zip" + + $currentDirectory = (Get-Location).Path + $binZipFilePath = Join-Path $ArtifactsDirectory $binZipFile + $binDirectoryPath = Join-Path $currentDirectory phpbin + + $testZipFilePath = Join-Path $ArtifactsDirectory $testZipFile + $testsDirectoryPath = Join-Path $currentDirectory $TestsDirectory + + if(-not(Test-Path $binZipFilePath)) { + Write-Host "Downloading PHP build $binZipFile..." + Get-PhpBuild -PhpVersion $PhpVersion -Arch $Arch -Ts $Ts -VsVersion $VsVersion + } else { + [System.IO.Compression.ZipFile]::ExtractToDirectory($binZipFilePath, $binDirectoryPath) + } + + if(-not(Test-Path $testZipFilePath)) { + Write-Host "Downloading PHP test pack $testZipFile..." + Get-PhpTestPack -PhpVersion $PhpVersion -TestsDirectory $TestsDirectory + } else { + [System.IO.Compression.ZipFile]::ExtractToDirectory($testZipFilePath, $testsDirectoryPath) + } + + $ldapDll = Join-Path $binDirectoryPath 'php_ldap.dll' + Remove-Item -LiteralPath $ldapDll -Force -ErrorAction SilentlyContinue + + $phpExePath = Join-Path $binDirectoryPath 'php.exe' + $phpCgiPath = Join-Path $binDirectoryPath 'php-cgi.exe' + try { & editbin "/stack:8388608" $phpExePath | Out-Null } catch {} + try { & editbin "/stack:8388608" $phpCgiPath | Out-Null } catch {} + + $Env:TEST_PHPDBG_EXECUTABLE = (Join-Path $binDirectoryPath 'phpdbg.exe') + + Get-PhpSdk + $env:DEPS_DIR = "$currentDirectory/../deps" + New-Item "$env:DEPS_DIR" -ItemType "directory" -Force > $null 2>&1 + $branch = if ($PhpVersion -eq 'master') {'master'} else {($PhpVersion -split '\.')[0..1] -join '.'} + & "$currentDirectory\php-sdk\bin\phpsdk_deps.bat" --update --no-backup --branch $branch --stability staging --deps $env:DEPS_DIR --crt $VsVersion --arch $Arch + + Set-MySqlTestEnvironment + Set-PgSqlTestEnvironment + Set-OdbcTestEnvironment + Set-MsSqlTestEnvironment + Set-FirebirdTestEnvironment + Set-EnchantTestEnvironment + Set-SnmpTestEnvironment -TestsDirectoryPath $testsDirectoryPath } end { } diff --git a/php/BuildPhp/private/Set-EnchantTestEnvironment.ps1 b/php/BuildPhp/private/Set-EnchantTestEnvironment.ps1 new file mode 100644 index 00000000..36ad655d --- /dev/null +++ b/php/BuildPhp/private/Set-EnchantTestEnvironment.ps1 @@ -0,0 +1,41 @@ +function Set-EnchantTestEnvironment { + <# + .SYNOPSIS + Prepare Enchant (hunspell) runtime and dictionaries for tests. + #> + [CmdletBinding()] + param () + process { + $driveRoot = [System.IO.Path]::GetPathRoot((Get-Location).Path) + $libDir = Join-Path $driveRoot 'usr\local\lib\enchant-2' + $dictDir = Join-Path $driveRoot 'usr\local\share\enchant\hunspell' + + New-Item -ItemType Directory -Force -Path $libDir | Out-Null + New-Item -ItemType Directory -Force -Path $dictDir | Out-Null + + $depsDir = $env:DEPS_DIR + if (-not $depsDir) { throw 'DEPS_DIR is not set.' } + $srcDll = Join-Path $depsDir 'bin\libenchant2_hunspell.dll' + if (-not (Test-Path -LiteralPath $srcDll)) { + throw "libenchant2_hunspell.dll not found at $srcDll" + } + Copy-Item -LiteralPath $srcDll -Destination $libDir -Force + + Write-Host 'Fetching enchant dicts' + Push-Location $dictDir + try { + $zip = Join-Path $dictDir 'dict.zip' + $url = 'https://downloads.php.net/~windows/qa/appveyor/ext/enchant/dict.zip' + Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile $zip + try { + Expand-Archive -LiteralPath $zip -DestinationPath $dictDir -Force + } catch { + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($zip, $dictDir) + } + Remove-Item -LiteralPath $zip -Force -ErrorAction SilentlyContinue + } finally { + Pop-Location + } + } +} diff --git a/php/BuildPhp/private/Set-FirebirdTestEnvironment.ps1 b/php/BuildPhp/private/Set-FirebirdTestEnvironment.ps1 new file mode 100644 index 00000000..b3f5a544 --- /dev/null +++ b/php/BuildPhp/private/Set-FirebirdTestEnvironment.ps1 @@ -0,0 +1,50 @@ +function Set-FirebirdTestEnvironment { + <# + .SYNOPSIS + Configure Firebird for PDO_Firebird tests on Windows. + #> + [CmdletBinding()] + param () + process { + $destDir = 'C:\Firebird' + $firebirdVersion = 'v4.0.4' + $firebirdRelease = "https://github.com/FirebirdSQL/firebird/releases/download/$firebirdVersion" + New-Item -ItemType Directory -Force -Path $destDir | Out-Null + + $is64 = [Environment]::Is64BitOperatingSystem + $url = if ($is64) { + "$firebirdRelease/Firebird-4.0.4.3010-0-x64.zip" + } else { + "$firebirdRelease/Firebird-4.0.4.3010-0-Win32.zip" + } + + $zipPath = Join-Path $destDir 'Firebird.zip' + Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile $zipPath + + try { + Expand-Archive -LiteralPath $zipPath -DestinationPath $destDir -Force + } catch { + Add-Type -AssemblyName System.IO.Compression.FileSystem + [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $destDir) + } + + $env:PDO_FIREBIRD_TEST_DATABASE = 'C:\test.fdb' + $env:PDO_FIREBIRD_TEST_DSN = "firebird:dbname=127.0.0.1:$($env:PDO_FIREBIRD_TEST_DATABASE)" + $env:PDO_FIREBIRD_TEST_USER = 'SYSDBA' + $env:PDO_FIREBIRD_TEST_PASS = 'phpfi' + + $createUserSql = Join-Path $destDir 'create_user.sql' + Set-Content -Path $createUserSql -Value "create user $($env:PDO_FIREBIRD_TEST_USER) password '$($env:PDO_FIREBIRD_TEST_PASS)';" -Encoding ASCII + Add-Content -Path $createUserSql -Value 'commit;' -Encoding ASCII + + $setupSql = Join-Path $destDir 'setup.sql' + Set-Content -Path $setupSql -Value "create database '$($env:PDO_FIREBIRD_TEST_DATABASE)' user '$($env:PDO_FIREBIRD_TEST_USER)' password '$($env:PDO_FIREBIRD_TEST_PASS)';" -Encoding ASCII + + & (Join-Path $destDir 'instsvc.exe') install -n TestInstance | Out-Null + & (Join-Path $destDir 'isql') -q -i $setupSql | Out-Null + & (Join-Path $destDir 'isql') -q -i $createUserSql -user sysdba $env:PDO_FIREBIRD_TEST_DATABASE | Out-Null + & (Join-Path $destDir 'instsvc.exe') start -n TestInstance | Out-Null + + Add-Path $destDir + } +} diff --git a/php/BuildPhp/private/Set-MsSqlTestEnvironment.ps1 b/php/BuildPhp/private/Set-MsSqlTestEnvironment.ps1 new file mode 100644 index 00000000..241a101b --- /dev/null +++ b/php/BuildPhp/private/Set-MsSqlTestEnvironment.ps1 @@ -0,0 +1,12 @@ +function Set-MsSqlTestEnvironment { + <# + .SYNOPSIS + Install Microsoft SQL Server Express required for SQL Server-related tests. + #> + [CmdletBinding()] + param () + process { + & choco install sql-server-express -y --no-progress --install-arguments="/SECURITYMODE=SQL /SAPWD=Password12!" | Out-Null + } +} + diff --git a/php/BuildPhp/private/Set-MySqlTestEnvironment.ps1 b/php/BuildPhp/private/Set-MySqlTestEnvironment.ps1 new file mode 100644 index 00000000..0a6d5cdf --- /dev/null +++ b/php/BuildPhp/private/Set-MySqlTestEnvironment.ps1 @@ -0,0 +1,43 @@ +function Set-MySqlTestEnvironment { + <# + .SYNOPSIS + Configure environment variables for MySQL-related PHP tests and ensure the test database exists. + #> + [CmdletBinding()] + param ( + ) + process { + $Database = 'test' + $DbHost = '127.0.0.1' + $User = 'root' + $Password = 'Password12!' + $Port = 3306 + & mysqld --initialize-insecure | Out-Null + & mysqld --install | Out-Null + & net start "MySQL" | Out-Null + & mysql --port=$Port --user=root --password="" -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$($Password)'; FLUSH PRIVILEGES;" | Out-Null + + $env:MYSQL_PWD = $Password + $env:MYSQL_TEST_PASSWD = $env:MYSQL_PWD + $env:MYSQL_TEST_USER = $User + $env:MYSQL_TEST_HOST = $DbHost + $env:MYSQL_TEST_PORT = "$Port" + $env:MYSQL_TEST_DB = $Database + + $env:PDO_MYSQL_TEST_USER = $env:MYSQL_TEST_USER + $env:PDO_MYSQL_TEST_PASS = $env:MYSQL_PWD + $env:PDO_MYSQL_TEST_HOST = $env:MYSQL_TEST_HOST + $env:PDO_MYSQL_TEST_PORT = $env:MYSQL_TEST_PORT + $env:PDO_MYSQL_TEST_DSN = "mysql:host=$($env:PDO_MYSQL_TEST_HOST);port=$($env:PDO_MYSQL_TEST_PORT);dbname=$Database" + + $params = @( + "--host=$($env:PDO_MYSQL_TEST_HOST)", + "--port=$($env:MYSQL_TEST_PORT)", + "--user=$($env:MYSQL_TEST_USER)", + "--password=$($env:MYSQL_TEST_PASSWD)", + "-e", "CREATE DATABASE IF NOT EXISTS $Database" + ) + + & mysql @params | Out-Null + } +} diff --git a/php/BuildPhp/private/Set-OdbcTestEnvironment.ps1 b/php/BuildPhp/private/Set-OdbcTestEnvironment.ps1 new file mode 100644 index 00000000..ffc7dcda --- /dev/null +++ b/php/BuildPhp/private/Set-OdbcTestEnvironment.ps1 @@ -0,0 +1,15 @@ +function Set-OdbcTestEnvironment { + <# + .SYNOPSIS + Configure environment variables for ODBC/PDO ODBC SQL Server tests. + #> + [CmdletBinding()] + param () + process { + $env:ODBC_TEST_USER = 'sa' + $env:ODBC_TEST_PASS = 'Password12!' + $env:ODBC_TEST_DSN = "Driver={ODBC Driver 17 for SQL Server};Server=(local)\SQLEXPRESS;Database=master;uid=$($env:ODBC_TEST_USER);pwd=$($env:ODBC_TEST_PASS)" + $env:PDOTEST_DSN = "odbc:$($env:ODBC_TEST_DSN)" + } +} + diff --git a/php/BuildPhp/private/Set-PgSqlTestEnvironment.ps1 b/php/BuildPhp/private/Set-PgSqlTestEnvironment.ps1 new file mode 100644 index 00000000..0cd38d15 --- /dev/null +++ b/php/BuildPhp/private/Set-PgSqlTestEnvironment.ps1 @@ -0,0 +1,36 @@ +function Set-PgSqlTestEnvironment { + <# + .SYNOPSIS + Configure environment variables for PostgreSQL-related PHP tests and ensure the test database exists. + #> + [CmdletBinding()] + param () + process { + $env:PGUSER = 'postgres' + $env:PGPASSWORD = 'Password12!' + Set-Service -Name "postgresql-x64-14" -StartupType manual -Status Running + $prevPgPwd = $env:PGPASSWORD + $env:PGPASSWORD = 'root' + & "$env:PGBIN\psql" -U postgres -c "ALTER USER ${$env.PGUSER} WITH PASSWORD '$($prevPgPwd)';" | Out-Null + $env:PGPASSWORD = $prevPgPwd + $env:PDO_PGSQL_TEST_DSN = "pgsql:host=127.0.0.1 port=5432 dbname=test user=$($env:PGUSER) password=$($env:PGPASSWORD)" + if ($env:PGBIN) { + $env:TMP_POSTGRESQL_BIN = $env:PGBIN + } + + $testsRoot = Join-Path (Get-Location).Path 'tests' + $configDir = Join-Path $testsRoot 'ext/pgsql/tests' + $configFile = Join-Path $configDir 'config.inc' + New-Item -ItemType Directory -Force -Path $configDir | Out-Null + + $phpLine = "" + Add-Content -Path $configFile -Value $phpLine -Encoding ASCII + + $createdb = Join-Path $env:TMP_POSTGRESQL_BIN 'createdb.exe' + if (-not (Test-Path $createdb)) { + throw "createdb.exe not found. Ensure PGBIN is set to PostgreSQL bin directory." + } + + & $createdb 'test' | Out-Null + } +} diff --git a/php/BuildPhp/private/Set-SnmpTestEnvironment.ps1 b/php/BuildPhp/private/Set-SnmpTestEnvironment.ps1 new file mode 100644 index 00000000..f48fe8e7 --- /dev/null +++ b/php/BuildPhp/private/Set-SnmpTestEnvironment.ps1 @@ -0,0 +1,47 @@ +function Set-SnmpTestEnvironment { + <# + .SYNOPSIS + Configure SNMP test environment: set MIBDIRS, patch snmpd.conf, and start snmpd. + .PARAMETER TestsDirectoryPath + Absolute path to the extracted PHP tests directory (use the $testsDirectoryPath from Add-TestRequirements). + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] $TestsDirectoryPath + ) + process { + if (-not $env:DEPS_DIR) { + throw 'DEPS_DIR is not set. Ensure dependencies are downloaded before SNMP setup.' + } + + $env:MIBDIRS = Join-Path $env:DEPS_DIR 'share\mibs' + + $confPath = Join-Path $TestsDirectoryPath 'ext\snmp\tests\snmpd.conf' + if (-not (Test-Path -LiteralPath $confPath)) { + throw "snmpd.conf not found at $confPath" + } + + $forwardTestsRoot = ($TestsDirectoryPath -replace '\\','/') + $bigTestJs = "$forwardTestsRoot/ext/snmp/tests/bigtest.js" + + $content = Get-Content -LiteralPath $confPath -Raw -Encoding UTF8 + $newLine = "exec HexTest cscript.exe /nologo $bigTestJs" + $updated = [System.Text.RegularExpressions.Regex]::Replace( + $content, + '^exec\s+HexTest\s+.*$', + [System.Text.RegularExpressions.Regex]::Escape($newLine).Replace('\/','/'), + [System.Text.RegularExpressions.RegexOptions]::Multiline + ) + if ($updated -ne $content) { + Set-Content -LiteralPath $confPath -Value $updated -Encoding UTF8 + } + + $snmpd = Join-Path $env:DEPS_DIR 'bin\snmpd.exe' + if (-not (Test-Path -LiteralPath $snmpd)) { + throw "snmpd.exe not found at $snmpd" + } + Start-Process -FilePath $snmpd -ArgumentList @('-C','-c', $confPath, '-Ln') -WindowStyle Hidden | Out-Null + } +} diff --git a/php/BuildPhp/public/Invoke-PhpTests.ps1 b/php/BuildPhp/public/Invoke-PhpTests.ps1 index b8e161a4..0154b089 100644 --- a/php/BuildPhp/public/Invoke-PhpTests.ps1 +++ b/php/BuildPhp/public/Invoke-PhpTests.ps1 @@ -40,6 +40,8 @@ function Invoke-PhpTests { $currentDirectory = (Get-Location).Path + Get-ChildItem $currentDirectory + $tempDirectory = [System.IO.Path]::GetTempPath() $buildDirectory = [System.IO.Path]::Combine($tempDirectory, [System.Guid]::NewGuid().ToString()) @@ -48,9 +50,11 @@ function Invoke-PhpTests { New-Item "$buildDirectory" -ItemType "directory" -Force > $null 2>&1 + New-Item "$buildDirectory\tmp" -ItemType "directory" -Force > $null 2>&1 + Set-Location "$buildDirectory" - Add-TestRequirements -PhpVersion $PhpVersion -Arch $Arch -Ts $Ts -VsVersion $VsData.vs -TestsDirectory $testsDirectory + Add-TestRequirements -PhpVersion $PhpVersion -Arch $Arch -Ts $Ts -VsVersion $VsData.vs -TestsDirectory $testsDirectory -ArtifactsDirectory $currentDirectory Set-PhpIniForTests -BuildDirectory $buildDirectory -Opcache $Opcache @@ -61,22 +65,24 @@ function Invoke-PhpTests { $Env:OPENSSL_CONF = "$buildDirectory\phpbin\extras\ssl\openssl.cnf" - $env:MYSQL_TEST_PORT = "3306" - $Env:MYSQL_TEST_USER = "root" - $Env:MYSQL_TEST_PASSWD = "" - $Env:MYSQL_TEST_DB = "test" - - $Env:PDO_MYSQL_TEST_DSN = "mysql:host=localhost;dbname=test" - $Env:PDO_MYSQL_TEST_USER = "root" - $Env:PDO_MYSQL_TEST_PASS = "" - Set-Location "$testsDirectory" Get-TestsList -OutputFile "tests-to-run.txt" $settings = Get-TestSettings -PhpVersion $PhpVersion - php $settings.runner $settings.progress "-g" "FAIL,BORK,WARN,LEAK" "-r" "tests-to-run.txt" + php ` + $settings.runner ` + $settings.progress ` + "-g" "FAIL,BORK,WARN,LEAK" ` + "-q" ` + "--offline" ` + "--show-diff" ` + "--show-slow" "1000" ` + "--set-timeout" "120" ` + "--temp-source" "$buildDirectory\tmp" ` + "--temp-target" "$buildDirectory\tmp" ` + "-r" "tests-to-run.txt" Copy-Item "$buildDirectory\test-$Arch-$Ts-$Opcache.xml" $currentDirectory diff --git a/php/action.yml b/php/action.yml index fcfd9a79..8074e0b2 100644 --- a/php/action.yml +++ b/php/action.yml @@ -32,7 +32,7 @@ runs: -Ts ${{inputs.ts}} - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: artifacts-${{inputs.php-version}}-${{inputs.arch}}-${{inputs.ts}} path: artifacts/*