Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix test run hang if an error occurs when trying to send test results to the server #17

Merged
merged 1 commit into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: windows-latest

env:
VERSION: 1.2.2
VERSION: 1.2.3

strategy:
matrix:
Expand All @@ -44,10 +44,10 @@ jobs:
echo SEM_VERSION=${{ env.SEM_VERSION }}

- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4.1.0

- name: Setup .NET Core 2.1
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 2.1

Expand All @@ -63,23 +63,19 @@ jobs:
copy ".\src\AzurePipelines.TestLogger\bin\${{ matrix.Configuration }}\netstandard1.5\*.dll" .\src\AzurePipelines.TestLogger\contentFiles\any\any

- name: Pack
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
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

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

- name: Create zip
run: |
mkdir AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
copy ".\src\AzurePipelines.TestLogger\bin\${{ matrix.Configuration }}\netstandard1.5\*.*" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
copy "~\.nuget\packages\semver\2.0.6\lib\netstandard1.1\*.*" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
copy ".\LICENSE" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
copy ".\README.md" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
copy ".\ReleaseNotes.md" .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
tar -cf AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip .\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}
- name: Create Zip
uses: vimtor/[email protected]
with:
files: LICENSE README.md ReleaseNotes.md .\src\AzurePipelines.TestLogger\bin\${{ matrix.Configuration }}\netstandard1.5
dest: AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip

- name: Upload Build Artifact
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v3.1.3
with:
name: AzurePipelines.TestLogger ${{ env.SEM_VERSION }} ${{ matrix.Configuration }}
path: AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip
Expand All @@ -95,15 +91,15 @@ jobs:
fail_on_unmatched_files: true
files: |
AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.zip
.\src\AzurePipelines.TestLogger\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
.\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg

- name: Upload NuGet Package
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v3.1.3
with:
name: AzurePipelines.TestLogger ${{ env.SEM_VERSION }} ${{ matrix.Configuration }}.nupkg
path: .\src\AzurePipelines.TestLogger\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
path: .\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg
if-no-files-found: error

- name: Publish NuGet Package
if: ${{ matrix.Configuration == 'Release' && github.event_name != 'pull_request' }}
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
run: dotnet nuget push .\build\AzurePipelines.TestLogger.${{ env.SEM_VERSION }}.nupkg --api-key ${{ secrets.NUGET_KEY }} --source https://api.nuget.org/v3/index.json
19 changes: 17 additions & 2 deletions AzurePipelines.TestLogger.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31112.23
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C95ECC05-F3E8-49F4-B7C5-A29CD7EACFC1}"
EndProject
Expand All @@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{B8315F74-E
stylecop.json = stylecop.json
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleUnitTestProject", "tests\SampleUnitTestProject\SampleUnitTestProject.csproj", "{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -60,13 +62,26 @@ Global
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8}.Release|x64.Build.0 = Release|Any CPU
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8}.Release|x86.ActiveCfg = Release|Any CPU
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8}.Release|x86.Build.0 = Release|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x64.ActiveCfg = Debug|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x64.Build.0 = Debug|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x86.ActiveCfg = Debug|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Debug|x86.Build.0 = Debug|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|Any CPU.Build.0 = Release|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x64.ActiveCfg = Release|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x64.Build.0 = Release|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x86.ActiveCfg = Release|Any CPU
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{77CA5040-B4A0-4D0B-ADDD-09853A385007} = {C95ECC05-F3E8-49F4-B7C5-A29CD7EACFC1}
{8C42EBD4-FF36-44B6-A70E-7D83CB0626F8} = {FA92AD98-1291-4A90-A3AA-ED81A9BBC86E}
{AFB35FB3-F22D-436A-84F7-A5DEFD879D3D} = {FA92AD98-1291-4A90-A3AA-ED81A9BBC86E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A7517899-6171-4E6B-BDD7-DBE01B34E83A}
Expand Down
Empty file added root
Empty file.
2 changes: 1 addition & 1 deletion src/AzurePipelines.TestLogger/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ internal virtual async Task<string> SendAsync(HttpMethod method, string endpoint

if (Verbose)
{
Console.WriteLine($"Request:\n{method} {requestUri}\n{body}\n\nResponse:\n{response.StatusCode}\n{responseBody}");
Console.WriteLine($"Request:\n{method} {requestUri}\n{body.Indented()}\n\nResponse:\n{response.StatusCode}\n{responseBody.Indented()}\n");
}

try
Expand Down
1 change: 0 additions & 1 deletion src/AzurePipelines.TestLogger/ApiClientV5.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using AzurePipelines.TestLogger.Json;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
Expand Down
52 changes: 26 additions & 26 deletions src/AzurePipelines.TestLogger/AzurePipelines.TestLogger.csproj
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.5</TargetFramework>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.5</TargetFramework>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<NoPackageAnalysis>true</NoPackageAnalysis>
</PropertyGroup>

<ItemGroup>
<Compile Remove="contentFiles\**" />
<EmbeddedResource Remove="contentFiles\**" />
<None Remove="contentFiles\**" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="15.0.0" />
<PackageReference Include="Semver" Version="2.0.6" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>AzurePipelines.TestLogger.Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="15.0.0" />
<PackageReference Include="Semver" Version="2.0.6" GeneratePathProperty="true" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>AzurePipelines.TestLogger.Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<Target Name="CopyFileFromNuGetPackage" AfterTargets="Build">
<Copy SourceFiles="$(PkgSemver)\lib\netstandard1.1\Semver.dll" DestinationFolder="$(OutDir)" />
</Target>

</Project>
28 changes: 24 additions & 4 deletions src/AzurePipelines.TestLogger/AzurePipelinesTestLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class AzurePipelinesTestLogger : ITestLoggerWithParameters

public AzurePipelinesTestLogger()
{
// For debugging purposes
// System.Diagnostics.Debugger.Launch();
_environmentVariableProvider = new EnvironmentVariableProvider();
_apiClientFactory = new ApiClientFactory();
}
Expand Down Expand Up @@ -137,10 +139,28 @@ private void TestMessageHandler(object sender, TestRunMessageEventArgs e)
// Add code to handle message
}

