Skip to content

Commit

Permalink
Trace failures to report progress
Browse files Browse the repository at this point in the history
  • Loading branch information
AArnott committed Apr 25, 2020
1 parent 046844a commit 8e4a3a5
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 9 deletions.
41 changes: 41 additions & 0 deletions src/StreamJsonRpc.Tests/CollectingTraceListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.VisualStudio.Threading;

public class CollectingTraceListener : TraceListener
{
private readonly StringBuilder lineInProgress = new StringBuilder();

private readonly ImmutableList<string>.Builder messages = ImmutableList.CreateBuilder<string>();

public override bool IsThreadSafe => false;

public IReadOnlyList<string> Messages
{
get
{
lock (this.messages)
{
return this.messages.ToImmutable();
}
}
}

public AsyncAutoResetEvent MessageReceived { get; } = new AsyncAutoResetEvent();

public override void Write(string message) => this.lineInProgress.Append(message);

public override void WriteLine(string message)
{
this.lineInProgress.Append(message);
lock (this.messages)
{
this.messages.Add(this.lineInProgress.ToString());
this.MessageReceived.Set();
}

this.lineInProgress.Clear();
}
}
34 changes: 34 additions & 0 deletions src/StreamJsonRpc.Tests/JsonRpcTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ public abstract class JsonRpcTests : TestBase
protected JsonRpc serverRpc;
protected IJsonRpcMessageHandler serverMessageHandler;
protected IJsonRpcMessageFormatter serverMessageFormatter;
protected CollectingTraceListener serverTraces;

protected Nerdbank.FullDuplexStream clientStream;
protected JsonRpc clientRpc;
protected IJsonRpcMessageHandler clientMessageHandler;
protected IJsonRpcMessageFormatter clientMessageFormatter;
protected CollectingTraceListener clientTraces;

private const int CustomTaskResult = 100;

Expand Down Expand Up @@ -940,6 +942,19 @@ public async Task InvokeWithParameterObject_Progress_InvalidParamMethod()
await Assert.ThrowsAsync<RemoteMethodNotFoundException>(() => this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithInvalidProgressParameter), new { p = progress }, this.TimeoutToken));
}

[Fact]
public async Task ReportProgressWithUnserializableData_LeavesTraceEvidence()
{
var progress = new Progress<TypeThrowsWhenSerialized>();
await this.clientRpc.InvokeWithCancellationAsync(nameof(Server.MethodWithUnserializableProgressType), new object[] { progress }, cancellationToken: this.TimeoutToken);

// Verify that the trace explains what went wrong with the original exception message.
while (!this.serverTraces.Messages.Any(m => m.Contains("Can't touch this")))
{
await this.serverTraces.MessageReceived.WaitAsync(this.TimeoutToken);
}
}

[Fact]
public async Task InvokeWithParameterObject_ProgressParameterAndFields()
{
Expand Down Expand Up @@ -1883,6 +1898,9 @@ private void ReinitializeRpcWithoutListening()

this.serverRpc.TraceSource.Listeners.Add(new XunitTraceListener(this.Logger));
this.clientRpc.TraceSource.Listeners.Add(new XunitTraceListener(this.Logger));

this.serverRpc.TraceSource.Listeners.Add(this.serverTraces = new CollectingTraceListener());
this.clientRpc.TraceSource.Listeners.Add(this.clientTraces = new CollectingTraceListener());
}

private void StartListening()
Expand Down Expand Up @@ -2036,6 +2054,11 @@ public static int MethodWithProgressAndMoreParameters(IProgress<int> p, int x, i
return sum;
}

public static void MethodWithUnserializableProgressType(IProgress<TypeThrowsWhenSerialized> progress)
{
progress.Report(new TypeThrowsWhenSerialized());
}

public static int MethodWithInvalidProgressParameter(Progress<int> p)
{
return 1;
Expand Down Expand Up @@ -2445,6 +2468,17 @@ public class CustomSerializedType
public string? Value { get; set; }
}

[DataContract]
public class TypeThrowsWhenSerialized
{
[DataMember]
public string Property
{
get => throw new Exception("Can't touch this.");
set => throw new NotImplementedException();
}
}

public class TypeThrowsWhenDeserialized
{
}
Expand Down
9 changes: 2 additions & 7 deletions src/StreamJsonRpc.Tests/XunitTraceListener.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using StreamJsonRpc;
using StreamJsonRpc.Protocol;
using Xunit.Abstractions;

internal class XunitTraceListener : TraceListener
Expand All @@ -28,7 +22,8 @@ public override void WriteLine(string message)
{
if (!this.disposed)
{
this.logger.WriteLine(this.lineInProgress.ToString() + message);
this.lineInProgress.Append(message);
this.logger.WriteLine(this.lineInProgress.ToString());
this.lineInProgress.Clear();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/StreamJsonRpc/JsonRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public enum TraceEvents
LocalContractViolation,

/// <summary>
/// An exception occurred when reading the $/progress notification.
/// An exception occurred when reading or writing the $/progress notification.
/// </summary>
ProgressNotificationError,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace StreamJsonRpc.Reflection
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft;
using Microsoft.VisualStudio.Threading;

Expand Down Expand Up @@ -267,7 +269,12 @@ public JsonProgress(JsonRpc rpc, object token)
/// <param name="value">The typed value that will be send in the notification to be reported by the original <see cref="IProgress{T}"/> instance.</param>
public void Report(T value)
{
this.rpc.NotifyAsync(ProgressRequestSpecialMethod, this.token, value).Forget();
this.rpc.NotifyAsync(ProgressRequestSpecialMethod, this.token, value).ContinueWith(
(t, s) => ((JsonRpc)s).TraceSource.TraceEvent(System.Diagnostics.TraceEventType.Error, (int)JsonRpc.TraceEvents.ProgressNotificationError, "Failed to send progress update. {0}", t.Exception.InnerException ?? t.Exception),
this.rpc,
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Default).Forget();
}
}
}
Expand Down

0 comments on commit 8e4a3a5

Please sign in to comment.