Skip to content

Commit 9a1ce58

Browse files
committed
Fixed test run hang if an error occurs when trying to send test results to the server (#17)
Pretty print requests and responses for easier troubleshooting Added integration tests disable warning NETSDK1138: The target framework 'netcoreapp2.1' is out of support and will not receive security updates in the future.
1 parent b6e77d4 commit 9a1ce58

19 files changed

Lines changed: 601 additions & 95 deletions

.github/workflows/build.yml

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
runs-on: windows-latest
2020

2121
env:
22-
VERSION: 1.2.2
22+
VERSION: 1.2.3
2323

2424
strategy:
2525
matrix:
@@ -44,10 +44,10 @@ jobs:
4444
echo SEM_VERSION=${{ env.SEM_VERSION }}
4545
4646
- name: Checkout
47-
uses: actions/checkout@v2
47+
uses: actions/checkout@v4.1.0
4848

4949
- name: Setup .NET Core 2.1
50-
uses: actions/setup-dotnet@v1
50+
uses: actions/setup-dotnet@v3.2.0
5151
with:
5252
dotnet-version: 2.1
5353

@@ -63,23 +63,19 @@ jobs:
6363
copy ".\src\AzurePipelines.TestLogger\bin\${{ matrix.Configuration }}\netstandard1.5\*.dll" .\src\AzurePipelines.TestLogger\contentFiles\any\any
6464
6565
- name: Pack
66-
run: dotnet pack .\src\AzurePipelines.TestLogger\AzurePipelines.TestLogger.csproj --configuration ${{ matrix.Configuration }} -p:NuspecProperties="Version=${{ env.SEM_VERSION }}" --no-restore --no-build --output:.\build -p:NuspecFile=AzurePipelines.TestLogger.nuspec
66+
run: dotnet pack .\src\AzurePipelines.TestLogger\AzurePipelines.TestLogger.csproj --configuration ${{ matrix.Configuration }} -p:NuspecProperties="Version=${{ env.SEM_VERSION }}" --no-restore --no-build --output:.\build -p:NuspecFile=..\AzurePipelines.TestLogger\AzurePipelines.TestLogger.nuspec
6767

6868
- name: Test
6969
run: dotnet test --no-build --no-restore --verbosity normal --configuration ${{ matrix.Configuration }}
7070

71-
- name: Create zip
72-
run: |
73-
mkdir AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
74-
copy ".\src\AzurePipelines.TestLogger\bin\${{ matrix.Configuration }}\netstandard1.5\*.*" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
75-
copy "~\.nuget\packages\semver\2.0.6\lib\netstandard1.1\*.*" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
76-
copy ".\LICENSE" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
77-
copy ".\README.md" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
78-
copy ".\ReleaseNotes.md" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
79-
tar -cf AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
71+
- name: Create Zip
72+
uses: vimtor/action-zip@v1.1
73+
with:
74+
files: LICENSE README.md ReleaseNotes.md .\src\AzurePipelines.TestLogger\bin\${{ matrix.Configuration }}\netstandard1.5
75+
dest: AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip
8076

8177
- name: Upload Build Artifact
82-
uses: actions/upload-artifact@v2.3.1
78+
uses: actions/upload-artifact@v3.1.3
8379
with:
8480
name: AzurePipelines.TestLogger ${{ env.SEM_VERSION }} ${{ matrix.Configuration }}
8581
path: AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip
@@ -95,15 +91,15 @@ jobs:
9591
fail_on_unmatched_files: true
9692
files: |
9793
AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip
98-
.\src\AzurePipelines.TestLogger\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
94+
./build/AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
9995
10096
- name: Upload NuGet Package
101-
uses: actions/upload-artifact@v2.3.1
97+
uses: actions/upload-artifact@v3.1.3
10298
with:
10399
name: AzurePipelines.TestLogger ${{ env.SEM_VERSION }} ${{ matrix.Configuration }}.nupkg
104-
path: .\src\AzurePipelines.TestLogger\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
100+
path: .\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
105101
if-no-files-found: error
106102

107103
- name: Publish NuGet Package
108104
if: ${{ matrix.Configuration == 'Release' && github.event_name != 'pull_request' }}
109-
run: dotnet nuget push .\src\AzurePipelines.TestLogger\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source https://api.nuget.org/v3/index.json
105+
run: dotnet nuget push .\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source https://api.nuget.org/v3/index.json

AzurePipelines.TestLogger.sln

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Microsoft Visual Studio Solution File, Format Version 12.00
2-
# Visual Studio Version 16
3-
VisualStudioVersion = 16.0.31112.23
2+
# Visual Studio Version 17
3+
VisualStudioVersion = 17.7.34031.279
44
MinimumVisualStudioVersion = 10.0.40219.1
55
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C95ECC05-F3E8-49F4-B7C5-A29CD7EACFC1}"
66
EndProject
@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{B8315F74-E
2626
stylecop.json = stylecop.json
2727
EndProjectSection
2828
EndProject
29+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleUnitTestProject", "tests\SampleUnitTestProject\SampleUnitTestProject.csproj", "{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}"
30+
EndProject
2931
Global
3032
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3133
Debug|Any CPU = Debug|Any CPU
@@ -60,13 +62,26 @@ Global
6062
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8}.Release|x64.Build.0 = Release|Any CPU
6163
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8}.Release|x86.ActiveCfg = Release|Any CPU
6264
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8}.Release|x86.Build.0 = Release|Any CPU
65+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
67+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x64.ActiveCfg = Debug|Any CPU
68+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x64.Build.0 = Debug|Any CPU
69+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x86.ActiveCfg = Debug|Any CPU
70+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x86.Build.0 = Debug|Any CPU
71+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
72+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|Any CPU.Build.0 = Release|Any CPU
73+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x64.ActiveCfg = Release|Any CPU
74+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x64.Build.0 = Release|Any CPU
75+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x86.ActiveCfg = Release|Any CPU
76+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x86.Build.0 = Release|Any CPU
6377
EndGlobalSection
6478
GlobalSection(SolutionProperties) = preSolution
6579
HideSolutionNode = FALSE
6680
EndGlobalSection
6781
GlobalSection(NestedProjects) = preSolution
6882
{77CA5040-B4A0-4D0B-ADDD-09853A385007} = {C95ECC05-F3E8-49F4-B7C5-A29CD7EACFC1}
6983
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8} = {FA92AD98-1291-4A90-A3AA-ED81A9BBC86E}
84+
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D} = {FA92AD98-1291-4A90-A3AA-ED81A9BBC86E}
7085
EndGlobalSection
7186
GlobalSection(ExtensibilityGlobals) = postSolution
7287
SolutionGuid = {A7517899-6171-4E6B-BDD7-DBE01B34E83A}

