Skip to content

Commit 279496f

Browse files
authored
Take screenshots on build agents during tests (dotnet#3056)
1 parent 292234a commit 279496f

File tree

3 files changed

+203
-0
lines changed

3 files changed

+203
-0
lines changed

azure-pipelines.yml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pr:
2828
- master
2929
- release/*
3030
- internal/release/*
31+
- internal/experimental/*
3132

3233
# Call the ci.yml template, which does the real work
3334
stages:

eng/Screenshots.win.psm1

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<#
2+
Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.
3+
See the LICENSE file in the project root for more information.
4+
#>
5+
6+
# Collection of powershell build utility functions that we use across our scripts.
7+
# Copied from https://raw.githubusercontent.com/dotnet/roslyn/3f851026b30b335ae328ce38a7817d93b96ad4ad/eng/build-utils-win.ps1
8+
9+
Set-StrictMode -version 2.0
10+
$ErrorActionPreference="Stop"
11+
$FLAG_FILE = "screenshots.lock"
12+
13+
Add-Type -AssemblyName 'System.Drawing'
14+
Add-Type -AssemblyName 'System.Windows.Forms'
15+
16+
. $PSScriptRoot\common\pipeline-logging-functions.ps1
17+
18+
function Save-Screenshot() {
19+
Param (
20+
[string] $TargetFileName,
21+
[string] $LogFile
22+
)
23+
24+
$width = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width
25+
$height = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height
26+
27+
$bitmap = New-Object System.Drawing.Bitmap $width, $height
28+
try {
29+
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
30+
try {
31+
$graphics.CopyFromScreen( `
32+
[System.Windows.Forms.Screen]::PrimaryScreen.Bounds.X, `
33+
[System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Y, `
34+
0, `
35+
0, `
36+
$bitmap.Size, `
37+
[System.Drawing.CopyPixelOperation]::SourceCopy)
38+
} finally {
39+
$graphics.Dispose()
40+
}
41+
42+
$bitmap.Save($TargetFileName, [System.Drawing.Imaging.ImageFormat]::Png)
43+
44+
$fullPath = Resolve-Path $TargetFileName
45+
Write-Host "##vso[task.uploadfile]$fullPath";
46+
"[SAVE] $fullPath" >> $LogFile;
47+
} finally {
48+
$bitmap.Dispose()
49+
}
50+
}
51+
52+
function Capture-Screenshot() {
53+
Param (
54+
[string] $TargetDir,
55+
[string] $LogFile
56+
)
57+
58+
$screenshotPath = (Join-Path $TargetDir "$(Get-Date -Format 'yyyyMMdd.HHmmss').png")
59+
try {
60+
Save-Screenshot -TargetFileName $screenshotPath -LogFile $LogFile
61+
}
62+
catch {
63+
Write-PipelineTaskError -Message "Screenshot failed; attempt to connect to the console?"
64+
"[CAPTURE] Screenshot failed; attempt to connect to the console?" >> $LogFile;
65+
}
66+
}
67+
68+
function Start-CaptureScreenshots() {
69+
<#
70+
.SYNOPSIS
71+
72+
Starts capturing screenshots.
73+
74+
.DESCRIPTION
75+
76+
Starts capturing screenshots by setting a lock file in the specified directory.
77+
If the target folder already contains a lock file, then it is assumed there is another
78+
process that is capturing screenshots, this is the end.
79+
80+
Screenshots are captured indefinitely every 3 minutes until the lock file
81+
in the target folder is deleted.
82+
83+
.PARAMETER TargetDir
84+
The full path to a folder that contains the lock file.
85+
86+
.INPUTS
87+
88+
None.
89+
90+
.OUTPUTS
91+
92+
None.
93+
94+
#>
95+
Param (
96+
[string] $TargetDir
97+
)
98+
99+
$flagFile = Join-Path $TargetDir $FLAG_FILE;
100+
$logFile = $flagFile.Replace('.lock', '.log');
101+
"[START] Flag: $flagFile" >> $logFile;
102+
103+
$hasFlagFile = Test-Path $flagFile
104+
if ($hasFlagFile) {
105+
Write-PipelineTaskError -Message "Screenshots are already being taken!" -Type 'warning'
106+
"[START] Screenshots are already being taken!" >> $logFile;
107+
return;
108+
}
109+
110+
'' | Out-File $flagFile
111+
112+
do
113+
{
114+
$hasFlagFile = Test-Path $flagFile
115+
if (!$hasFlagFile) {
116+
Write-PipelineTaskError -Message "Screenshots no longer being taken" -Type 'warning'
117+
"[START] Screenshots no longer being taken" >> $logFile;
118+
return;
119+
}
120+
121+
Capture-Screenshot -TargetDir $TargetDir -LogFile $logFile;
122+
"[START] Screenshot taken" >> $logFile;
123+
Start-Sleep -Seconds 180
124+
125+
$hasFlagFile = Test-Path $flagFile
126+
}
127+
until (!$hasFlagFile)
128+
}
129+
130+
function Stop-CaptureScreenShots()
131+
{
132+
<#
133+
.SYNOPSIS
134+
135+
Stop capturing screenshots by removing the lock file.
136+
137+
.DESCRIPTION
138+
139+
Stop capturing screenshots by removing the lock file.
140+
141+
.PARAMETER TargetDir
142+
The full path to a folder that contains the lock file.
143+
144+
.INPUTS
145+
146+
None.
147+
148+
.OUTPUTS
149+
150+
None.
151+
152+
#>
153+
Param (
154+
[string] $TargetDir
155+
)
156+
157+
$flagFile = Join-Path $TargetDir $FLAG_FILE;
158+
$logFile = $flagFile.Replace('.lock', '.log');
159+
"[STOP] Flag: $flagFile" >> $logFile;
160+
161+
$hasFlagFile = Test-Path $flagFile
162+
163+
if ($hasFlagFile) {
164+
Remove-Item -Path $flagFile -Force
165+
"[STOP] Flag file removed" >> $logFile;
166+
}
167+
168+
"[STOP] Stopped" >> $logFile;
169+
}
170+
171+
Export-ModuleMember -Function Start-CaptureScreenshots
172+
Export-ModuleMember -Function Stop-CaptureScreenShots

eng/common/build.ps1

+30
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ function Print-Usage() {
6969

7070
. $PSScriptRoot\tools.ps1
7171

72+
$moduleLocation = Resolve-Path "$PSScriptRoot\..\Screenshots.win.psm1"
73+
$initScreenshotsModule = [scriptblock]::Create("Import-Module $moduleLocation")
74+
7275
function InitializeCustomToolset {
7376
if (-not $restore) {
7477
return
@@ -142,12 +145,39 @@ try {
142145
InitializeNativeTools
143146
}
144147

148+
$TakeScreenshots = $ci -and ($test -or $integrationTest -or $performanceTest);
149+
$ImageLogs = '';
150+
if ($TakeScreenshots) {
151+
$ImageLogs = Join-Path $LogDir 'screenshots'
152+
Create-Directory $ImageLogs
153+
154+
[ScriptBlock] $ScreenshotCaptureScript = {
155+
param($ImageLogs)
156+
Start-CaptureScreenshots "$ImageLogs"
157+
};
158+
159+
$job = Start-Job -InitializationScript $initScreenshotsModule `
160+
-ScriptBlock $ScreenshotCaptureScript `
161+
-ArgumentList $ImageLogs
162+
}
163+
145164
Build
146165
}
147166
catch {
148167
Write-Host $_.ScriptStackTrace
149168
Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_
150169
ExitWithExitCode 1
151170
}
171+
finally {
172+
if ($TakeScreenshots) {
173+
[ScriptBlock] $ScreenshotCaptureScript = {
174+
param($ImageLogs)
175+
Stop-CaptureScreenshots -TargetDir $ImageLogs
176+
};
177+
Start-Job -InitializationScript $initScreenshotsModule `
178+
-ScriptBlock $ScreenshotCaptureScript `
179+
-ArgumentList $ImageLogs | Receive-Job -AutoRemoveJob -Wait
180+
}
181+
}
152182

153183
ExitWithExitCode 0

0 commit comments

Comments
 (0)