From 22dd05270e7caab6fe3d2293dbcaa685cc2281c1 Mon Sep 17 00:00:00 2001 From: Maxim Lobanov Date: Thu, 16 Jul 2020 07:30:34 +0300 Subject: [PATCH] Migrate toolcache / toolset tests to Pester (#1222) * implement toolset tests * Update TestsHelpers.ps1 * Update InstallHelpers.ps1 --- images/win/Windows2016-Azure.json | 35 +---- images/win/Windows2019-Azure.json | 35 +---- .../scripts/ImageHelpers/ImageHelpers.psm1 | 1 + .../scripts/ImageHelpers/InstallHelpers.ps1 | 26 ++-- .../win/scripts/ImageHelpers/TestsHelpers.ps1 | 5 + .../scripts/Installers/Configure-Toolset.ps1 | 4 +- .../scripts/Installers/Validate-ToolCache.ps1 | 143 ------------------ .../scripts/Installers/Validate-Toolset.ps1 | 94 ------------ images/win/scripts/Tests/Toolset.Tests.ps1 | 95 ++++++++++++ 9 files changed, 130 insertions(+), 308 deletions(-) delete mode 100644 images/win/scripts/Installers/Validate-ToolCache.ps1 delete mode 100644 images/win/scripts/Installers/Validate-Toolset.ps1 create mode 100644 images/win/scripts/Tests/Toolset.Tests.ps1 diff --git a/images/win/Windows2016-Azure.json b/images/win/Windows2016-Azure.json index 10fafd3e6..faf1bac3b 100644 --- a/images/win/Windows2016-Azure.json +++ b/images/win/Windows2016-Azure.json @@ -364,18 +364,11 @@ { "type": "powershell", "environment_vars":[ - "TOOLSET_JSON_PATH={{user `toolset_json_path`}}" - ], - "scripts":[ - "{{ template_dir }}/scripts/Installers/Install-PyPy.ps1" - ] - }, - { - "type": "powershell", - "environment_vars":[ - "TOOLSET_JSON_PATH={{user `toolset_json_path`}}" + "TOOLSET_JSON_PATH={{user `toolset_json_path`}}", + "ROOT_FOLDER={{user `root_folder`}}" ], "scripts":[ + "{{ template_dir }}/scripts/Installers/Install-PyPy.ps1", "{{ template_dir }}/scripts/Installers/Install-Toolset.ps1", "{{ template_dir }}/scripts/Installers/Configure-Toolset.ps1" ] @@ -724,24 +717,6 @@ "{{ template_dir }}/scripts/Installers/Validate-Msys2.ps1" ] }, - { - "type": "powershell", - "environment_vars":[ - "ROOT_FOLDER={{user `root_folder`}}" - ], - "scripts":[ - "{{ template_dir }}/scripts/Installers/Validate-ToolCache.ps1" - ] - }, - { - "type": "powershell", - "environment_vars":[ - "TOOLSET_JSON_PATH={{user `toolset_json_path`}}" - ], - "scripts":[ - "{{ template_dir }}/scripts/Installers/Validate-Toolset.ps1" - ] - }, { "type": "powershell", "scripts":[ @@ -896,6 +871,10 @@ "type": "powershell", "scripts":[ "{{ template_dir }}/scripts/Tests/RunAll-Tests.ps1" + ], + "environment_vars":[ + "TOOLSET_JSON_PATH={{user `toolset_json_path`}}", + "ROOT_FOLDER={{user `root_folder`}}" ] }, { diff --git a/images/win/Windows2019-Azure.json b/images/win/Windows2019-Azure.json index e72aa38b4..642c35808 100644 --- a/images/win/Windows2019-Azure.json +++ b/images/win/Windows2019-Azure.json @@ -345,18 +345,11 @@ { "type": "powershell", "environment_vars":[ - "TOOLSET_JSON_PATH={{user `toolset_json_path`}}" - ], - "scripts":[ - "{{ template_dir }}/scripts/Installers/Install-PyPy.ps1" - ] - }, - { - "type": "powershell", - "environment_vars":[ - "TOOLSET_JSON_PATH={{user `toolset_json_path`}}" + "TOOLSET_JSON_PATH={{user `toolset_json_path`}}", + "ROOT_FOLDER={{user `root_folder`}}" ], "scripts":[ + "{{ template_dir }}/scripts/Installers/Install-PyPy.ps1", "{{ template_dir }}/scripts/Installers/Install-Toolset.ps1", "{{ template_dir }}/scripts/Installers/Configure-Toolset.ps1" ] @@ -723,24 +716,6 @@ "{{ template_dir }}/scripts/Installers/Validate-Msys2.ps1" ] }, - { - "type": "powershell", - "environment_vars":[ - "ROOT_FOLDER={{user `root_folder`}}" - ], - "scripts":[ - "{{ template_dir }}/scripts/Installers/Validate-ToolCache.ps1" - ] - }, - { - "type": "powershell", - "environment_vars":[ - "TOOLSET_JSON_PATH={{user `toolset_json_path`}}" - ], - "scripts":[ - "{{ template_dir }}/scripts/Installers/Validate-Toolset.ps1" - ] - }, { "type": "powershell", "scripts":[ @@ -901,6 +876,10 @@ "type": "powershell", "scripts":[ "{{ template_dir }}/scripts/Tests/RunAll-Tests.ps1" + ], + "environment_vars":[ + "TOOLSET_JSON_PATH={{user `toolset_json_path`}}", + "ROOT_FOLDER={{user `root_folder`}}" ] }, { diff --git a/images/win/scripts/ImageHelpers/ImageHelpers.psm1 b/images/win/scripts/ImageHelpers/ImageHelpers.psm1 index d711f7459..da1449568 100644 --- a/images/win/scripts/ImageHelpers/ImageHelpers.psm1 +++ b/images/win/scripts/ImageHelpers/ImageHelpers.psm1 @@ -30,6 +30,7 @@ Export-ModuleMember -Function @( 'Choco-Install' 'Extract-7Zip' 'Get-CommandResult' + 'Get-WhichTool' 'Get-EnvironmentVariable' 'Invoke-PesterTests' ) diff --git a/images/win/scripts/ImageHelpers/InstallHelpers.ps1 b/images/win/scripts/ImageHelpers/InstallHelpers.ps1 index af82e3785..2d6988eaa 100644 --- a/images/win/scripts/ImageHelpers/InstallHelpers.ps1 +++ b/images/win/scripts/ImageHelpers/InstallHelpers.ps1 @@ -360,6 +360,12 @@ function Get-ToolsetContent ConvertFrom-Json -InputObject $toolsetJson } +function Get-ToolcacheToolDirectory { + Param ([string] $ToolName) + $toolcacheRootPath = Resolve-Path $env:AGENT_TOOLSDIRECTORY + return Join-Path $toolcacheRootPath $ToolName +} + function Get-ToolsetToolFullPath { <# @@ -385,33 +391,25 @@ function Get-ToolsetToolFullPath [string] $Arch = "x64" ) - $ToolPath = Join-Path $env:AGENT_TOOLSDIRECTORY $Name + $toolPath = Get-ToolcacheToolDirectory -ToolName $Name # Add wildcard if missing if ($Version.Split(".").Length -lt 3) { $Version += ".*" } - # Check if version folder exists - $expectedVersionPath = Join-Path $ToolPath $Version - if (-not (Test-Path $expectedVersionPath)) { - Write-Host "Expected ${Name} ${Version} folder is not found!" - exit 1 - } + $versionPath = Join-Path $toolPath $Version # Take latest installed version in case if toolset version contains wildcards - $foundVersion = Get-Item $expectedVersionPath ` + $foundVersion = Get-Item $versionPath ` | Sort-Object -Property {[version]$_.name} -Descending ` | Select-Object -First 1 - # Check for required architecture folder - $foundVersionArchPath = Join-Path $foundVersion $Arch - if (-not (Test-Path $foundVersionArchPath)) { - Write-Host "Expected ${Name}(${Arch}) $($foundVersion.name) architecture folder is not found!" - exit 1 + if (-not $foundVersion) { + return $null } - return $foundVersionArchPath + return Join-Path $foundVersion $Arch } function Get-ToolsByName diff --git a/images/win/scripts/ImageHelpers/TestsHelpers.ps1 b/images/win/scripts/ImageHelpers/TestsHelpers.ps1 index 5228492b2..f2c6e437c 100644 --- a/images/win/scripts/ImageHelpers/TestsHelpers.ps1 +++ b/images/win/scripts/ImageHelpers/TestsHelpers.ps1 @@ -12,6 +12,11 @@ function Get-CommandResult { } } +# Gets path to the tool, analogue of 'which tool' +function Get-WhichTool($tool) { + return (Get-Command $tool).Path +} + # Gets value of environment variable by the name function Get-EnvironmentVariable($variable) { return [System.Environment]::GetEnvironmentVariable($variable) diff --git a/images/win/scripts/Installers/Configure-Toolset.ps1 b/images/win/scripts/Installers/Configure-Toolset.ps1 index 07594eb11..a4fdba1f0 100644 --- a/images/win/scripts/Installers/Configure-Toolset.ps1 +++ b/images/win/scripts/Installers/Configure-Toolset.ps1 @@ -81,4 +81,6 @@ foreach ($tool in $tools) Set-DefaultVariables -ToolVersionPath $toolVersionPath -EnvVars $toolEnvVars } -} \ No newline at end of file +} + +Invoke-PesterTests -TestFile "Toolset" \ No newline at end of file diff --git a/images/win/scripts/Installers/Validate-ToolCache.ps1 b/images/win/scripts/Installers/Validate-ToolCache.ps1 deleted file mode 100644 index a44b75a40..000000000 --- a/images/win/scripts/Installers/Validate-ToolCache.ps1 +++ /dev/null @@ -1,143 +0,0 @@ -################################################################################ -## File: Validate-ToolCache.ps1 -## Desc: Validate Tool Cache -################################################################################ - -# Helpers -function GetChildFolders { - param ( - [Parameter(Mandatory = $True)] - [string]$Path - ) - return Get-ChildItem -Path $Path -Directory -Name -} - -function Get-ToolcachePackages { - $toolcachePath = Join-Path $env:ROOT_FOLDER "toolcache.json" - return Get-Content -Raw $toolcachePath | ConvertFrom-Json -} - -$toolcachePackages = (Get-ToolcachePackages).PSObject.Properties | ForEach-Object { - $packageNameParts = $_.Name.Split("-") - return [PSCustomObject] @{ - ToolName = $packageNameParts[1] - Versions = $_.Value - Architecture = $packageNameParts[3] - } -} - -function GetToolsByName { - param ( - [Parameter(Mandatory = $True)] - [string]$SoftwareName - ) - return $toolcachePackages | Where-Object { $_.ToolName -eq $SoftwareName } -} - -function RunTestsByPath { - param ( - [Parameter(Mandatory = $True)] - [string[]]$ExecTests, - [Parameter(Mandatory = $True)] - [string]$Path, - [Parameter(Mandatory = $True)] - [string]$SoftwareName, - [Parameter(Mandatory = $True)] - [string]$SoftwareVersion, - [Parameter(Mandatory = $True)] - [string]$SoftwareArchitecture - ) - - foreach ($test in $ExecTests) - { - if (Test-Path "$Path\$test") - { - Write-Host "$SoftwareName($test) $SoftwareVersion($SoftwareArchitecture) is successfully installed:" - Write-Host (& "$Path\$test" --version) - } - else - { - Write-Host "$SoftwareName($test) $SoftwareVersion($SoftwareArchitecture) is not installed" - exit 1 - } - } -} - -function Get-SystemDefaultRuby { - Write-Host "Validate system Ruby..." - - if (Get-Command -Name 'ruby') - { - Write-Host "$(ruby --version) is on the path." - } - else - { - Write-Host "Ruby is not on the path." - exit 1 - } - - $rubyBinOnPath = Split-Path -Path (Get-Command -Name 'ruby').Path - if ( $(ruby --version) -notmatch 'ruby (?.*) \(.*' ) - { - Write-Host "Unable to determine Ruby version at " + $rubyBinOnPath - exit 1 - - } -} - -function ToolcacheTest { - param ( - [Parameter(Mandatory = $True)] - [string]$SoftwareName, - [Parameter(Mandatory = $True)] - [string[]]$ExecTests - ) - - $softwarePath = "$env:AGENT_TOOLSDIRECTORY\$SoftwareName" - - if (-Not (Test-Path $softwarePath)) - { - Write-Host "$softwarePath does not exist" - exit 1 - } - - [array]$installedVersions = GetChildFolders -Path $softwarePath - if ($installedVersions.count -eq 0) - { - Write-Host "$softwarePath does not include any folders" - exit 1 - } - - $tools = GetToolsByName -SoftwareName $SoftwareName - foreach($tool in $tools) - { - foreach ($version in $tool.Versions) - { - $foundVersion = $installedVersions | where { $_.StartsWith($version) } - if ($foundVersion -eq $null) - { - Write-Host "$softwarePath\$version.* was not found" - exit 1 - } - - $installedArchitecture = GetChildFolders -Path "$softwarePath\$foundVersion" - $requiredArchitecture = $tool.Architecture - if (-Not ($installedArchitecture -Contains $requiredArchitecture)) - { - Write-Host "$softwarePath\$foundVersion does not include the $requiredArchitecture architecture" - exit 1 - } - - $path = "$softwarePath\$foundVersion\$requiredArchitecture" - RunTestsByPath -ExecTests $ExecTests -Path $path -SoftwareName $SoftwareName -SoftwareVersion $foundVersion -SoftwareArchitecture $requiredArchitecture - } - } - - if ($SoftwareName -contains "Ruby") { - Get-SystemDefaultRuby - } -} - -# Ruby test -$RubyTests = @("bin\ruby.exe") -ToolcacheTest -SoftwareName "Ruby" -ExecTests $RubyTests diff --git a/images/win/scripts/Installers/Validate-Toolset.ps1 b/images/win/scripts/Installers/Validate-Toolset.ps1 deleted file mode 100644 index 93dcc9bde..000000000 --- a/images/win/scripts/Installers/Validate-Toolset.ps1 +++ /dev/null @@ -1,94 +0,0 @@ -################################################################################ -## File: Validate-Toolset.ps1 -## Team: CI-Build -## Desc: Validate Toolset -################################################################################ - -function Run-ExecutableTests { - param ( - [Parameter(Mandatory)] [string[]] $Executables, - [Parameter(Mandatory)] [string] $ToolPath - ) - $versionCommand = $Executables["command"] - foreach ($executable in $Executables["tools"]) { - $executablePath = Join-Path $ToolPath $executable - - Write-Host "Check $executable..." - if (Test-Path $executablePath) { - Write-Host "$executable is successfully installed: $(& $executablePath $versionCommand)" - } else { - Write-Host "$executablePath is not installed!" - exit 1 - } - } -} - -function Validate-SystemDefaultTool { - param ( - [Parameter(Mandatory)] [string] $ToolName, - [Parameter(Mandatory)] [string] $ExpectedVersion - ) - - $versionCommand = $toolsExecutables[$ToolName]["command"] - $binName = $ToolName.ToLower() - - # Check if tool on path - if (Get-Command -Name $binName) { - $versionOnPath = $(& $binName $versionCommand 2>&1) | Select-String -Pattern ".*(\d+\.\d+[\.\d+]+)" - - # Check if version is correct - if ($versionOnPath.matches.Groups[1].Value -notlike $ExpectedVersion) { - Write-Error "$ToolName $ExpectedVersion is not in the PATH" - exit 1 - } - - Write-Host "$ToolName $versionOnPath on path" - } else { - Write-Host "$ToolName is not on path" - exit 1 - } -} - -$ErrorActionPreference = "Stop" - -# Define executables for cached tools -$toolsExecutables = @{ - Python = @{ - tools = @("python.exe", "Scripts\pip.exe") - command = "--version" - } - node = @{ - tools = @("node.exe", "npm") - command = "--version" - } - PyPy = @{ - tools = @("python.exe", "Scripts\pip.exe") - command = "--version" - } - go = @{ - tools = @("bin\go.exe") - command = "version" - } -} - -# Get toolcache content from toolset -$tools = Get-ToolsetContent | Select-Object -ExpandProperty toolcache - -foreach($tool in $tools) { - # Get executables for current tool - $toolExecs = $toolsExecutables[$tool.name] - - foreach ($version in $tool.versions) { - $foundVersionArchPath = Get-ToolsetToolFullPath -Name $tool.name -Version $version -Arch $tool.arch - - if ($toolExecs) { - Write-Host "Run validation test for $($tool.name)($($tool.arch)) $($foundVersion.name) executables..." - Run-ExecutableTests -Executables $toolExecs -ToolPath $foundVersionArchPath - } - } - - if (-not ([string]::IsNullOrEmpty($tool.default))) { - Write-Host "Validate system default $($tool.name)($($tool.arch)) $($tool.default)..." - Validate-SystemDefaultTool -ToolName $tool.name -ExpectedVersion $tool.default - } -} \ No newline at end of file diff --git a/images/win/scripts/Tests/Toolset.Tests.ps1 b/images/win/scripts/Tests/Toolset.Tests.ps1 new file mode 100644 index 000000000..0116ab22c --- /dev/null +++ b/images/win/scripts/Tests/Toolset.Tests.ps1 @@ -0,0 +1,95 @@ +$toolsExecutables = @{ + Python = @( + @{ Binary = "python.exe"; Arguments = "--version" }, + @{ Binary = "Scripts\pip.exe"; Arguments = "--version" } + ) + PyPy = @( + @{ Binary = "python.exe"; Arguments = "--version" }, + @{ Binary = "Scripts\pip.exe"; Arguments = "--version" } + ) + Node = @( + @{ Binary = "node.exe"; Arguments = "--version" }, + @{ Binary = "npm"; Arguments = "--version" } + ) + Go = @( + @{ Binary = "bin\go.exe"; Arguments = "version" } + ) + Ruby = @( + @{ Binary = "bin\ruby.exe"; Arguments = "--version" } + ) +} + +function Get-ToolExecutables { + Param ([String] $Name) + if ($toolsExecutables.ContainsKey($Name)) { $toolsExecutables[$Name] } else { @() } +} + +function Test-Binaries { + Param ( + [String] $Name, + [String] $Version, + [String] $Arch, + [Array] $ToolExecs + ) + $testCases = $ToolExecs | ForEach-Object { + @{ Name = $Name; Version = $Version; Arch = $Arch; Binary = $_.Binary; Arguments = $_.Arguments } + } + It " " -TestCases $testCases { + $binaryFullPath = Join-Path (Get-ToolsetToolFullPath -Name $Name -Version $Version -Arch $Arch) $Binary + "$binaryFullPath $Arguments" | Should -ReturnZeroExitCode + } +} + +function Test-DefaultVersion { + Param ( + [String] $Name, + [String] $ExpectedVersion, + [Array] $ToolExecs + ) + $binaryName = [IO.Path]::GetFileNameWithoutExtension($ToolExecs[0].Binary) + $testCase = @{ Binary = $binaryName; Arguments = $ToolExecs[0].Arguments; ExpectedVersion = $ExpectedVersion } + It " is default version" -TestCases $testCase { + $commandResult = Get-CommandResult "$Binary $Arguments" + $commandResult.ExitCode | Should -Be 0 + $commandResult.Output | Should -Match $ExpectedVersion + } + + It "default version is located in tool-cache" -TestCases $testCase { + $binaryFullPath = Get-WhichTool $Binary + $toolcacheDirectory = Get-ToolcacheToolDirectory -ToolName $Name + $binaryFullPath | Should -Match ([Regex]::Escape($toolcacheDirectory)) + } +} + +$tools = Get-ToolsetContent | Select-Object -ExpandProperty toolcache +# convert old tool-cache to toolset format on-flight to re-use code +(Get-ToolcachePackages).PSObject.Properties | ForEach-Object { + $packageNameParts = $_.Name.Split("-") + $tools += @{ name = $packageNameParts[1]; arch = $packageNameParts[3]; versions = $_.Value; default = "." } +} + +foreach ($tool in $tools) { + Describe "$($tool.name) [$($tool.arch)]" { + $toolExecs = Get-ToolExecutables -Name $tool.name + + foreach ($version in $tool.versions) { + Context "$version" { + $toolInfo = @{ Name = $tool.name; Version = $version; Arch = $tool.arch } + It "tool-cache directory exists" -TestCases $toolInfo { + $toolFullPath = Get-ToolsetToolFullPath -Name $Name -Version $Version -Arch $Arch + $toolFullPath | Should -Exist + } + + if ($toolExecs) { + Test-Binaries -Name $tool.name -Version $version -Arch $tool.arch -ToolExecs $toolExecs + } + } + } + + if ($tool.default -and $toolExecs) { + Context "Default" { + Test-DefaultVersion -Name $tool.name -ExpectedVersion $tool.default -ToolExecs $toolExecs + } + } + } +} \ No newline at end of file