root

Whitespace-only changes.

src/AzurePipelines.TestLogger/ApiClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ internal virtual async Task<string> SendAsync(HttpMethod method, string endpoint
248248

249249
if (Verbose)
250250
{
251-
Console.WriteLine($"Request:\n{method} {requestUri}\n{body}\n\nResponse:\n{response.StatusCode}\n{responseBody}");
251+
Console.WriteLine($"Request:\n{method} {requestUri}\n{body.Indented()}\n\nResponse:\n{response.StatusCode}\n{responseBody.Indented()}\n");
252252
}
253253

254254
try

src/AzurePipelines.TestLogger/ApiClientV5.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Globalization;
43
using System.Linq;
54
using AzurePipelines.TestLogger.Json;
65
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
2-
<PropertyGroup>
3-
<TargetFramework>netstandard1.5</TargetFramework>
4-
<Version>1.0.0</Version>
5-
<AssemblyVersion>1.0.0.0</AssemblyVersion>
6-
<FileVersion>1.0.0.0</FileVersion>
7-
</PropertyGroup>
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>netstandard1.5</TargetFramework>
4+
<Version>1.0.0</Version>
5+
<AssemblyVersion>1.0.0.0</AssemblyVersion>
6+
<FileVersion>1.0.0.0</FileVersion>
7+
<NoPackageAnalysis>true</NoPackageAnalysis>
8+
</PropertyGroup>
9+
810
<ItemGroup>
9-
<Compile Remove="contentFiles\**" />
10-
<EmbeddedResource Remove="contentFiles\**" />
11-
<None Remove="contentFiles\**" />
12-
</ItemGroup>
13-
14-
<ItemGroup>
15-
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="15.0.0" />
16-
<PackageReference Include="Semver" Version="2.0.6" />
17-
</ItemGroup>
18-
19-
<ItemGroup>
20-
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
21-
<_Parameter1>AzurePipelines.TestLogger.Tests</_Parameter1>
22-
</AssemblyAttribute>
23-
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
24-
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
25-
</AssemblyAttribute>
26-
</ItemGroup>
27-
</Project>
11+
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="15.0.0" />
12+
<PackageReference Include="Semver" Version="2.0.6" GeneratePathProperty="true" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
17+
<_Parameter1>AzurePipelines.TestLogger.Tests</_Parameter1>
18+
</AssemblyAttribute>
19+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
20+
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
21+
</AssemblyAttribute>
22+
</ItemGroup>
23+
<Target Name="CopyFileFromNuGetPackage" AfterTargets="Build">
24+
<Copy SourceFiles="$(PkgSemver)\lib\netstandard1.1\Semver.dll" DestinationFolder="$(OutDir)" />
25+
</Target>
26+
27+
</Project>

src/AzurePipelines.TestLogger/AzurePipelinesTestLogger.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class AzurePipelinesTestLogger : ITestLoggerWithParameters
2828

2929
public AzurePipelinesTestLogger()
3030
{
31+
// For debugging purposes
32+
// System.Diagnostics.Debugger.Launch();
3133
_environmentVariableProvider = new EnvironmentVariableProvider();
3234
_apiClientFactory = new ApiClientFactory();
3335
}
@@ -137,10 +139,28 @@ private void TestMessageHandler(object sender, TestRunMessageEventArgs e)
137139
// Add code to handle message
138140
}
139141

