Implement PGO in pipelines for AMD64 architecture; supply training test scenarios (#10071)

Implement PGO in pipelines for AMD64 architecture; supply training test scenarios

## References
- #3075 - Relevant to speed interests there and other linked issues.

## PR Checklist
* [x] Closes #6963
* [x] I work here.
* [x] New UIA Tests added and passed. Manual build runs also tested.

## Detailed Description of the Pull Request / Additional comments
- Creates a new pipeline run for creating instrumented binaries for Profile Guided Optimization (PGO).
- Creates a new suite of UIA tests on the full Windows Terminal app to run PGO training scenarios on instrumented binaries (and incidentally can be used to write other UIA tests later for the full Terminal app.)
- Creates a new NuGet artifact to store trained PGO databases (PGD files) at `Microsoft.Internal.Windows.Terminal.PGODatabase`
- Creates a new NuGet artifact to supply large-scale test content for automated tests at `Microsoft.Internal.Windows.Terminal.TestContent`
- Adjusts the release pipeline to run binaries in PGO optimized mode where content from PGO databases is leveraged at link time to optimize the final release build

The following binaries are trained:
- OpenConsole.exe
- WindowsTerminal.exe
- TerminalApp.dll
- TerminalConnection.dll
- Microsoft.Terminal.Control.dll
- Microsoft.Terminal.Remoting.dll
- Microsoft.Terminal.Settings.Editor.dll
- Microsoft.Terminal.Settings.Model.dll

In the future, adding `<PgoTarget>true</PgoTarget>` to a new `vcxproj` file will automatically enroll the DLL/EXE for PGO instrumentation and optimization going forward.

Two training test scenarios are implemented:
- Smoke test the Terminal by just opening it and typing a bit of text then exiting. (Should help focus on the standard launch path.)
- Optimize bulk text output by launching terminal, outputting `big.txt`, then exiting.

Additional scenarios can be contributed to the `WindowsTerminal_UIATests` project with the `[TestProperty("IsPGO", "true")]` annotation to add them to the suite of scenarios for PGO.

**NOTE:** There are currently no weights applied to the various test scenarios. We will revisit that in the future when/if necessary.

## Validation Steps Performed
- [x] - Training run completed at https://dev.azure.com/ms/terminal/_build?definitionId=492&_a=summary
- [x] - Optimization run completed locally (by forcing `PGOBuildMode` to `Optimize` on my local machine, manually retrieving the databases with NuGet, and building).
- [x] - Validated locally that x86 and ARM64 do not get trained and automatically skip optimization as databases are not present for them.
- [x] - Smoke tested optimized binary versus latest releases. `big.txt` output through CMD is ~11-12seconds prior to PGO and just over 8 seconds with PGO.
This commit is contained in:
Michael Niksa 2021-05-13 14:12:30 -07:00 committed by GitHub
parent a3a2a4102d
commit 7dadde5dd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2496 additions and 95 deletions

View File

@ -18,10 +18,10 @@ ACIOSS
ACover
actctx
ACTCTXW
actionmap
activatable
ACTIVEBORDER
ACTIVECAPTION
adaa
ADDALIAS
ADDB
ADDREF
@ -199,6 +199,7 @@ byref
bytearray
bytebuffer
cac
cacafire
callee
cang
capslock
@ -298,6 +299,8 @@ codepage
codepath
codepoint
codeproject
coffgroup
coffgrp
coinit
COLLECTIONURI
colorizing
@ -487,6 +490,7 @@ dai
DATABLOCK
DATAVIEW
DATAW
datetime
DBatch
dbcs
DBCSCHAR
@ -676,6 +680,7 @@ dxgidwm
dxinterop
dxttbmp
eachother
eae
eaf
EASTEUROPE
eb
@ -698,6 +703,7 @@ elems
elif
elseif
emacs
emplate
EMPTYBOX
enabledelayedexpansion
endian
@ -761,6 +767,7 @@ fcb
fcharset
fclose
fcntl
fd
fdc
FDD
fdopen
@ -781,6 +788,7 @@ FILESYSPATH
filesystem
FILETIME
FILETYPE
fileurl
FILEW
FILLATTR
FILLCONSOLEOUTPUT
@ -846,6 +854,7 @@ fuzzwrapper
fwdecl
fwe
fwlink
Fx
GAUSSIAN
gb
gci
@ -854,6 +863,7 @@ gcy
gdi
gdip
gdirenderer
GENPROFILE
geopol
GETALIAS
GETALIASES
@ -973,6 +983,7 @@ hfont
hglobal
hh
hhh
HHmm
hhook
hhx
HIBYTE
@ -1003,6 +1014,7 @@ horiz
HORZ
hostable
hostlib
Hostx
HPA
HPAINTBUFFER
hpcon
@ -1129,6 +1141,7 @@ INPUTPROCESSORPROFILE
inputrc
Inputreadhandledata
INSERTMODE
installationpath
intellisense
INTERACTIVITYBASE
INTERCEPTCOPYPASTE
@ -1430,6 +1443,7 @@ mkdir
MMBB
mmcc
MMCPL
MMdd
mmsystem
MNC
MNOPQ
@ -1618,6 +1632,7 @@ numlock
numpad
NUMSCROLL
nupkg
nuspec
NVIDIA
NVR
Nx
@ -1760,7 +1775,11 @@ PFNCONSOLECREATEIOTHREAD
PFONT
PFONTENUMDATA
PFS
pgd
pgdn
pgorepro
pgort
PGU
pguid
pgup
PHANDLE
@ -1994,7 +2013,6 @@ Replymessage
repositorypath
rescap
Resequence
reserialize
RESETCONTENT
resheader
resizable
@ -2271,6 +2289,7 @@ STX
stylecop
SUA
subcompartment
subfolder
subkey
SUBLANG
sublicensable
@ -2521,7 +2540,7 @@ UNORM
unparseable
unpause
Unregister
Unregistering
unregistering
untests
untextured
untimes
@ -2831,7 +2850,6 @@ xdy
XEncoding
xes
Xes
XES
xff
XFile
XFORM
@ -2879,6 +2897,7 @@ YVIRTUALSCREEN
Yw
YWalk
yx
yy
YZ
Zc
ZCmd
@ -2890,3 +2909,4 @@ zsh
zu
zxcvbnm
zy
zz

View File

@ -252,10 +252,26 @@ DEALINGS IN THE SOFTWARE.
```
# Microsoft Open Source
This product also incorporates source code from other Microsoft open source projects, all licensed under the MIT license.
## `GSL`
**Source**: [https://github.com/microsoft/GSL](https://github.com/microsoft/GSL)
## `Microsoft-UI-XAML`
**Source**: [https://github.com/microsoft/Microsoft-UI-XAML](https://github.com/microsoft/Microsoft-UI-XAML)
## `VirtualDesktopUtils`
**Source**: [https://github.com/microsoft/PowerToys](https://github.com/microsoft/PowerToys)
## `wil`
**Source**: [https://github.com/microsoft/wil](https://github.com/microsoft/wil)
### License
```

View File

@ -395,6 +395,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Control", "src\ca
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests", "src\cascadia\WindowsTerminal_UIATests\WindowsTerminal.UIA.Tests.csproj", "{F19DACD5-0C6E-40DC-B6E4-767A3200542C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@ -3256,6 +3258,40 @@ Global
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x64.Build.0 = Release|x64
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x86.ActiveCfg = Release|Win32
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|x86.Build.0 = Release|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|ARM.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|ARM64.ActiveCfg = Debug|ARM64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|DotNet_x64Test.ActiveCfg = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|DotNet_x86Test.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|x64.ActiveCfg = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.AuditMode|x86.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|Any CPU.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|ARM64.Build.0 = Debug|ARM64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x64.ActiveCfg = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x64.Build.0 = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x86.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Debug|x86.Build.0 = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|ARM.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|DotNet_x64Test.ActiveCfg = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|DotNet_x86Test.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|x64.ActiveCfg = Debug|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Fuzzing|x86.ActiveCfg = Debug|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|Any CPU.ActiveCfg = Release|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM.ActiveCfg = Release|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM64.ActiveCfg = Release|ARM64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|ARM64.Build.0 = Release|ARM64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|DotNet_x64Test.ActiveCfg = Release|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.ActiveCfg = Release|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.Build.0 = Release|x64
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.ActiveCfg = Release|Win32
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -3353,6 +3389,7 @@ Global
{9921CA0A-320C-4460-8623-3A3196E7F4CB} = {59840756-302F-44DF-AA47-441A9D673202}
{05D9052F-D78F-478F-968A-2DE38A6DB996} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
{F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}

View File

@ -29,4 +29,147 @@ function GetQueryTestRunsUri
$baseUri = GetAzureDevOpsBaseUri -CollectionUri $CollectionUri -TeamProject $TeamProject
$queryUri = "$baseUri/_apis/test/runs?buildUri=$BuildUri$includeRunDetailsParameter&api-version=5.0"
return $queryUri
}
function Get-HelixJobTypeFromTestRun
{
Param ($testRun)
$testRunSingleResultUri = "$($testRun.url)/results?`$top=1&`$skip=0&api-version=5.1"
$singleTestResult = Invoke-RestMethod -Uri $testRunSingleResultUri -Method Get -Headers $azureDevOpsRestApiHeaders
$count = $singleTestResult.value.Length
if($count -eq 0)
{
# If the count is 0, then results have not yet been reported for this run.
# We only care about completed runs with results, so it is ok to just return 'UNKNOWN' for this run.
return "UNKNOWN"
}
else
{
$info = ConvertFrom-Json $singleTestResult.value.comment
$helixJobId = $info.HelixJobId
$job = Invoke-RestMethodWithRetries "https://helix.dot.net/api/2019-06-17/jobs/${helixJobId}?access_token=${HelixAccessToken}"
return $job.Type
}
}
function Append-HelixAccessTokenToUrl
{
Param ([string]$url, [string]$token)
if($url.Contains("?"))
{
$url = "$($url)&access_token=$($token)"
}
else
{
$url = "$($url)?access_token=$($token)"
}
return $url
}
# The Helix Rest api is sometimes unreliable. So we call these apis with retry logic.
# Note: The Azure DevOps apis are stable and do not need to be called with this retry logic.
$helixApiRetries = 0
$helixApiRetriesMax = 10
function Download-StringWithRetries
{
Param ([string]$fileName, [string]$url)
$result = ""
$done = $false
while(!($done))
{
try
{
Write-Host "Downloading $fileName"
$result = (New-Object System.Net.WebClient).DownloadString($url)
$done = $true
}
catch
{
Write-Host "Failed to download $fileName $($PSItem.Exception)"
$helixApiRetries = $helixApiRetries + 1
if($helixApiRetries -lt $helixApiRetriesMax)
{
Write-Host "Sleep and retry download of $fileName"
Start-Sleep 60
}
else
{
throw "Failed to download $fileName"
}
}
}
return $result
}
function Invoke-RestMethodWithRetries
{
Param ([string]$url,$Headers)
$result = @()
$done = $false
while(!($done))
{
try
{
$result = Invoke-RestMethod -Uri $url -Method Get -Headers $Headers
$done = $true
}
catch
{
Write-Host "Failed to invoke Rest method $($PSItem.Exception)"
$helixApiRetries = $helixApiRetries + 1
if($helixApiRetries -lt $helixApiRetriesMax)
{
Write-Host "Sleep and retry invoke"
Start-Sleep 60
}
else
{
throw "Failed to invoke Rest method"
}
}
}
return $result
}
function Download-FileWithRetries
{
Param ([string]$fileurl, [string]$destination)
$done = $false
while(!($done))
{
try
{
Write-Host "Downloading $destination"
$webClient.DownloadFile($fileurl, $destination)
$done = $true
}
catch
{
Write-Host "Failed to download $destination $($PSItem.Exception)"
$helixApiRetries = $helixApiRetries + 1
if($helixApiRetries -lt $helixApiRetriesMax)
{
Write-Host "Sleep and retry download of $destination"
Start-Sleep 60
}
else
{
throw "Failed to download $destination"
}
}
}
}

View File

@ -20,10 +20,12 @@ New-Item -ItemType Directory -Force -Path $payloadDir
# Copy files from nuget packages
Copy-Item "$nugetPackagesDir\microsoft.windows.apps.test.1.0.181203002\lib\netcoreapp2.1\*.dll" $payloadDir
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\*" $payloadDir
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\CoreClr\*" $payloadDir
Copy-Item "$nugetPackagesDir\Microsoft.Taef.10.58.210305002\build\Binaries\$Platform\NetFx4.5\*" $payloadDir
New-Item -ItemType Directory -Force -Path "$payloadDir\.NETCoreApp2.1\"
Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\lib\netcoreapp2.1\*" "$payloadDir\.NETCoreApp2.1\"
Copy-Item "$nugetPackagesDir\runtime.win-$Platform.microsoft.netcore.app.2.1.0\runtimes\win-$Platform\native\*" "$payloadDir\.NETCoreApp2.1\"
New-Item -ItemType Directory -Force -Path "$payloadDir\content\"
Copy-Item "$nugetPackagesDir\Microsoft.Internal.Windows.Terminal.TestContent.1.0.1\content\*" "$payloadDir\content\"
function Copy-If-Exists
{
@ -52,3 +54,13 @@ Copy-Item "build\helix\HelixTestHelpers.cs" "$payloadDir"
Copy-Item "build\helix\runtests.cmd" $payloadDir
Copy-Item "build\helix\InstallTestAppDependencies.ps1" "$payloadDir"
Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
# Copy the APPX package from the 'drop' artifact dir
Copy-Item "$repoDirectory\Artifacts\$ArtifactName\appx\CascadiaPackage_0.0.1.0_$Platform.msix" $payloadDir\CascadiaPackage.zip
# Rename it to extension of ZIP because Expand-Archive is real sassy on the build machines
# and refuses to unzip it because of its file extension while on a desktop, it just
# does the job without complaining.
# Extract the APPX package
Expand-Archive -LiteralPath $payloadDir\CascadiaPackage.zip -DestinationPath $payloadDir\appx

View File

@ -9,11 +9,6 @@ Param(
$helixLinkFile = "$OutputFolder\LinksToHelixTestFiles.html"
$accessTokenParam = ""
if($HelixAccessToken)
{
$accessTokenParam = "?access_token=$HelixAccessToken"
}
function Generate-File-Links
{
@ -45,66 +40,69 @@ $azureDevOpsRestApiHeaders = @{
$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri -IncludeRunDetails
Write-Host "queryUri = $queryUri"
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
$webClient = New-Object System.Net.WebClient
[System.Collections.Generic.List[string]]$workItems = @()
foreach ($testRun in $testRuns.value)
{
$testResults = Invoke-RestMethod -Uri "$($testRun.url)/results?api-version=5.0" -Method Get -Headers $azureDevOpsRestApiHeaders
Write-Host "testRunUri = $testRun.url"
$testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
$isTestRunNameShown = $false
foreach ($testResult in $testResults.value)
{
if ("comment" -in $testResult)
$info = ConvertFrom-Json $testResult.comment
$helixJobId = $info.HelixJobId
$helixWorkItemName = $info.HelixWorkItemName
$workItem = "$helixJobId-$helixWorkItemName"
Write-Host "Helix Work Item = $workItem"
if (-not $workItems.Contains($workItem))
{
$info = ConvertFrom-Json $testResult.comment
$helixJobId = $info.HelixJobId
$helixWorkItemName = $info.HelixWorkItemName
$workItems.Add($workItem)
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
$files = Invoke-RestMethodWithRetries $filesQueryUri
$workItem = "$helixJobId-$helixWorkItemName"
if (-not $workItems.Contains($workItem))
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
$dumps = $files | where { $_.Name.EndsWith(".dmp") }
$pgcFiles = $files | where { $_.Name.EndsWith(".pgc") }
if ($screenShots.Count + $dumps.Count + $pgcFiles.Count -gt 0)
{
$workItems.Add($workItem)
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
$files = Invoke-RestMethod -Uri $filesQueryUri -Method Get
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
$dumps = $files | where { $_.Name.EndsWith(".dmp") }
$pgcFiles = $files | where { $_.Name.EndsWith(".pgc") }
if ($screenShots.Count + $dumps.Count + $pgcFiles.Count -gt 0)
if(-Not $isTestRunNameShown)
{
if(-Not $isTestRunNameShown)
Out-File -FilePath $helixLinkFile -Append -InputObject "<h2>$($testRun.name)</h2>"
$isTestRunNameShown = $true
}
Out-File -FilePath $helixLinkFile -Append -InputObject "<h3>$helixWorkItemName</h3>"
Generate-File-Links $screenShots "Screenshots"
Generate-File-Links $dumps "CrashDumps"
Generate-File-Links $pgcFiles "PGC files"
$misc = $files | where { ($screenShots -NotContains $_) -And ($dumps -NotContains $_) -And ($visualTreeVerificationFiles -NotContains $_) -And ($pgcFiles -NotContains $_) }
Generate-File-Links $misc "Misc"
foreach($pgcFile in $pgcFiles)
{
$flavorPath = $testResult.automatedTestName.Split('.')[0]
$archPath = $testResult.automatedTestName.Split('.')[1]
$fileName = $pgcFile.Name
$fullPath = "$OutputFolder\PGO\$flavorPath\$archPath"
$destination = "$fullPath\$fileName"
Write-Host "Copying $($pgcFile.Name) to $destination"
if (-Not (Test-Path $fullPath))
{
Out-File -FilePath $helixLinkFile -Append -InputObject "<h2>$($testRun.name)</h2>"
$isTestRunNameShown = $true
New-Item $fullPath -ItemType Directory
}
Out-File -FilePath $helixLinkFile -Append -InputObject "<h3>$helixWorkItemName</h3>"
Generate-File-Links $screenShots "Screenshots"
Generate-File-Links $dumps "CrashDumps"
Generate-File-Links $pgcFiles "PGC files"
$misc = $files | where { ($screenShots -NotContains $_) -And ($dumps -NotContains $_) -And ($visualTreeVerificationFiles -NotContains $_) -And ($pgcFiles -NotContains $_) }
Generate-File-Links $misc "Misc"
foreach($pgcFile in $pgcFiles)
{
$flavorPath = $pgcFile.Name.Split('.')[0]
$archPath = $pgcFile.Name.Split('.')[1]
$fileName = $pgcFile.Name.Remove(0, $flavorPath.length + $archPath.length + 2)
$fullPath = "$OutputFolder\PGO\$flavorPath\$archPath"
$destination = "$fullPath\$fileName"
$link = $pgcFile.Link
Write-Host "Copying $($pgcFile.Name) to $destination"
Write-Host "Downloading $link to $destination"
if (-Not (Test-Path $fullPath))
{
New-Item $fullPath -ItemType Directory
}
$link = "$($pgcFile.Link)$accessTokenParam"
$webClient.DownloadFile($link, $destination)
}
Download-FileWithRetries $link $destination
}
}
}

View File

@ -13,6 +13,8 @@
</ItemGroup>
<!-- These .proj files are generated by the build machine prior to running tests via GenerateTestProjFile.ps1. -->
<Import Project="$(ProjFilesPath)\RunTestsInHelix-TerminalAppLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
<Import Project="$(ProjFilesPath)\RunTestsInHelix-HostTestsUIA.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-TerminalAppLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-SettingsModelLocalTests.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-HostTestsUIA.proj" Condition=" '$(TestSuite)'=='DevTestSuite' " />
<Import Project="$(ProjFilesPath)\$(Configuration)\$(Platform)\RunTestsInHelix-WindowsTerminalUIATests.proj" Condition=" '$(TestSuite)'=='PgoInstrumentationSuite' " />
</Project>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
<package id="Microsoft.Internal.Windows.Terminal.TestContent" version="1.0.1" />
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="native" />
<package id="microsoft.windows.apps.test" version="1.0.181203002" targetFramework="native" />
<package id="runtime.win-x86.microsoft.netcore.app" version="2.1.0" targetFramework="native" />

View File

@ -28,7 +28,7 @@ echo %TIME%
powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1
echo %TIME%
set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll
set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll WindowsTerminal.UIA.Tests.dll
set testBinaries=
for %%B in (%testBinaryCandidates%) do (
if exist %%B (
@ -46,7 +46,6 @@ move te.wtl te_original.wtl
copy /y te_original.wtl %HELIX_WORKITEM_UPLOAD_ROOT%
copy /y WexLogFileOutput\*.jpg %HELIX_WORKITEM_UPLOAD_ROOT%
for /f "tokens=* delims=" %%a in ('dir /b *.pgc') do ren "%%a" "%testnameprefix%.%%~na.pgc"
copy /y *.pgc %HELIX_WORKITEM_UPLOAD_ROOT%
set FailedTestQuery=

27
build/pipelines/pgo.yml Normal file
View File

@ -0,0 +1,27 @@
trigger: none
pr: none
variables:
- name: runCodesignValidationInjectionBG
value: false
# 0.0.yyMM.dd##
# 0.0.1904.0900
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
stages:
- stage: Build_x64
displayName: Build x64
dependsOn: []
condition: succeeded()
jobs:
- template: ./templates/build-console-pgo.yml
parameters:
platform: x64
- stage: Publish_PGO_Databases
displayName: Publish PGO databases
dependsOn: ['Build_x64']
jobs:
- template: ./templates/pgo-build-and-publish-nuget-job.yml
parameters:
pgoArtifact: 'PGO'

View File

@ -9,6 +9,7 @@ jobs:
variables:
BuildConfiguration: ${{ parameters.configuration }}
BuildPlatform: ${{ parameters.platform }}
PGOBuildMode: 'Optimize'
pool:
name: Package ES Lab E

View File

@ -0,0 +1,51 @@
parameters:
configuration: 'Release'
platform: ''
additionalBuildArguments: ''
minimumExpectedTestsExecutedCount: 1 # Sanity check for minimum expected tests to be reported
rerunPassesRequiredToAvoidFailure: 0
jobs:
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
variables:
BuildConfiguration: ${{ parameters.configuration }}
BuildPlatform: ${{ parameters.platform }}
PGOBuildMode: 'Instrument'
pool: "windevbuildagents"
# The public pool is also an option!
# pool: { vmImage: windows-2019 }
steps:
- template: build-console-steps.yml
parameters:
additionalBuildArguments: ${{ parameters.additionalBuildArguments }}
- template: helix-runtests-job.yml
parameters:
name: 'RunTestsInHelix'
dependsOn: Build${{ parameters.platform }}${{ parameters.configuration }}
condition: succeeded()
testSuite: 'PgoInstrumentationSuite'
taefQuery: '@IsPgo=true'
configuration: ${{ parameters.configuration }}
platform: ${{ parameters.platform }}
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
- template: helix-processtestresults-job.yml
parameters:
name: 'ProcessTestResults'
pgoArtifact: 'PGO'
dependsOn:
- RunTestsInHelix
condition: succeededOrFailed()
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
minimumExpectedTestsExecutedCount: ${{ parameters.minimumExpectedTestsExecutedCount }}
- template: pgo-merge-pgd-job.yml
parameters:
name: 'MergePGD'
dependsOn:
- ProcessTestResults
pgoArtifact: 'PGO'
platform: ${{ parameters.platform }}

View File

@ -32,6 +32,29 @@ steps:
restoreSolution: build/packages.config
restoreDirectory: '$(Build.SourcesDirectory)\packages'
# The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
- script: |
"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
del %TEMP%\vsinstalldir.txt
call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
echo VCToolsInstallDir = %VCToolsInstallDir%
echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
displayName: 'Retrieve VC tools directory'
- task: CmdLine@1
displayName: 'Display build machine environment variables'
inputs:
filename: 'set'
- task: powershell@2
displayName: 'Restore PGO database'
condition: eq(variables['PGOBuildMode'], 'Optimize')
inputs:
targetType: filePath
workingDirectory: $(Build.SourcesDirectory)\tools\PGODatabase
filePath: $(Build.SourcesDirectory)\tools\PGODatabase\restore-pgodb.ps1
- task: VSBuild@1
displayName: 'Build solution **\OpenConsole.sln'
inputs:
@ -45,6 +68,9 @@ steps:
- task: PowerShell@2
displayName: 'Check MSIX for common regressions'
# PGO runtime needs its own CRT and it's in the package for convenience.
# That will make this script mad so skip since we're not shipping the PGO Instrumentation one anyway.
condition: ne(variables['PGOBuildMode'], 'Instrument')
inputs:
targetType: inline
script: |
@ -53,6 +79,7 @@ steps:
- task: powershell@2
displayName: 'Source Index PDBs'
condition: ne(variables['PGOBuildMode'], 'Instrument')
inputs:
targetType: filePath
filePath: build\scripts\Index-Pdbs.ps1
@ -68,13 +95,25 @@ steps:
If ($Arch -Eq "x86") { $Arch = "Win32" }
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
- task: PowerShell@2
displayName: 'Validate binaries are optimized'
condition: eq(variables['pgoBuildMode'], 'Optimize')
inputs:
targetType: inline
script: |
$Binaries = 'OpenConsole.exe', 'WindowsTerminal.exe', 'TerminalApp.dll', 'TerminalConnection.dll', 'Microsoft.Terminal.Control.dll', 'Microsoft.Terminal.Remoting.dll', 'Microsoft.Terminal.Settings.Editor.dll', 'Microsoft.Terminal.Settings.Model.dll'
foreach ($BinFile in $Binaries)
{
& "$(Build.SourcesDirectory)\tools\PGODatabase\verify-pgo.ps1" "$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/$BinFile"
}
- task: PowerShell@2
displayName: 'Run Unit Tests'
inputs:
targetType: filePath
filePath: build\scripts\Run-Tests.ps1
arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}'
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
condition: and(and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument')), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
- task: PowerShell@2
displayName: 'Run Feature Tests (x64 only)'
@ -82,7 +121,7 @@ steps:
targetType: filePath
filePath: build\scripts\Run-Tests.ps1
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)' -LogPath '${{ parameters.testLogPath }}'
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
condition: and(and(succeeded(), ne(variables['PGOBuildMode'], 'Instrument')), eq(variables['BuildPlatform'], 'x64'))
- task: PowerShell@2
displayName: 'Convert Test Logs from WTL to xUnit format'
@ -90,10 +129,11 @@ steps:
targetType: filePath
filePath: build\Helix\ConvertWttLogToXUnit.ps1
arguments: -WttInputPath '${{ parameters.testLogPath }}' -WttSingleRerunInputPath 'unused.wtl' -WttMultipleRerunInputPath 'unused2.wtl' -XUnitOutputPath 'onBuildMachineResults.xml' -TestNamePrefix '$(BuildConfiguration).$(BuildPlatform)'
condition: or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86'))
condition: and(ne(variables['PGOBuildMode'], 'Instrument'),or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
- task: PublishTestResults@2
displayName: 'Upload converted test logs'
condition: ne(variables['PGOBuildMode'], 'Instrument')
inputs:
testResultsFormat: 'xUnit' # Options: JUnit, NUnit, VSTest, xUnit, cTest
testResultsFiles: '**/onBuildMachineResults.xml'
@ -127,24 +167,47 @@ steps:
TargetFolder: '$(Build.ArtifactStagingDirectory)/appx'
OverWrite: true
flattenFolders: true
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
condition: succeeded()
- task: CopyFiles@2
displayName: 'Copy outputs needed for test runs to Artifacts'
inputs:
Contents: |
$(Build.SourcesDirectory)/bin/$(BuildPlatform)/$(BuildConfiguration)/*.exe
$(Build.SourcesDirectory)/bin/$(BuildPlatform)/$(BuildConfiguration)/*.dll
$(Build.SourcesDirectory)/bin/$(BuildPlatform)/$(BuildConfiguration)/*.xml
$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.exe
$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.dll
$(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.xml
**/Microsoft.VCLibs.*.appx
**/TestHostApp/*
**/TestHostApp/*.exe
**/TestHostApp/*.dll
**/TestHostApp/*.xml
!**/*.pdb
!**/*.ipdb
!**/*.obj
!**/*.pch
TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test'
OverWrite: true
flattenFolders: true
condition: and(and(succeeded(), eq(variables['BuildPlatform'], 'x64')), ne(variables['Build.Reason'], 'PullRequest'))
condition: succeeded()
- task: PublishBuildArtifacts@1
displayName: 'Publish All Build Artifacts'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
ArtifactName: 'drop'
- task: CopyFiles@2
displayName: 'Copy PGO databases needed for PGO instrumentation run'
inputs:
Contents: |
**/*.pgd
TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/PGO/$(BuildPlatform)'
OverWrite: true
flattenFolders: true
condition: and(succeeded(), eq(variables['PGOBuildMode'], 'Instrument'))
- task: PublishBuildArtifacts@1
displayName: 'Publish All PGO Artifacts'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/PGO'
ArtifactName: 'PGO'
condition: and(succeeded(), eq(variables['PGOBuildMode'], 'Instrument'))

View File

@ -12,4 +12,4 @@ steps:
inputs:
targetType: filePath
filePath: build\Helix\GenerateTestProjFile.ps1
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'
arguments: -TestFile '${{ parameters.testFilePath }}' -OutputProjFile '$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\${{ parameters.outputProjFileName }}' -JobTestSuiteName '${{ parameters.testSuite }}' -TaefPath '$(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\x86' -TaefQuery '${{ parameters.taefQuery }}'

View File

@ -10,19 +10,11 @@ parameters:
maxParallel: 4
rerunPassesRequiredToAvoidFailure: 5
taefQuery: ''
configuration: ''
platform: ''
# if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
useBuildOutputFromPipeline: $(System.DefinitionId)
matrix:
# Release_x86:
# buildPlatform: 'x86'
# buildConfiguration: 'release'
# openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
# closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml'
Release_x64:
buildPlatform: 'x64'
buildConfiguration: 'release'
openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml'
openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml'
jobs:
- job: ${{ parameters.name }}
@ -33,8 +25,10 @@ jobs:
timeoutInMinutes: 120
strategy:
maxParallel: ${{ parameters.maxParallel }}
matrix: ${{ parameters.matrix }}
variables:
buildConfiguration: ${{ parameters.configuration }}
buildPlatform: ${{ parameters.platform }}
openHelixTargetQueues: ${{ parameters.openHelixTargetQueues }}
artifactsDir: $(Build.SourcesDirectory)\Artifacts
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.58.210305002\build\Binaries\$(buildPlatform)
helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}'
@ -97,9 +91,17 @@ jobs:
filename: 'dir'
arguments: '/s $(Build.SourcesDirectory)\HelixPayload'
- task: PowerShell@2
displayName: 'Make artifact directories'
inputs:
targetType: inline
script: |
New-Item -ItemType Directory -Force -Path "$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\"
New-Item -ItemType Directory -Force -Path "$(Build.ArtifactStagingDirectory)\$(BuildConfiguration)\$(BuildPlatform)\"
- template: helix-createprojfile-steps.yml
parameters:
condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite'))
condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\TerminalApp.LocalTests.dll'
outputProjFileName: 'RunTestsInHelix-TerminalAppLocalTests.proj'
testSuite: '${{ parameters.testSuite }}'
@ -107,7 +109,7 @@ jobs:
- template: helix-createprojfile-steps.yml
parameters:
condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite'))
condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\SettingsModel.LocalTests.dll'
outputProjFileName: 'RunTestsInHelix-SettingsModelLocalTests.proj'
testSuite: '${{ parameters.testSuite }}'
@ -116,12 +118,20 @@ jobs:
- template: helix-createprojfile-steps.yml
parameters:
condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite'))
condition: and(succeeded(),eq('${{ parameters.testSuite }}','DevTestSuite'))
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Conhost.UIA.Tests.dll'
outputProjFileName: 'RunTestsInHelix-HostTestsUIA.proj'
testSuite: '${{ parameters.testSuite }}'
taefQuery: ${{ parameters.taefQuery }}
- template: helix-createprojfile-steps.yml
parameters:
condition: and(succeeded(),or(eq('${{ parameters.testSuite }}','PgoInstrumentationSuite'),eq('${{ parameters.testSuite }}','DevTestSuite')))
testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\WindowsTerminal.UIA.Tests.dll'
outputProjFileName: 'RunTestsInHelix-WindowsTerminalUIATests.proj'
testSuite: '${{ parameters.testSuite }}'
taefQuery: ${{ parameters.taefQuery }}
- task: PublishBuildArtifacts@1
displayName: 'Publish generated .proj files'
inputs:

View File

@ -0,0 +1,62 @@
# From our friends at MUX: https://github.com/microsoft/microsoft-ui-xaml/blob/main/build/AzurePipelinesTemplates/MUX-BuildAndPublishPGONuGet-Job.yml
parameters:
dependsOn: ''
pgoArtifact: PGO
jobs:
- job: BuildAndPublishPGONuGet
dependsOn: ${{ parameters.dependsOn }}
pool:
vmImage: 'windows-2019'
variables:
artifactsPath: $(Build.SourcesDirectory)\Artifacts
pgoToolsPath: $(Build.SourcesDirectory)\tools\PGODatabase
nuspecPath: $(pgoToolsPath)\NuSpecs
nuspecFilename: PGO.nuspec
steps:
- task: DownloadBuildArtifacts@0
inputs:
artifactName: ${{ parameters.pgoArtifact }}
downloadPath: $(artifactsPath)
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 5.2.0'
inputs:
versionSpec: 5.2.0
- task: CopyFiles@2
displayName: 'Copy pgd files to NuGet build directory'
inputs:
sourceFolder: $(artifactsPath)\${{ parameters.pgoArtifact }}
contents: '**\*.pgd'
targetFolder: $(nuspecPath)\tools
- task: powershell@2
displayName: 'Generate NuSpec file'
inputs:
targetType: filePath
filePath: $(pgoToolsPath)\generate-nuspec.ps1
workingDirectory: $(pgoToolsPath)
arguments: $(nuspecPath)\$(nuspecFilename).template $(nuspecPath)\$(nuspecFilename)
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
displayName: 'NuGet pack'
inputs:
command: pack
packagesToPack: '$(nuspecPath)\$(nuspecFilename)'
basePath: '$(nuspecPath)'
packDestination: '$(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: $(Build.ArtifactStagingDirectory)
artifactName: ${{ parameters.pgoArtifact }}
- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2
displayName: 'NuGet push'
inputs:
command: push
publishVstsFeed: Terminal/TerminalDependencies
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg

View File

@ -0,0 +1,90 @@
parameters:
dependsOn: ''
pgoArtifact: PGO
platform: ''
jobs:
- job: MergePGD
dependsOn: ${{ parameters.dependsOn }}
pool:
vmImage: 'windows-2019'
variables:
artifactsPath: $(Build.SourcesDirectory)\Artifacts
pgoArtifactsPath: $(artifactsPath)\${{ parameters.pgoArtifact }}
buildPlatform: ${{ parameters.platform }}
steps:
# The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves.
- script: |
"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt
set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt
del %TEMP%\vsinstalldir.txt
call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat"
echo VCToolsInstallDir = %VCToolsInstallDir%
echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
displayName: 'Retrieve VC tools directory'
- task: DownloadBuildArtifacts@0
inputs:
artifactName: ${{ parameters.pgoArtifact }}
downloadPath: $(artifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge WindowsTerminal*.pgc WindowsTerminal.pgd
displayName: 'Merge Terminal pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge OpenConsole*.pgc OpenConsole.pgd
displayName: 'Merge OpenConsole pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Control*.pgc Microsoft.Terminal.Control.pgd
displayName: 'Merge Microsoft.Terminal.Control pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Remoting*.pgc Microsoft.Terminal.Remoting.pgd
displayName: 'Merge Microsoft.Terminal.Remoting pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Settings.Editor*.pgc Microsoft.Terminal.Settings.Editor.pgd
displayName: 'Merge Microsoft.Terminal.Settings.Editor pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge Microsoft.Terminal.Settings.Model*.pgc Microsoft.Terminal.Settings.Model.pgd
displayName: 'Merge Microsoft.Terminal.Settings.Model pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge TerminalApp*.pgc TerminalApp.pgd
displayName: 'Merge TerminalApp pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- script: |
cd $(buildPlatform)
"%VCToolsInstallDir%\bin\hostx64\x64\pgomgr.exe" /merge TerminalConnection*.pgc TerminalConnection.pgd
displayName: 'Merge TerminalConnection pgc files into pgd'
workingDirectory: $(pgoArtifactsPath)
- task: CopyFiles@2
displayName: 'Copy merged pgd to artifact staging'
inputs:
sourceFolder: $(pgoArtifactsPath)
contents: '**\$(buildPlatform)\*.pgd'
targetFolder: $(Build.ArtifactStagingDirectory)
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: $(Build.ArtifactStagingDirectory)
artifactName: ${{ parameters.pgoArtifact }}

View File

@ -10,6 +10,7 @@
<SubSystem>Console</SubSystem>
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<PgoTarget>true</PgoTarget>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@ -61,8 +62,8 @@
<Link>
<AdditionalDependencies>User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!-- Our lib contains a DllMain that we need to force the use of. -->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain %(AdditionalOptions)</AdditionalOptions>
</Link>
<Reference>
<!-- Do not propagate microsoft.ui.xaml upwards as a private dependency. -->

View File

@ -10,6 +10,7 @@
<SubSystem>Console</SubSystem>
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<PgoTarget>true</PgoTarget>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@ -105,8 +106,8 @@
<Link>
<AdditionalDependencies>User32.lib;WindowsApp.lib;shell32.lib;WinMM.Lib;%(AdditionalDependencies)</AdditionalDependencies>
<!-- TerminalAppLib contains a DllMain that we need to force the use of. -->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />

View File

@ -7,6 +7,7 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
<SubSystem>Console</SubSystem>
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<PgoTarget>true</PgoTarget>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@ -87,4 +88,4 @@
</Link>
</ItemDefinitionGroup>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>

View File

@ -10,6 +10,7 @@
<SubSystem>Console</SubSystem>
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<PgoTarget>true</PgoTarget>
<!-- C++/WinRT sets the depth to 1 if there is a XAML file in the project
Unfortunately for us, we need it to be 3. When the namespace merging
@ -87,8 +88,8 @@
If you don't have this, then you'll see an error like
"(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)"
-->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain %(AdditionalOptions)</AdditionalOptions>
<Reference>
<!-- Do not propagate microsoft.ui.xaml upwards as a private dependency. -->

View File

@ -20,6 +20,7 @@
<SubSystem>Console</SubSystem>
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<PgoTarget>true</PgoTarget>
<!-- C++/WinRT sets the depth to 1 if there is a XAML file in the project
Unfortunately for us, we need it to be 3. When the namespace merging
depth is 1, Microsoft.Terminal.Control becomes "Microsoft",

View File

@ -10,6 +10,7 @@
<SubSystem>Console</SubSystem>
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
<PgoTarget>true</PgoTarget>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@ -115,8 +116,8 @@
<Link>
<AdditionalDependencies>User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!-- TSM Lib contains a DllMain that we need to force the use of. -->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain %(AdditionalOptions)</AdditionalOptions>
</Link>
<Reference>
<!-- Do not propagate microsoft.ui.xaml upwards as a private dependency. -->

View File

@ -17,11 +17,12 @@
<!-- IMPORTANT! cppwinrt.pre.props specifies 17134 -->
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<TargetPlatformIdentifier>Windows</TargetPlatformIdentifier>
<PgoTarget>true</PgoTarget>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<SDLCheck>true</SDLCheck>
@ -170,7 +171,6 @@
</ItemGroup>
<!-- **END VC LIBS HACK** -->
</Target>
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" />
</Project>

View File

@ -0,0 +1,49 @@
//----------------------------------------------------------------------------------------------------------------------
// <copyright file="Globals.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <summary>Windows Terminal UI Automation global settings</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace WindowsTerminal.UIA.Tests.Common
{
using System;
using WEX.TestExecution.Markup;
public static class Globals
{
public const int Timeout = 50; // in milliseconds
public const int LongTimeout = 5000; // in milliseconds
public const int AppCreateTimeout = 3000; // in milliseconds
public static void WaitForTimeout()
{
System.Threading.Thread.Sleep(Globals.Timeout);
}
public static void WaitForLongTimeout()
{
System.Threading.Thread.Sleep(Globals.LongTimeout);
}
static string[] modules =
{
"WindowsTerminal.exe",
"OpenConsole.exe",
"Microsoft.Terminal.Control.dll",
"Microsoft.Terminal.Remoting.dll",
"Microsoft.Terminal.Settings.Editor.dll",
"Microsoft.Terminal.Settings.Model.dll",
"TerminalApp.dll",
"TerminalConnection.dll"
};
public static void SweepAllModules(TestContext context)
{
foreach (var mod in modules)
{
PgoManager.PgoSweepIfInstrumented(context, mod);
}
}
}
}

View File

@ -0,0 +1,861 @@
//----------------------------------------------------------------------------------------------------------------------
// <copyright file="NativeMethods.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <summary>Wrapper class for storing P/Invoke and COM Interop definitions.</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace WindowsTerminal.UIA.Tests.Common.NativeMethods
{
using System;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
using WEX.TestExecution;
using WEX.Logging.Interop;
// Small extension method helpers to make C# feel closer to native.
public static class NativeExtensions
{
public static int LoWord(this int val)
{
return val & 0xffff;
}
public static int HiWord(this int val)
{
return (val >> 16) & 0xffff;
}
}
public static class NativeMethods
{
public static void Win32BoolHelper(bool result, string actionMessage)
{
if (!result)
{
string errorMsg = string.Format("Win32 error occurred: 0x{0:X}", Marshal.GetLastWin32Error());
Log.Comment(errorMsg);
}
Verify.IsTrue(result, actionMessage);
}
public static void Win32NullHelper(IntPtr result, string actionMessage)
{
if (result == IntPtr.Zero)
{
string errorMsg = string.Format("Win32 error occurred: 0x{0:X}", Marshal.GetLastWin32Error());
Log.Comment(errorMsg);
}
Verify.IsNotNull(result, actionMessage);
}
}
public static class WinCon
{
[Flags()]
public enum CONSOLE_SELECTION_INFO_FLAGS : uint
{
CONSOLE_NO_SELECTION = 0x0,
CONSOLE_SELECTION_IN_PROGRESS = 0x1,
CONSOLE_SELECTION_NOT_EMPTY = 0x2,
CONSOLE_MOUSE_SELECTION = 0x4,
CONSOLE_MOUSE_DOWN = 0x8
}
public enum CONSOLE_STD_HANDLE : int
{
STD_INPUT_HANDLE = -10,
STD_OUTPUT_HANDLE = -11,
STD_ERROR_HANDLE = -12
}
public enum CONSOLE_ATTRIBUTES : ushort
{
FOREGROUND_BLUE = 0x1,
FOREGROUND_GREEN = 0x2,
FOREGROUND_RED = 0x4,
FOREGROUND_INTENSITY = 0x8,
FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN,
FOREGROUND_CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE,
FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE,
FOREGROUND_COLORS = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
FOREGROUND_ALL = FOREGROUND_COLORS | FOREGROUND_INTENSITY,
BACKGROUND_BLUE = 0x10,
BACKGROUND_GREEN = 0x20,
BACKGROUND_RED = 0x40,
BACKGROUND_INTENSITY = 0x80,
BACKGROUND_YELLOW = BACKGROUND_RED | BACKGROUND_GREEN,
BACKGROUND_CYAN = BACKGROUND_GREEN | BACKGROUND_BLUE,
BACKGROUND_MAGENTA = BACKGROUND_RED | BACKGROUND_BLUE,
BACKGROUND_COLORS = BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN,
BACKGROUND_ALL = BACKGROUND_COLORS | BACKGROUND_INTENSITY,
COMMON_LVB_LEADING_BYTE = 0x100,
COMMON_LVB_TRAILING_BYTE = 0x200,
COMMON_LVB_GRID_HORIZONTAL = 0x400,
COMMON_LVB_GRID_LVERTICAL = 0x800,
COMMON_LVB_GRID_RVERTICAL = 0x1000,
COMMON_LVB_REVERSE_VIDEO = 0x4000,
COMMON_LVB_UNDERSCORE = 0x8000
}
public enum CONSOLE_OUTPUT_MODES : uint
{
ENABLE_PROCESSED_OUTPUT = 0x1,
ENABLE_WRAP_AT_EOL_OUTPUT = 0x2,
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
}
//CHAR_INFO struct, which was a union in the old days
// so we want to use LayoutKind.Explicit to mimic it as closely
// as we can
[StructLayout(LayoutKind.Explicit)]
public struct CHAR_INFO
{
[FieldOffset(0)]
internal char UnicodeChar;
[FieldOffset(0)]
internal char AsciiChar;
[FieldOffset(2)] //2 bytes seems to work properly
internal CONSOLE_ATTRIBUTES Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short X;
public short Y;
public override string ToString()
{
return string.Format("(X:{0} Y:{1})", X, Y);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
public short Width
{
get
{
// The API returns bottom/right as the inclusive lower-right
// corner, so we need +1 for the true width
return (short)(this.Right - this.Left + 1);
}
}
public short Height
{
get
{
// The API returns bottom/right as the inclusive lower-right
// corner, so we need +1 for the true height
return (short)(this.Bottom - this.Top + 1);
}
}
public override string ToString()
{
return string.Format("(L:{0} T:{1} R:{2} B:{3})", Left, Top, Right, Bottom);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_CURSOR_INFO
{
public uint dwSize;
public bool bVisible;
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_FONT_INFO
{
public int nFont;
public COORD dwFontSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SELECTION_INFO
{
public CONSOLE_SELECTION_INFO_FLAGS Flags;
public COORD SelectionAnchor;
public SMALL_RECT Selection;
public override string ToString()
{
return string.Format("Flags:{0:X} Anchor:{1} Selection:{2}", Flags, SelectionAnchor, Selection);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public CONSOLE_ATTRIBUTES wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO_EX
{
public uint cbSize;
public COORD dwSize;
public COORD dwCursorPosition;
public CONSOLE_ATTRIBUTES wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
public CONSOLE_ATTRIBUTES wPopupAttributes;
public bool bFullscreenSupported;
internal COLORREF black;
internal COLORREF darkBlue;
internal COLORREF darkGreen;
internal COLORREF darkCyan;
internal COLORREF darkRed;
internal COLORREF darkMagenta;
internal COLORREF darkYellow;
internal COLORREF gray;
internal COLORREF darkGray;
internal COLORREF blue;
internal COLORREF green;
internal COLORREF cyan;
internal COLORREF red;
internal COLORREF magenta;
internal COLORREF yellow;
internal COLORREF white;
}
[StructLayout(LayoutKind.Sequential)]
public struct COLORREF
{
internal uint ColorDWORD;
public COLORREF(Color color)
{
ColorDWORD = (uint)color.R + (((uint)color.G) << 8) + (((uint)color.B) << 16);
}
public COLORREF(uint r, uint g, uint b)
{
ColorDWORD = r + (g << 8) + (b << 16);
}
public Color GetColor()
{
return Color.FromArgb((int)(0x000000FFU & ColorDWORD),
(int)(0x0000FF00U & ColorDWORD) >> 8, (int)(0x00FF0000U & ColorDWORD) >> 16);
}
public void SetColor(Color color)
{
ColorDWORD = (uint)color.R + (((uint)color.G) << 8) + (((uint)color.B) << 16);
}
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(CONSOLE_STD_HANDLE nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AttachConsole(UInt32 dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeConsole();
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetConsoleTitle(string ConsoleTitle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleMode(IntPtr hConsoleOutputHandle, out CONSOLE_OUTPUT_MODES lpMode);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint GetConsoleTitle(StringBuilder lpConsoleTitle, int nSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetConsoleWindow();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleSelectionInfo(out CONSOLE_SELECTION_INFO lpConsoleSelectionInfo);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleWindowInfo(IntPtr hConsoleOutput, bool bAbsolute, [In] ref SMALL_RECT lpConsoleWindow);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleCursorInfo(IntPtr hConsoleOutput, out CONSOLE_CURSOR_INFO lpConsoleCursorInfo);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfo);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleScreenBufferInfoEx(IntPtr ConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfoEx);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetCurrentConsoleFont(IntPtr hConsoleOutput, bool bMaximumWindow, out CONSOLE_FONT_INFO lpConsoleCurrentFont);
[DllImport("kernel32.dll", EntryPoint = "ReadConsoleOutputW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool ReadConsoleOutput(
IntPtr hConsoleOutput,
/* This pointer is treated as the origin of a two-dimensional array of CHAR_INFO structures
whose size is specified by the dwBufferSize parameter.*/
[MarshalAs(UnmanagedType.LPArray), Out] CHAR_INFO[,] lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", EntryPoint = "WriteConsoleOutputCharacterW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool WriteConsoleOutputCharacter(
IntPtr hConsoleOutput,
string lpCharacter,
UInt32 nLength,
COORD dwWriteCoord,
ref UInt32 lpNumberOfCharsWritten);
}
/// <summary>
/// The definitions within this file match the winconp.h file that is generated from wincon.w
/// Please see /windows/published/main/wincon.w
/// </summary>
public static class WinConP
{
private static readonly Guid PKEY_Console_FormatId = new Guid(0x0C570607, 0x0396, 0x43DE, new byte[] { 0x9D, 0x61, 0xE3, 0x21, 0xD7, 0xDF, 0x50, 0x26 });
public static readonly Wtypes.PROPERTYKEY PKEY_Console_ForceV2 = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 1 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_WrapText = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 2 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_FilterOnPaste = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 3 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_CtrlKeysDisabled = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 4 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_LineSelection = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 5 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_WindowTransparency = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 6 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_TrimZeros = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 7 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_CursorType = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 8 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_CursorColor = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 9 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_InterceptCopyPaste = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 10 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_DefaultForeground = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 11 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_DefaultBackground = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 12 };
public static readonly Wtypes.PROPERTYKEY PKEY_Console_TerminalScrolling = new Wtypes.PROPERTYKEY() { fmtid = PKEY_Console_FormatId, pid = 13 };
public static readonly uint NT_CONSOLE_PROPS_SIG = 0xA0000002;
public static readonly uint NT_FE_CONSOLE_PROPS_SIG = 0xA0000004;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct NT_CONSOLE_PROPS
{
public Shell32.DATABLOCK_HEADER dbh;
public short wFillAttribute;
public short wPopupFillAttribute;
public WinCon.COORD dwScreenBufferSize;
public WinCon.COORD dwWindowSize;
public WinCon.COORD dwWindowOrigin;
public int nFont;
public int nInputBufferSize;
public WinCon.COORD dwFontSize;
public uint uFontFamily;
public uint uFontWeight;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string FaceName;
public uint uCursorSize;
public int bFullScreen;
public int bQuickEdit;
public int bInsertMode;
public int bAutoPosition;
public uint uHistoryBufferSize;
public uint uNumberOfHistoryBuffers;
public int bHistoryNoDup;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public int[] ColorTable;
public uint CursorType;
public WinCon.COLORREF CursorColor;
public bool InterceptCopyPaste;
public WinCon.COLORREF DefaultForeground;
public WinCon.COLORREF DefaultBackground;
public bool TerminalScrolling;
}
[StructLayout(LayoutKind.Sequential)]
public struct NT_FE_CONSOLE_PROPS
{
Shell32.DATABLOCK_HEADER dbh;
uint uCodePage;
}
}
public static class User32
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public Int32 x;
public Int32 y;
}
public const int WHEEL_DELTA = 120;
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
public const int GWL_STYLE = (-16);
public const int GWL_EXSTYLE = (-20);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
public static extern bool AdjustWindowRectEx(ref RECT lpRect, int dwStyle, bool bMenu, int dwExStyle);
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
public enum WindowMessages : UInt32
{
WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
WM_CHAR = 0x0102,
WM_MOUSEWHEEL = 0x020A,
WM_MOUSEHWHEEL = 0x020E,
WM_USER = 0x0400,
CM_SET_KEY_STATE = WM_USER + 18
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, WindowMessages Msg, Int32 wParam, IntPtr lParam);
public enum SPI : uint
{
SPI_GETWHEELSCROLLLINES = 0x0068,
SPI_GETWHEELSCROLLCHARACTERS = 0x006C
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
public enum WinEventId : uint
{
EVENT_CONSOLE_CARET = 0x4001,
EVENT_CONSOLE_UPDATE_REGION = 0x4002,
EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003,
EVENT_CONSOLE_UPDATE_SCROLL = 0x4004,
EVENT_CONSOLE_LAYOUT = 0x4005,
EVENT_CONSOLE_START_APPLICATION = 0x4006,
EVENT_CONSOLE_END_APPLICATION = 0x4007
}
[Flags]
public enum WinEventFlags : uint
{
WINEVENT_OUTOFCONTEXT = 0x0000, // Events are ASYNC
WINEVENT_SKIPOWNTHREAD = 0x0001, // Don't call back for events on installer's thread
WINEVENT_SKIPOWNPROCESS = 0x0002, // Don't call back for events on installer's process
WINEVENT_INCONTEXT = 0x0004, // Events are SYNC, this causes your dll to be injected into every process
}
public delegate void WinEventDelegate(IntPtr hWinEventHook, WinEventId eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWinEventHook(WinEventId eventMin, WinEventId eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, WinEventFlags dwFlags);
[DllImport("user32.dll")]
public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
public struct MSG
{
public IntPtr hwnd;
public uint message;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public POINT pt;
}
public enum PM : uint
{
PM_NOREMOVE = 0x0000,
PM_REMOVE = 0x0001,
PM_NOYIELD = 0x0002,
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, PM wRemoveMsg);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr DispatchMessage(ref MSG lpmsg);
}
public static class Shell32
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773249(v=vs.85).aspx
[StructLayout(LayoutKind.Sequential)]
public struct DATABLOCK_HEADER
{
public int cbSize;
public int dwSignature;
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944(v=vs.85).aspx
// http://pinvoke.net/default.aspx/Enums/SLGP_FLAGS.html
/// <summary>IShellLink.GetPath fFlags: Flags that specify the type of path information to retrieve</summary>
[Flags()]
public enum SLGP_FLAGS
{
/// <summary>Retrieves the standard short (8.3 format) file name</summary>
SLGP_SHORTPATH = 0x1,
/// <summary>Retrieves the Universal Naming Convention (UNC) path name of the file</summary>
SLGP_UNCPRIORITY = 0x2,
/// <summary>Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded</summary>
SLGP_RAWPATH = 0x4
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952(v=vs.85).aspx
// http://pinvoke.net/default.aspx/Enums/SLR_FLAGS.html
/// <summary>IShellLink.Resolve fFlags</summary>
[Flags()]
public enum SLR_FLAGS
{
/// <summary>
/// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set,
/// the high-order word of fFlags can be set to a time-out value that specifies the
/// maximum amount of time to be spent resolving the link. The function returns if the
/// link cannot be resolved within the time-out duration. If the high-order word is set
/// to zero, the time-out duration will be set to the default value of 3,000 milliseconds
/// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out
/// duration, in milliseconds.
/// </summary>
SLR_NO_UI = 0x1,
/// <summary>Obsolete and no longer used</summary>
SLR_ANY_MATCH = 0x2,
/// <summary>If the link object has changed, update its path and list of identifiers.
/// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine
/// whether or not the link object has changed.</summary>
SLR_UPDATE = 0x4,
/// <summary>Do not update the link information</summary>
SLR_NOUPDATE = 0x8,
/// <summary>Do not execute the search heuristics</summary>
SLR_NOSEARCH = 0x10,
/// <summary>Do not use distributed link tracking</summary>
SLR_NOTRACK = 0x20,
/// <summary>Disable distributed link tracking. By default, distributed link tracking tracks
/// removable media across multiple devices based on the volume name. It also uses the
/// Universal Naming Convention (UNC) path to track remote file systems whose drive letter
/// has changed. Setting SLR_NOLINKINFO disables both types of tracking.</summary>
SLR_NOLINKINFO = 0x40,
/// <summary>Call the Microsoft Windows Installer</summary>
SLR_INVOKE_MSI = 0x80
}
[ComImport, Guid("00021401-0000-0000-C000-000000000046")]
public class ShellLink
{
// Making new of this class will call CoCreate e.g. new ShellLink();
// Cast to one of the interfaces below will QueryInterface. e.g. (IPersistFile)new ShellLink();
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774950(v=vs.85).aspx
// http://pinvoke.net/default.aspx/Interfaces/IShellLinkW.html
/// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary>
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
public interface IShellLinkW
{
/// <summary>Retrieves the path and file name of a Shell link object</summary>
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WinBase.WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
void GetIDList(out IntPtr ppidl);
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
void SetIDList(IntPtr pidl);
/// <summary>Retrieves the description string for a Shell link object</summary>
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
/// <summary>Sets the name of the working directory for a Shell link object</summary>
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
/// <summary>Sets the command-line arguments for a Shell link object</summary>
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
/// <summary>Retrieves the hot key for a Shell link object</summary>
void GetHotkey(out short pwHotkey);
/// <summary>Sets a hot key for a Shell link object</summary>
void SetHotkey(short wHotkey);
/// <summary>Retrieves the show command for a Shell link object</summary>
void GetShowCmd(out int piShowCmd);
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
void SetShowCmd(int iShowCmd);
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
int cchIconPath, out int piIcon);
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
/// <summary>Sets the relative path to the Shell link object</summary>
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
void Resolve(IntPtr hwnd, SLR_FLAGS fFlags);
/// <summary>Sets the path and file name of a Shell link object</summary>
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb774916(v=vs.85).aspx
// http://pinvoke.net/default.aspx/Interfaces/IShellLonkDataList.html
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("45e2b4ae-b1c3-11d0-b92f-00a0c90312e1")]
public interface IShellLinkDataList
{
void AddDataBlock(IntPtr pDataBlock);
void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);
void RemoveDataBlock(uint dwSig);
void GetFlags(out uint pdwFlags);
void SetFlags(uint dwFlags);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms688695(v=vs.85).aspx
// http://pinvoke.net/default.aspx/Interfaces/IPersist.html
[ComImport, Guid("0000010c-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersist
{
[PreserveSig]
void GetClassID(out Guid pClassID);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms687223(v=vs.85).aspx
// http://www.pinvoke.net/default.aspx/Interfaces/IPersistFile.html
[ComImport, Guid("0000010b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPersistFile : IPersist
{
new void GetClassID(out Guid pClassID);
[PreserveSig]
int IsDirty();
[PreserveSig]
void Load([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);
[PreserveSig]
void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
[In, MarshalAs(UnmanagedType.Bool)] bool fRemember);
[PreserveSig]
void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
[PreserveSig]
void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName);
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb761474(v=vs.85).aspx
// http://www.pinvoke.net/default.aspx/Interfaces/IPropertyStore.html
[ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IPropertyStore
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCount([Out] out uint cProps);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetAt([In] uint iProp, out Wtypes.PROPERTYKEY pkey);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetValue([In] ref Wtypes.PROPERTYKEY key, out object pv);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetValue([In] ref Wtypes.PROPERTYKEY key, [In] ref object pv);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Commit();
}
}
public static class WinBase
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365740(v=vs.85).aspx
// http://www.pinvoke.net/default.aspx/Structures/WIN32_FIND_DATA.html
// The CharSet must match the CharSet of the corresponding PInvoke signature
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
public enum STARTF : Int32
{
STARTF_TITLEISLINKNAME = 0x00000800
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public STARTF dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[Flags]
public enum CP_CreationFlags : uint
{
CREATE_SUSPENDED = 0x4,
CREATE_NEW_CONSOLE = 0x10,
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
CP_CreationFlags dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, IntPtr lpName);
[DllImport("kernel32.dll")]
public static extern bool TerminateJobObject(IntPtr hJob, uint uExitCode);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int ResumeThread(IntPtr hThread);
public enum JOBOBJECTINFOCLASS : uint
{
JobObjectBasicProcessIdList = 3
}
[StructLayout(LayoutKind.Sequential)]
public struct JOBOBJECT_BASIC_PROCESS_ID_LIST
{
public uint NumberOfAssignedProcesses;
public uint NumberOfProcessIdsInList;
public IntPtr ProcessId;
public IntPtr ProcessId2;
}
[DllImport("kernel32.dll")]
public static extern bool QueryInformationJobObject(IntPtr hJob,
JOBOBJECTINFOCLASS JobObjectInformationClass,
IntPtr lpJobObjectInfo,
int cbJobObjectInfoLength,
IntPtr lpReturnLength);
}
public static class Wtypes
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773381(v=vs.85).aspx
// http://pinvoke.net/default.aspx/Structures/PROPERTYKEY.html
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PROPERTYKEY
{
public Guid fmtid;
public uint pid;
}
}
public static class ObjBase
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380337(v=vs.85).aspx
// http://www.pinvoke.net/default.aspx/Enums/StgmConstants.html
[Flags]
public enum STGM
{
STGM_READ = 0x0,
STGM_WRITE = 0x1,
STGM_READWRITE = 0x2,
STGM_SHARE_DENY_NONE = 0x40,
STGM_SHARE_DENY_READ = 0x30,
STGM_SHARE_DENY_WRITE = 0x20,
STGM_SHARE_EXCLUSIVE = 0x10,
STGM_PRIORITY = 0x40000,
STGM_CREATE = 0x1000,
STGM_CONVERT = 0x20000,
STGM_FAILIFTHERE = 0x0,
STGM_DIRECT = 0x0,
STGM_TRANSACTED = 0x10000,
STGM_NOSCRATCH = 0x100000,
STGM_NOSNAPSHOT = 0x200000,
STGM_SIMPLE = 0x8000000,
STGM_DIRECT_SWMR = 0x400000,
STGM_DELETEONRELEASE = 0x4000000
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using WEX.Logging.Interop;
using WEX.TestExecution.Markup;
namespace WindowsTerminal.UIA.Tests.Common
{
public static class PgoManager
{
public static void PgoSweepIfInstrumented(TestContext context, string assemblyName)
{
#if PGO_INSTRUMENT
string pgcFileName = context.TestName;
Log.Comment($"Running pgosweep on '{assemblyName}' for test: {pgcFileName}");
try
{
var startInfo = new ProcessStartInfo() {
FileName = Path.GetFullPath(Path.Combine(context.TestDeploymentDir, "pgosweep.exe")),
Arguments = $"{assemblyName} {assemblyName}-{pgcFileName}.pgc",
UseShellExecute = false,
RedirectStandardOutput = true
};
using (var process = Process.Start(startInfo))
{
var output = new StringBuilder();
while (!process.HasExited)
{
Log.Comment(process.StandardOutput.ReadToEnd());
}
}
}
catch (Exception ex)
{
Log.Comment("Failed trying to pgosweep. " + ex.ToString());
throw;
}
#endif
}
}
}

View File

@ -0,0 +1,176 @@
//----------------------------------------------------------------------------------------------------------------------
// <copyright file="TerminalApp.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <summary>Helper and wrapper for generating the base test application and its UI Root node.</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace WindowsTerminal.UIA.Tests.Elements
{
using System;
using System.IO;
using WindowsTerminal.UIA.Tests.Common;
using WindowsTerminal.UIA.Tests.Common.NativeMethods;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Interactions;
using WEX.Logging.Interop;
using WEX.TestExecution;
using WEX.TestExecution.Markup;
using System.Runtime.InteropServices;
using System.Security.Principal;
using OpenQA.Selenium;
public class TerminalApp : IDisposable
{
protected const string AppDriverUrl = "http://127.0.0.1:4723";
private IntPtr job;
public IOSDriver<IOSElement> Session { get; private set; }
public Actions Actions { get; private set; }
public AppiumWebElement UIRoot { get; private set; }
private bool isDisposed = false;
private TestContext context;
public string ContentPath { get; private set; }
public string GetFullTestContentPath(string filename)
{
return Path.GetFullPath(Path.Combine(ContentPath, filename));
}
public TerminalApp(TestContext context, string shellToLaunch = "powershell.exe")
{
this.context = context;
// If running locally, set WTPath to where we can find a loose deployment of Windows Terminal
// On the build machines, the scripts lay it out at the appx\ subfolder of the test deployment directory
string path = Path.GetFullPath(Path.Combine(context.TestDeploymentDir, @"appx\WindowsTerminal.exe"));
if (context.Properties.Contains("WTPath"))
{
path = (string)context.Properties["WTPath"];
}
Log.Comment($"Windows Terminal will be launched from '{path}'");
// Same goes for the content directory. Set WTTestContent for where the content files are
// for running tests.
// On the build machines, the scripts lay it out at the content\ subfolder.
ContentPath = @"content";
if (context.Properties.Contains("WTTestContent"))
{
ContentPath = (string)context.Properties["WTTestContent"];
}
Log.Comment($"Test Content will be loaded from '{Path.GetFullPath(ContentPath)}'");
this.CreateProcess(path, shellToLaunch);
}
~TerminalApp()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public AppiumWebElement GetRoot()
{
return this.UIRoot;
}
protected virtual void Dispose(bool disposing)
{
if (!this.isDisposed)
{
// ensure we're exited when this is destroyed or disposed of explicitly
this.ExitProcess();
this.isDisposed = true;
}
}
private void CreateProcess(string path, string shellToLaunch)
{
string WindowTitleToFind = "WindowsTerminal.UIA.Tests";
job = WinBase.CreateJobObject(IntPtr.Zero, IntPtr.Zero);
NativeMethods.Win32NullHelper(job, "Creating job object to hold binaries under test.");
Log.Comment("Attempting to launch command-line application at '{0}'", path);
string binaryToRunPath = path;
string args = $"new-tab --title \"{WindowTitleToFind}\" --suppressApplicationTitle \"{shellToLaunch}\"";
string launchArgs = $"{binaryToRunPath} {args}";
WinBase.STARTUPINFO si = new WinBase.STARTUPINFO();
si.cb = Marshal.SizeOf(si);
WinBase.PROCESS_INFORMATION pi = new WinBase.PROCESS_INFORMATION();
NativeMethods.Win32BoolHelper(WinBase.CreateProcess(null,
launchArgs,
IntPtr.Zero,
IntPtr.Zero,
false,
WinBase.CP_CreationFlags.CREATE_SUSPENDED,
IntPtr.Zero,
null,
ref si,
out pi),
"Attempting to create child host window process.");
Log.Comment($"Host window PID: {pi.dwProcessId}");
NativeMethods.Win32BoolHelper(WinBase.AssignProcessToJobObject(job, pi.hProcess), "Assigning new host window (suspended) to job object.");
NativeMethods.Win32BoolHelper(-1 != WinBase.ResumeThread(pi.hThread), "Resume host window process now that it is attached and its launch of the child application will be caught in the job object.");
Globals.WaitForTimeout();
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", @"Root");
Session = new IOSDriver<IOSElement>(new Uri(AppDriverUrl), appCapabilities);
Verify.IsNotNull(Session);
Actions = new Actions(Session);
Verify.IsNotNull(Session);
Globals.WaitForLongTimeout();
UIRoot = Session.FindElementByName(WindowTitleToFind);
// Set the timeout to 15 seconds after we found the initial window.
Session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(15);
}
private bool IsRunningAsAdmin()
{
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
}
private void ExitProcess()
{
Globals.SweepAllModules(this.context);
// Release attachment to the child process console.
WinCon.FreeConsole();
this.UIRoot = null;
if (this.job != IntPtr.Zero)
{
WinBase.TerminateJobObject(this.job, 0);
}
this.job = IntPtr.Zero;
}
}
}

View File

@ -0,0 +1,46 @@
//----------------------------------------------------------------------------------------------------------------------
// <copyright file="ExperimentalTabTests.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// </copyright>
//----------------------------------------------------------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.IO;
using WEX.Logging.Interop;
using WEX.TestExecution.Markup;
namespace Host.Tests.UIA
{
[TestClass]
class Init
{
static Process appDriver;
[AssemblyInitialize]
public static void SetupAll(TestContext context)
{
Log.Comment("Searching for WinAppDriver in the same directory where this test was launched from...");
string winAppDriver = Path.Combine(context.TestDeploymentDir, "WinAppDriver.exe");
Log.Comment($"Attempting to launch WinAppDriver at: {winAppDriver}");
Log.Comment($"Working directory: {Environment.CurrentDirectory}");
appDriver = Process.Start(winAppDriver);
}
[AssemblyCleanup]
public static void CleanupAll()
{
try
{
appDriver.Kill();
}
catch
{
}
}
}
}

View File

@ -0,0 +1,179 @@
//----------------------------------------------------------------------------------------------------------------------
// <copyright file="SmokeTests.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// </copyright>
// <summary>UI Automation tests for verifying the basic opening/closing of the Terminal.</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace WindowsTerminal.UIA.Tests
{
using OpenQA.Selenium;
using WEX.TestExecution.Markup;
using WindowsTerminal.UIA.Tests.Common;
using WindowsTerminal.UIA.Tests.Elements;
[TestClass]
public class SmokeTests
{
public TestContext TestContext { get; set; }
[TestMethod]
[TestProperty("IsPGO", "true")]
public void StartTerminal()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
root.SendKeys("Hello smoke test!");
Globals.WaitForLongTimeout();
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunBigTextPowershell()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
var contentPath = app.GetFullTestContentPath("big.txt");
root.SendKeys($"cat \"{contentPath}\"");
root.SendKeys(Keys.Enter);
System.Threading.Thread.Sleep(25000);
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunBigTextPowershellBulk ()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
var contentPath = app.GetFullTestContentPath("big.txt");
root.SendKeys($"Get-Content -ReadCount 0 \"{contentPath}\" | Out-Default");
root.SendKeys(Keys.Enter);
System.Threading.Thread.Sleep(25000);
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunBigTextCmd()
{
using (TerminalApp app = new TerminalApp(TestContext, "cmd.exe"))
{
var root = app.GetRoot();
var contentPath = app.GetFullTestContentPath("big.txt");
root.SendKeys($"type \"{contentPath}\"");
root.SendKeys(Keys.Enter);
System.Threading.Thread.Sleep(25000);
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunCmatrixCmd()
{
using (TerminalApp app = new TerminalApp(TestContext, "cmd.exe"))
{
var root = app.GetRoot();
root.SendKeys("chcp 65001" + Keys.Enter); // This output needs UTF-8
var contentPath = app.GetFullTestContentPath("cmatrix.txt");
root.SendKeys($"type \"{contentPath}\"");
root.SendKeys(Keys.Enter);
System.Threading.Thread.Sleep(10000);
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunCacafireCmd()
{
using (TerminalApp app = new TerminalApp(TestContext, "cmd.exe"))
{
var root = app.GetRoot();
root.SendKeys("chcp 65001" + Keys.Enter); // This output needs UTF-8
var contentPath = app.GetFullTestContentPath("cacafire.txt");
root.SendKeys($"type \"{contentPath}\"");
root.SendKeys(Keys.Enter);
System.Threading.Thread.Sleep(25000);
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunChafaCmd()
{
using (TerminalApp app = new TerminalApp(TestContext, "cmd.exe"))
{
var root = app.GetRoot();
root.SendKeys("chcp 65001" + Keys.Enter); // This output needs UTF-8
var contentPath = app.GetFullTestContentPath("chafa.txt");
root.SendKeys($"type \"{contentPath}\"");
root.SendKeys(Keys.Enter);
System.Threading.Thread.Sleep(10000);
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunMakeKillPanes()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "+");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "+");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "-");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "-");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
Globals.WaitForLongTimeout();
}
}
[TestMethod]
[TestProperty("IsPGO", "true")]
public void RunMakeKillTabs()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
Globals.WaitForTimeout();
Globals.WaitForLongTimeout();
}
}
}
}

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{F19DACD5-0C6E-40DC-B6E4-767A3200542C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WindowsTerminal.UIA.Tests</RootNamespace>
<AssemblyName>WindowsTerminal.UIA.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<OutputPath>$(SolutionDir)\bin\$(Platform)\$(Configuration)</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)' == 'ARM64'">
<PlatformTarget>ARM64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)' == 'x64'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)' == 'Win32'">
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants Condition="'$(PGOBuildMode)' == 'Instrument'">$(DefineConstants);PGO_INSTRUMENT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="appium-dotnet-driver, Version=3.0.0.2, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Appium.WebDriver.3.0.0.2\lib\net45\appium-dotnet-driver.dll</HintPath>
</Reference>
<Reference Include="Castle.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Castle.Core.4.1.1\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="TE.Managed, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Taef.10.58.210305002\lib\net45\TE.Managed.dll</HintPath>
</Reference>
<Reference Include="TE.Model.Managed, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Taef.10.58.210305002\lib\net45\TE.Model.Managed.dll</HintPath>
</Reference>
<Reference Include="UIAutomationClient" />
<Reference Include="UIAutomationTypes" />
<Reference Include="WebDriver, Version=3.5.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Selenium.WebDriver.3.5.0\lib\net40\WebDriver.dll</HintPath>
</Reference>
<Reference Include="WebDriver.Support, Version=3.5.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Selenium.Support.3.5.0\lib\net40\WebDriver.Support.dll</HintPath>
</Reference>
<Reference Include="Wex.Common.Managed, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Taef.10.58.210305002\lib\net45\Wex.Common.Managed.dll</HintPath>
</Reference>
<Reference Include="Wex.Logger.Interop, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Taef.10.58.210305002\lib\net45\Wex.Logger.Interop.dll</HintPath>
</Reference>
<Reference Include="WindowsBase" />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Common\Globals.cs" />
<Compile Include="Common\NativeMethods.cs" />
<Compile Include="Common\PgoManager.cs" />
<Compile Include="Elements\TerminalApp.cs" />
<Compile Include="Init.cs" />
<Compile Include="SmokeTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy $(SolutionDir)\dep\WinAppDriver\* $(OutDir)\</PostBuildEvent>
</PropertyGroup>
<Import Project="..\..\..\packages\Microsoft.Taef.10.58.210305002\build\Microsoft.Taef.targets" Condition="Exists('..\..\..\packages\Microsoft.Taef.10.58.210305002\build\Microsoft.Taef.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.Taef.10.58.210305002\build\Microsoft.Taef.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Taef.10.58.210305002\build\Microsoft.Taef.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
<package id="Castle.Core" version="4.1.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
<package id="Selenium.Support" version="3.5.0" targetFramework="net45" />
<package id="Selenium.WebDriver" version="3.5.0" targetFramework="net45" />
<package id="Microsoft.Taef" version="10.58.210305002" targetFramework="net45" />
</packages>

View File

@ -33,6 +33,10 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<!-- PGO -->
<Import Condition="'$(PgoTarget)' == 'true' And '$(PGOBuildMode)' == 'Optimize'" Project="$(SolutionDir)packages\$(PGODatabaseId).$(PGODatabaseVersion)\build\PGO.targets" />
<Import Condition="'$(PgoTarget)' == 'true' And '$(PGOBuildMode)' == 'Instrument'" Project="$(SolutionDir)\src\common.pgo.runtime.props" />
<!-- Exclude our dependencies from static analysis. CAExcludePath can only be
set after we've imported Microsoft.Cpp.targets -->
<PropertyGroup>

View File

@ -156,6 +156,9 @@
</Link>
</ItemDefinitionGroup>
<Import Condition="'$(PgoTarget)' == 'true' And '$(Configuration)' == 'Release'" Project="$(SolutionDir)\src\common.pgo.compile.props" />
<Import Condition="'$(PgoTarget)' == 'true' And '$(PGOBuildMode)' == 'Optimize'" Project="$(SolutionDir)\tools\PGODatabase\PGO.version.props" />
<!-- For Win32 (x86) ONLY ... we use all defaults for AMD64. No def for those. -->
<ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
<ClCompile>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- From our friends at MUX: https://github.com/microsoft/microsoft-ui-xaml/blob/main/PGO.compile.props -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(PGOBuildMode)' == 'Instrument'">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<!-- Enable PGO optimization only for Release builds. -->
<ItemDefinitionGroup Condition="'$(PGOBuildMode)' == 'Instrument'">
<Link>
<AdditionalOptions>/GENPROFILE %(AdditionalOptions)</AdditionalOptions>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<AdditionalDependencies Condition="'$(Platform)'=='ARM'">$(VC_ReferencesPath_VC_ARM)\pgort.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='ARM64'">$(VC_ReferencesPath_VC_ARM64)\pgort.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='Win32'">$(VC_ReferencesPath_VC_x86)\pgort.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='x64'">$(VC_ReferencesPath_VC_x64)\pgort.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- From our friends at MUX: https://github.com/microsoft/microsoft-ui-xaml/blob/main/PGO.runtime.props -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PlatformShortName Condition="'$(PlatformShortName)'==''">$(Platform)</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='Win32'">x86</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='AnyCPU' and '$(Prefer32Bit)'==''">x64</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='AnyCPU' and '$(Prefer32Bit)'=='true'">x64</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='AnyCPU' and '$(Prefer32Bit)'=='false'">x64</PlatformShortName>
<HostFolderName Condition="'$(VCToolArchitecture)'=='Native32Bit'">Hostx86</HostFolderName>
<HostFolderName Condition="'$(VCToolArchitecture)'=='Native64Bit'">Hostx64</HostFolderName>
<PGOFilesDir>$(VCToolsInstallDir)bin\$(HostFolderName)\$(PlatformShortName)</PGOFilesDir>
<PGOFilesDir Condition="'$(PlatformShortName)'=='ARM' or '$(PlatformShortName)'=='ARM64'">$(VCToolsInstallDir)bin\$(PlatformShortName)</PGOFilesDir>
</PropertyGroup>
<ItemGroup>
<None Include="$(PGOFilesDir)\pgort140.dll">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(PGOFilesDir)\pgosweep.exe">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(PGOFilesDir)\vcruntime140.dll" Condition="Exists('$(PGOFilesDir)\vcruntime140.dll')">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -7,7 +7,9 @@
<ProjectName>Host.EXE</ProjectName>
<TargetName>OpenConsole</TargetName>
<ConfigurationType>Application</ConfigurationType>
<PgoTarget>true</PgoTarget>
</PropertyGroup>
<Import Project="$(SolutionDir)common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClInclude Include="..\precomp.h" />
@ -90,5 +92,5 @@
</Link>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="..\..\common.build.post.props" />
</Project>
<Import Project="$(SolutionDir)src\common.build.post.props" />
</Project>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>$id</id>
<version>$version</version>
<title>PGO Database</title>
<authors>Microsoft</authors>
<owners>Microsoft</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>PGO Database</description>
<releaseNotes>Initial release</releaseNotes>
<copyright>Copyright 2021</copyright>
</metadata>
<files>
<file target="build\PGO.targets" src="build\PGO.targets"/>
<file target="tools\x64\" src="tools\x64\*.pgd"/>
</files>
</package>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PlatformShortName Condition="'$(PlatformShortName)'==''">$(Platform)</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='Win32'">x86</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='AnyCPU' and '$(Prefer32Bit)'==''">x64</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='AnyCPU' and '$(Prefer32Bit)'=='true'">x64</PlatformShortName>
<PlatformShortName Condition="'$(PlatformShortName)'=='AnyCPU' and '$(Prefer32Bit)'=='false'">x64</PlatformShortName>
<ProfileGuidedDatabaseFileName>$(MSBuildThisFileDirectory)..\tools\$(PlatformShortName)\$(TargetName).pgd</ProfileGuidedDatabaseFileName>
</PropertyGroup>
<!-- Enable PGO optimization only for if the pgds file exists. -->
<ItemDefinitionGroup Condition="'$(Configuration)' == 'Release' and '$(PGOBuildMode)' == 'Optimize' and Exists('$(ProfileGuidedDatabaseFileName)')">
<Link>
<LinkTimeCodeGeneration>PGUpdate</LinkTimeCodeGeneration>
<ProfileGuidedDatabase>$(ProfileGuidedDatabaseFileName)</ProfileGuidedDatabase>
<AdditionalOptions>/force:pgorepro %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<PGODatabaseId>$id</PGODatabaseId>
<PGODatabaseVersion>$version</PGODatabaseVersion>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,7 @@
$pgoBranch = "main"
$packageId = "Microsoft.Internal.Windows.Terminal.PGODatabase"
# Get release version
[xml] $customProps = ( Get-Content "..\..\custom.props" )
$releaseVersionMajor = ( [int]::Parse( $customProps.GetElementsByTagName("VersionMajor").'#text' ) )
$releaseVersionMinor = ( [int]::Parse( $customProps.GetElementsByTagName("VersionMinor").'#text' ) )

View File

@ -0,0 +1,11 @@
Param(
[Parameter(Mandatory = $true, Position = 1)] [string] $templatePath,
[Parameter(Mandatory = $true, Position = 2)] [string] $outputPath)
. .\version.ps1
. .\template.ps1
. .\config.ps1
$version = FormatVersion ( MakeVersion $releaseVersionMajor $releaseVersionMinor ( GetDatetimeStamp $pgoBranch ) )
Write-Host ( "PGO INSTRUMENT: generating {0} version {1}" -f $packageId, $version )
FillOut-Template $templatePath $outputPath @{ "version" = $version; "id" = $packageId }

View File

@ -0,0 +1,49 @@
# Profile Guided Optimization
**NOTE: All PGO work builds on work from Microsoft/Microsoft-UI-XAML**
## Description
We generate PGO database NuGet package which is versioned based on product release version and branch name/time stamp of the code that was used for instrumentation and training. In CI/release builds an initialization step enumerates all available versions, filters out those for other releases and branches. Given a list of applicable versions, it will find the one that is closest (BEFORE) the time-stamp of the last commit or a fork-point from instrumented branch. That package version will be installed and version references will be updated. The PGO branch is determined by variable $pgoBranch in tools/PGODatabase/config.ps1. It will need to be updated if a forked branch should be PGO'd.
## Scenarios
For the purpose of illustration, lets assume the following is a chronological list of check-ins to two branches (main and release/2.4). Some of them have had instrumentation/training run done on them and have generated PGO NuGets (version numbers in parentheses). To simplify, lets assume that release major and minor versions are the same for all check-in as they merely act as filters for what versions are considered to be available.
1b27fd5f -- main --
7b303f74 -- main --
930ff585 -- main -- 2.4.2001312227-main
63948a75 -- main --
0d379b51 -- main --
f23f1fad -- main -- 2.4.2001312205-main
bcf9adaa -- main --
6ef44a23 -- main --
310bc133 -- release/2.4 --
80a4ab55 -- release/2.4 -- 2.4.2001312054-release_2_4
18b956f6 -- release/2.4 --
4abd4d54 -- main -- 2.4.2001312033-main
d150eae0 -- main -- 2.4.2001312028-main
### Optimizing on PGOd branch
If we are building on main (which in this example is PGOd), the version picked will be the one that has the same major and minor versions AND branch name and is the same or is right before the SHA being built.
E.g.
1b27fd5f -- 2.4.2001312227-main
f23f1fad -- 2.4.2001312205-main
bcf9adaa -- 2.4.2001312033-main
### Optimizing release branch
A branch which will be PGOd requires a slightly different handling. Lets say release/2.4 forked from main on commit 4abd4d54. Initially, it will be configured to track main and 18b956f6 will be optimized with 2.4.2001312033-main. When the configuration is changed to start tracking release/2.4 (change branch name $pgoBranch in tools/PGODatabase/config.ps1 script), it will start tracking its own branch.
E.g.
18b956f6 -- if tracking main -> 2.4.2001312033-main,
if tracking release/2.4 -> ERROR (no database exists)
310bc133 -- 2.4.2001312054-release_2_4
### Optimizing topic branch
Assuming topic branch will not have a training run done, it can still use database from branch it was forked from. Lets say we have a branch which was forked from main on 4abd4d54. If we dont change which branch its tracking, it will keep using 2.4.2001312033-main. Merging main on f23f1fad into topic branch, will change used database to 2.4.2001312205-main.

View File

@ -0,0 +1,47 @@
. .\version.ps1
. .\template.ps1
. .\config.ps1
$feedUri = "https://pkgs.dev.azure.com/ms/terminal/_packaging/TerminalDependencies/nuget/v2"
$currentVersion = MakeVersion $releaseVersionMajor $releaseVersionMinor ( GetDatetimeStamp $pgoBranch )
Write-Host ( "PGO OPTIMIZE: requesting {0} version {1}" -f $packageId, ( FormatVersion $currentVersion ) )
$packageSource = Register-PackageSource -ForceBootstrap -Name TerminalDependencies -Location $feedUri -ProviderName NuGet -Trusted
$packages = ( Find-Package $packageId -Source TerminalDependencies -AllowPrereleaseVersions -AllVersions ) | Sort-Object -Property Version -Descending
$best = $null
foreach ( $existing in $packages )
{
$existingVersion = MakeVersionFromString $existing.Version
if ( ( CompareBranches $existingVersion $currentVersion ) -eq $False -or
( CompareReleases $existingVersion $currentVersion ) -ne 0 )
{
# If this is different release or branch, then skip it.
continue
}
if ( ( CompareRevisions $existingVersion $currentVersion ) -le 0 )
{
# Version are sorted in descending order, the first one less than or equal to the current is the one we want.
# NOTE: at this point the only difference between versions will be revision (date-time stamp)
# which is formatted as a fixed-length string, so string comparison WILL sort it correctly.
$best = $existing
break
}
}
if ( $best -eq $null )
{
throw "Appropriate database cannot be found"
}
Write-Host ( "PGO OPTIMIZE: picked {0} version {1}" -f $packageId, $best.Version )
$best | Install-Package -Destination ..\..\packages -Force
$packageSource | Unregister-PackageSource
FillOut-Template "PGO.version.props.template" "PGO.version.props" @{ "version" = $best.Version; "id" = $packageId }

View File

@ -0,0 +1,16 @@
function Replace-Many ( $string, $dictionary )
{
foreach ( $key in $dictionary.Keys )
{
$field = '$' + $key.ToString()
$string = $string.Replace( $field, $dictionary[$key].ToString() )
}
return $string
}
function FillOut-Template ( $inputPath, $outputPath, $dictionary )
{
$replaced = Replace-Many ( Get-Content $inputPath ) $dictionary
Write-Output $replaced | Set-Content $outputPath -Force | Out-Null
}

View File

@ -0,0 +1,45 @@
Param(
[Parameter(Mandatory = $true, Position = 1)]
[string] $module)
if ( !( Test-Path $module ) )
{
throw "File does not exist $module"
}
$vsPath = &(Join-Path ${env:ProgramFiles(x86)} "\Microsoft Visual Studio\Installer\vswhere.exe") -property installationpath
Import-Module (Get-ChildItem $vsPath -Recurse -File -Filter Microsoft.VisualStudio.DevShell.dll).FullName
Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation
$output = ( & link.exe /dump /headers /coffgroup $module )
$regex1 = [regex] '^\s*[A-F0-9]+ coffgrp(\s+[A-F0-9]+){4} \(PGU\)$'
$regex2 = [regex] '^\s*([A-F0-9]+\s+){2}\.text\$zz$'
$matchFlags = 0
foreach ( $line in $output )
{
if ( $line -match $regex1 )
{
$matchFlags = $matchFlags -bor 1
}
if ( $line -match $regex2 )
{
$matchFlags = $matchFlags -bor 2
}
}
$optimized = $matchFlags -eq 3
$message = "$module is $( if ( $optimized ) { "optimized" } else { "not optimized" } )"
if ( -not $optimized )
{
throw $message
}
else
{
write-host $message
}

View File

@ -0,0 +1,75 @@
function MakeVersion ( $major, $minor, $datetimeStamp )
{
$revision, $branch = $datetimeStamp.Split("-")
if ( $branch -eq $null )
{
$branch = ""
}
return [PSCustomObject] @{
Major = $major
Minor = $minor
Revision = $revision
Branch = $branch
}
}
function MakeVersionFromString ( $str )
{
$parts = $str.Split(".")
return MakeVersion ( [int]::Parse($parts[0]) ) ( [int]::Parse($parts[1]) ) $parts[2]
}
function FormatVersion ( $version )
{
$branch = ""
if ( $version.Branch -ne "" )
{
$branch = "-{0}" -f $version.Branch
}
return "{0}.{1}.{2}{3}" -f $version.Major, $version.Minor, $version.Revision, $branch
}
function CompareReleases ( $version1, $version2 )
{
$cmpMajor = [Math]::Sign($version1.Major - $version2.Major)
if ( $cmpMajor -ne 0 )
{
return $cmpMajor
}
return [Math]::Sign($version1.Minor - $version2.Minor)
}
function CompareRevisions ( $version1, $version2 )
{
return [Math]::Sign($version1.Revision - $version2.Revision)
}
function CompareBranches ( $version1, $version2 )
{
return $version1.Branch -eq $version2.Branch
}
function GetDatetimeStamp ( $pgoBranch )
{
$forkSHA = $( git merge-base origin/$pgoBranch HEAD )
if ( $LastExitCode -ne 0 )
{
throw "FAILED: git merge-base"
}
$forkDate = ( Get-Date -Date $( git log -1 $forkSHA --date=iso --pretty=format:"%ad" ) ).ToUniversalTime().ToString("yyMMddHHmm")
if ( $LastExitCode -ne 0 )
{
throw "FAILED: Get forkDate"
}
return $forkDate + "-" + $pgoBranch.Replace("/", "_").Replace("-", "_").Replace(".", "_")
}