-
Notifications
You must be signed in to change notification settings - Fork 1
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
Added progress support for LSP #1
Changes from 10 commits
ffaff4f
4f2eb72
aa1f904
cd09aa2
2c1476b
434bfb3
99fea2c
fb40a13
8ec6b86
9b96a9d
b4ba6cf
1cc5b4d
8504512
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.Serialization; | ||
using System.Text; | ||
using System.Threading; | ||
|
@@ -280,6 +281,14 @@ public async Task ThrowsIfTargetNotSet() | |
await Assert.ThrowsAsync<RemoteMethodNotFoundException>(() => this.serverRpc.InvokeAsync(nameof(Server.OverloadedMethod))); | ||
} | ||
|
||
[Fact] | ||
public async Task InvokeWithProgressParameter_NoMemoryLeakConfirm() | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a |
||
WeakReference weakRef = await this.InvokeWithProgressParameter_NoMemoryLeakConfirm_Helper(); | ||
GC.Collect(); | ||
Assert.False(weakRef.IsAlive); | ||
} | ||
|
||
[Theory] | ||
[InlineData(true)] | ||
[InlineData(false)] | ||
|
@@ -834,6 +843,104 @@ public async Task InvokeWithParameterObject_DefaultParameters() | |
Assert.Equal(12, sum); | ||
} | ||
|
||
[Fact] | ||
public async Task InvokeWithParameterObject_ProgressParameter() | ||
{ | ||
int report = 0; | ||
ProgressWithCompletion<int> progress = new ProgressWithCompletion<int>(n => | ||
{ | ||
report = n; | ||
}); | ||
|
||
int result = await this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithProgressParameter), new { p = progress }, this.TimeoutToken); | ||
|
||
Assert.Equal(1, report); | ||
Assert.Equal(1, result); | ||
} | ||
|
||
[Fact] | ||
public async Task InvokeWithParameterObject_ProgressParameterMultipleRequests() | ||
{ | ||
int report1 = 0; | ||
ProgressWithCompletion<int> progress1 = new ProgressWithCompletion<int>(n => | ||
{ | ||
report1 = n; | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tip: you can shrink this syntax from 4 lines to 1: |
||
|
||
int report2 = 0; | ||
ProgressWithCompletion<int> progress2 = new ProgressWithCompletion<int>(n => | ||
{ | ||
report2 = n * 2; | ||
}); | ||
|
||
int report3 = 0; | ||
ProgressWithCompletion<int> progress3 = new ProgressWithCompletion<int>(n => | ||
{ | ||
report3 = n * 3; | ||
}); | ||
|
||
await this.InvokeMethodWithProgressParameter(progress1); | ||
await this.InvokeMethodWithProgressParameter(progress2); | ||
await this.InvokeMethodWithProgressParameter(progress3); | ||
|
||
await progress1.WaitAsync(); | ||
await progress2.WaitAsync(); | ||
await progress3.WaitAsync(); | ||
|
||
Assert.Equal(1, report1); | ||
Assert.Equal(2, report2); | ||
Assert.Equal(3, report3); | ||
} | ||
|
||
[Fact] | ||
public async Task InvokeWithParameterObject_InvalidParamMethod() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think the exception thrown is reasonable. Can you check the trace output of the test and share what it prints? I'm hoping on the server-side it gives a clear idea of why the method expected to take this wasn't selected. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, this is the output of the test: In reply to: 304388083 [](ancestors = 304388083) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm... that error is not super-great, given the client did supply a value. We might want to find the code that's emitting that message and see if we can fine-tune it to distinguish between "no value" and "incompatible value". #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found this in the JsonRpc class: The targetMethod.LookupErrorMessage contains the message that is being thrown in the Exception, maybe we could customize that message just like we do for the MethodNotfoundError:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. If you make any change in this area please review the messages in the relevant cases to make sure it all makes sense. IMO this is optional, but a nice to have. In reply to: 304577320 [](ancestors = 304577320) |
||
{ | ||
int report = 0; | ||
ProgressWithCompletion<int> progress = new ProgressWithCompletion<int>(n => | ||
{ | ||
report = n; | ||
}); | ||
|
||
await Assert.ThrowsAsync<RemoteMethodNotFoundException>(() => this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithInvalidProgressParameter), new { p = progress }, this.TimeoutToken)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The preferred syntax for such an assertion is: await Assert.ThrowsAsync<RemoteMethodNotFoundException>(() => this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithInvalidProgressParameter), new { p = progress }, this.TimeoutToken));
``` #Closed |
||
|
||
[Fact] | ||
public async Task InvokeWithParameterObject_ProgressParameterAndFields() | ||
{ | ||
int report = 0; | ||
ProgressWithCompletion<int> progress = new ProgressWithCompletion<int>(n => | ||
{ | ||
report += n; | ||
}); | ||
|
||
int sum = await this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithProgressAndMoreParameters), new { p = progress, x = 2, y = 5 }, this.TimeoutToken); | ||
|
||
Assert.Equal(7, report); | ||
Assert.Equal(7, sum); | ||
} | ||
|
||
[Fact] | ||
public async Task InvokeWithParameterObject_ProgressAndDefaultParameters() | ||
{ | ||
int report = 0; | ||
ProgressWithCompletion<int> progress = new ProgressWithCompletion<int>(n => | ||
{ | ||
report += n; | ||
}); | ||
|
||
int sum = await this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithProgressAndMoreParameters), new { p = progress, x = 2 }, this.TimeoutToken); | ||
|
||
Assert.Equal(12, report); | ||
Assert.Equal(12, sum); | ||
} | ||
|
||
[Fact] | ||
public async Task InvokeWithParameterObject_ClassIncludingProgressProperty() | ||
{ | ||
int sum = await this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithProgressAndMoreParameters), new XAndYFieldsWithProgress { x = 2, y = 5, p = new Progress<int>() }, this.TimeoutToken); | ||
Assert.Equal(7, sum); | ||
} | ||
|
||
[Fact] | ||
public async Task CanInvokeServerMethodWithNoParameterPassedAsArray() | ||
{ | ||
|
@@ -1402,6 +1509,26 @@ private void StartListening() | |
this.clientRpc.StartListening(); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private async Task<WeakReference> InvokeWithProgressParameter_NoMemoryLeakConfirm_Helper() | ||
{ | ||
ProgressWithCompletion<int> progress = new ProgressWithCompletion<int>(report => { }); | ||
|
||
WeakReference weakRef = new WeakReference(progress); | ||
|
||
int invokeTask = await this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithProgressParameter), new { p = progress }); | ||
|
||
// Clear progress variable locally | ||
progress = null; | ||
|
||
return weakRef; | ||
} | ||
|
||
private async Task InvokeMethodWithProgressParameter(IProgress<int> progress) | ||
{ | ||
await this.clientRpc.InvokeWithParameterObjectAsync<int>(nameof(Server.MethodWithProgressParameter), new { p = progress }, this.TimeoutToken); | ||
} | ||
|
||
public class BaseClass | ||
{ | ||
protected readonly TaskCompletionSource<string> notificationTcs = new TaskCompletionSource<string>(); | ||
|
@@ -1459,6 +1586,38 @@ public static int MethodWithDefaultParameter(int x, int y = 10) | |
return x + y; | ||
} | ||
|
||
public static int MethodWithProgressParameter(IProgress<int> p) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We should create a new nested class (sibling to the Server class) that defines just one method which takes a |
||
{ | ||
p.Report(1); | ||
return 1; | ||
} | ||
|
||
public static int MethodWithProgressAndMoreParameters(IProgress<int> p, int x, int y = 10) | ||
{ | ||
int sum = x + y; | ||
p.Report(x); | ||
p.Report(y); | ||
return sum; | ||
} | ||
|
||
public static int MethodWithProgressArrayParameter(params IProgress<int>[] progressArray) | ||
{ | ||
int report = 0; | ||
|
||
foreach (IProgress<int> progress in progressArray) | ||
{ | ||
report++; | ||
progress.Report(report); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
public static int MethodWithInvalidProgressParameter(Progress<int> p) | ||
{ | ||
return 1; | ||
} | ||
|
||
public int? MethodReturnsNullableInt(int a) => a > 0 ? (int?)a : null; | ||
|
||
public int MethodAcceptsNullableArgs(int? a, int? b) => (a.HasValue ? 1 : 0) + (b.HasValue ? 1 : 0); | ||
|
@@ -1786,6 +1945,20 @@ public class XAndYFields | |
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter | ||
} | ||
|
||
[DataContract] | ||
public class XAndYFieldsWithProgress | ||
{ | ||
// We disable SA1307 because we must use lowercase members as required to match the parameter names. | ||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter | ||
[DataMember] | ||
public int x; | ||
[DataMember] | ||
public int y; | ||
[DataMember] | ||
public IProgress<int> p; | ||
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter | ||
} | ||
|
||
internal class InternalClass | ||
{ | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all the same comments as the test method above.
There's enough in common between these tests that you may want to refactor them to share a helper method that just takes a delegate that does the original client invocation but does the rest exactly the same. #Closed