private void TestResultHandler(object sender, TestResultEventArgs e) =>
_queue.Enqueue(new VstpTestResult(e.Result));
private void TestResultHandler(object sender, TestResultEventArgs e)
{
try
{
_queue.Enqueue(new VstpTestResult(e.Result));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}

private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) =>
_queue.Flush(new VstpTestRunComplete(e.IsAborted || e.IsCanceled, e.AttachmentSets));
private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e)
{
try
{
_queue.Flush(new VstpTestRunComplete(e.IsAborted || e.IsCanceled, e.AttachmentSets));
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
41 changes: 41 additions & 0 deletions src/AzurePipelines.TestLogger/Json/JsonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,46 @@ private static string JsonEscape(string value)

return sb.ToString();
}

/// <summary>
/// Indent JSON string.
/// </summary>
/// <param name="input">The JSON string.</param>
/// <returns>The indented JSON string.</returns>
public static string Indented(this string input)
{
int level = 0;
StringBuilder result = new StringBuilder();

for (int i = 0; i < input.Length; i++)
{
char c = input[i];

if (c == '{' || c == '[')
{
result.Append(c);
result.AppendLine();
result.Append(new string(' ', ++level * 2));
}
else if (c == '}' || c == ']')
{
result.AppendLine();
result.Append(new string(' ', --level * 2));
result.Append(c);
}
else if (c == ',')
{
result.Append(c);
result.AppendLine();
result.Append(new string(' ', level * 2));
}
else
{
result.Append(c);
}
}

return result.ToString();
}
}
}
77 changes: 35 additions & 42 deletions src/AzurePipelines.TestLogger/LoggerQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,72 +43,65 @@ public void Flush(VstpTestRunComplete testRunComplete)
// Cancel any idle consumers and let them return
_queue.Cancel();

try
{
// Any active consumer will circle back around and batch post the remaining queue
_consumeTask.Wait(TimeSpan.FromSeconds(60));
// Any active consumer will circle back around and batch post the remaining queue
_consumeTask.Wait(TimeSpan.FromSeconds(60));

// Update the run and parents to a completed state
SendTestsCompleted(testRunComplete, _consumeTaskCancellationSource.Token).Wait(TimeSpan.FromSeconds(60));
// Update the run and parents to a completed state
SendTestsCompleted(testRunComplete, _consumeTaskCancellationSource.Token).Wait(TimeSpan.FromSeconds(60));

// Cancel any active HTTP requests if still hasn't finished flushing
_consumeTaskCancellationSource.Cancel();
if (!_consumeTask.Wait(TimeSpan.FromSeconds(10)))
{
throw new TimeoutException("Cancellation didn't happen quickly");
}
}
catch (Exception ex)
// Cancel any active HTTP requests if still hasn't finished flushing
_consumeTaskCancellationSource.Cancel();
if (!_consumeTask.Wait(TimeSpan.FromSeconds(10)))
{
Console.WriteLine(ex);
throw new TimeoutException("Cancellation didn't happen quickly");
}
}

private async Task ConsumeItemsAsync(CancellationToken cancellationToken)
{
while (true)
{
ITestResult[] nextItems = await _queue.TakeAsync().ConfigureAwait(false);

if (nextItems == null || nextItems.Length == 0)
try
{
// Queue is canceling and is empty
return;
}
ITestResult[] nextItems = await _queue.TakeAsync().ConfigureAwait(false);

if (nextItems == null || nextItems.Length == 0)
{
// Queue is canceling and is empty
return;
}

await SendResultsAsync(nextItems, cancellationToken).ConfigureAwait(false);
await SendResultsAsync(nextItems, cancellationToken).ConfigureAwait(false);

if (cancellationToken.IsCancellationRequested)
if (cancellationToken.IsCancellationRequested)
{
return;
}
}
catch (Exception ex)
{
return;
Console.WriteLine(ex);
}
}
}

private async Task SendResultsAsync(ITestResult[] testResults, CancellationToken cancellationToken)
{
try
// Create a test run if we need it
if (RunId == 0)
{
// Create a test run if we need it
if (RunId == 0)
{
Source = GetSource(testResults);
RunId = await CreateTestRun(cancellationToken).ConfigureAwait(false);
}
Source = GetSource(testResults);
RunId = await CreateTestRun(cancellationToken).ConfigureAwait(false);
}

// Group results by their parent
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent = GroupTestResultsByParent(testResults);
// Group results by their parent
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent = GroupTestResultsByParent(testResults);

// Create any required parent nodes
await CreateParents(testResultsByParent, cancellationToken).ConfigureAwait(false);
// Create any required parent nodes
await CreateParents(testResultsByParent, cancellationToken).ConfigureAwait(false);

// Update parents with the test results
await SendTestResults(testResultsByParent, cancellationToken).ConfigureAwait(false);
}
catch (Exception)
{
// Eat any communications exceptions
}
// Update parents with the test results
await SendTestResults(testResultsByParent, cancellationToken).ConfigureAwait(false);
}

// Internal for testing
Expand Down
Loading