140-
private void TestResultHandler(object sender, TestResultEventArgs e) =>
141-
_queue.Enqueue(new VstpTestResult(e.Result));
142+
private void TestResultHandler(object sender, TestResultEventArgs e)
143+
{
144+
try
145+
{
146+
_queue.Enqueue(new VstpTestResult(e.Result));
147+
}
148+
catch (Exception ex)
149+
{
150+
Console.WriteLine(ex.ToString());
151+
}
152+
}
142153

143-
private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) =>
144-
_queue.Flush(new VstpTestRunComplete(e.IsAborted || e.IsCanceled, e.AttachmentSets));
154+
private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e)
155+
{
156+
try
157+
{
158+
_queue.Flush(new VstpTestRunComplete(e.IsAborted || e.IsCanceled, e.AttachmentSets));
159+
}
160+
catch (Exception ex)
161+
{
162+
Console.WriteLine(ex.ToString());
163+
}
164+
}
145165
}
146166
}

src/AzurePipelines.TestLogger/Json/JsonExtensions.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,46 @@ private static string JsonEscape(string value)
8383

8484
return sb.ToString();
8585
}
86+
87+
/// <summary>
88+
/// Indent JSON string.
89+
/// </summary>
90+
/// <param name="input">The JSON string.</param>
91+
/// <returns>The indented JSON string.</returns>
92+
public static string Indented(this string input)
93+
{
94+
int level = 0;
95+
StringBuilder result = new StringBuilder();
96+
97+
for (int i = 0; i < input.Length; i++)
98+
{
99+
char c = input[i];
100+
101+
if (c == '{' || c == '[')
102+
{
103+
result.Append(c);
104+
result.AppendLine();
105+
result.Append(new string(' ', ++level * 2));
106+
}
107+
else if (c == '}' || c == ']')
108+
{
109+
result.AppendLine();
110+
result.Append(new string(' ', --level * 2));
111+
result.Append(c);
112+
}
113+
else if (c == ',')
114+
{
115+
result.Append(c);
116+
result.AppendLine();
117+
result.Append(new string(' ', level * 2));
118+
}
119+
else
120+
{
121+
result.Append(c);
122+
}
123+
}
124+
125+
return result.ToString();
126+
}
86127
}
87128
}

src/AzurePipelines.TestLogger/LoggerQueue.cs

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -43,72 +43,65 @@ public void Flush(VstpTestRunComplete testRunComplete)
4343
// Cancel any idle consumers and let them return
4444
_queue.Cancel();
4545

46-
try
47-
{
48-
// Any active consumer will circle back around and batch post the remaining queue
49-
_consumeTask.Wait(TimeSpan.FromSeconds(60));
46+
// Any active consumer will circle back around and batch post the remaining queue
47+
_consumeTask.Wait(TimeSpan.FromSeconds(60));
5048

51-
// Update the run and parents to a completed state
52-
SendTestsCompleted(testRunComplete, _consumeTaskCancellationSource.Token).Wait(TimeSpan.FromSeconds(60));
49+
// Update the run and parents to a completed state
50+
SendTestsCompleted(testRunComplete, _consumeTaskCancellationSource.Token).Wait(TimeSpan.FromSeconds(60));
5351

54-
// Cancel any active HTTP requests if still hasn't finished flushing
55-
_consumeTaskCancellationSource.Cancel();
56-
if (!_consumeTask.Wait(TimeSpan.FromSeconds(10)))
57-
{
58-
throw new TimeoutException("Cancellation didn't happen quickly");
59-
}
60-
}
61-
catch (Exception ex)
52+
// Cancel any active HTTP requests if still hasn't finished flushing
53+
_consumeTaskCancellationSource.Cancel();
54+
if (!_consumeTask.Wait(TimeSpan.FromSeconds(10)))
6255
{
63-
Console.WriteLine(ex);
56+
throw new TimeoutException("Cancellation didn't happen quickly");
6457
}
6558
}
6659

6760
private async Task ConsumeItemsAsync(CancellationToken cancellationToken)
6861
{
6962
while (true)
7063
{
71-
ITestResult[] nextItems = await _queue.TakeAsync().ConfigureAwait(false);
72-
73-
if (nextItems == null || nextItems.Length == 0)
64+
try
7465
{
75-
// Queue is canceling and is empty
76-
return;
77-
}
66+
ITestResult[] nextItems = await _queue.TakeAsync().ConfigureAwait(false);
67+
68+
if (nextItems == null || nextItems.Length == 0)
69+
{
70+
// Queue is canceling and is empty
71+
return;
72+
}
7873

79-
await SendResultsAsync(nextItems, cancellationToken).ConfigureAwait(false);
74+
await SendResultsAsync(nextItems, cancellationToken).ConfigureAwait(false);
8075

81-
if (cancellationToken.IsCancellationRequested)
76+
if (cancellationToken.IsCancellationRequested)
77+
{
78+
return;
79+
}
80+
}
81+
catch (Exception ex)
8282
{
83-
return;
83+
Console.WriteLine(ex);
8484
}
8585
}
8686
}
8787

8888
private async Task SendResultsAsync(ITestResult[] testResults, CancellationToken cancellationToken)
8989
{
90-
try
90+
// Create a test run if we need it
91+
if (RunId == 0)
9192
{
92-
// Create a test run if we need it
93-
if (RunId == 0)
94-
{
95-
Source = GetSource(testResults);
96-
RunId = await CreateTestRun(cancellationToken).ConfigureAwait(false);
97-
}
93+
Source = GetSource(testResults);
94+
RunId = await CreateTestRun(cancellationToken).ConfigureAwait(false);
95+
}
9896

99-
// Group results by their parent
100-
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent = GroupTestResultsByParent(testResults);
97+
// Group results by their parent
98+
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent = GroupTestResultsByParent(testResults);
10199

102-
// Create any required parent nodes
103-
await CreateParents(testResultsByParent, cancellationToken).ConfigureAwait(false);
100+
// Create any required parent nodes
101+
await CreateParents(testResultsByParent, cancellationToken).ConfigureAwait(false);
104102

105-
// Update parents with the test results
106-
await SendTestResults(testResultsByParent, cancellationToken).ConfigureAwait(false);
107-
}
108-
catch (Exception)
109-
{
110-
// Eat any communications exceptions
111-
}
103+
// Update parents with the test results
104+
await SendTestResults(testResultsByParent, cancellationToken).ConfigureAwait(false);
112105
}
113106

114107
// Internal for testing

0 commit comments

Comments
 (0)