Add tests to AsyncRx.NET
- Dominant language
- C#
- Stars
- 7.2k
- Forks
- 798
- PR merge metrics
- No merged PRs in 30d
Description
The AsyncRx.NET code is currently experimental. Although it was added to this repository several years ago, it was not published to NuGet until the very recent [6.0.0-alpha.3 release](https://www.nuget.org/packages/System.Reactive.Async/6.0.0-alpha.3).
It is labelled as `alpha` to indicate that this is currently a long way from being considered of production quality. In particular, there are currently no tests at all for this code
We would like to be able to share test code with the main Rx.NET repository. (A secondary goal for this sharing is to make it possible for other repositories to run the entire Rx test suite against either Rx.NET, AsyncRx.NET, or both, to make it possible for us to push support for UI frameworks out into separate repos. So a repo with WPF-specific extensions could, in this proposed world, execute all the parts of the test suite that will be affected by WPF-specific concerns, e.g., tests involving schedulers.) The majority of what the Rx.NET tests express is also applicable to AsyncRx.NET. Take this test of the Rx `Average` operator, for example:
https://github.com/dotnet/reactive/blob/2a8c658d8ba1af1fe21dd70e73fba4d12a85f04f/Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/AverageTest.cs#L101-L126
This describes a sequence of values that an observable source should emit, along with the schedule on which they should be emitted (in virtual time), tells the `TestScheduler` to send the sequence just described through the `Average` operator. (Something not immediately obvious from this code is that by default, the `TestScheduler.Start` method invokes the callback that subscribes to the source at virtual time 200, meaning that the first value specified by this test—value 1 at virtual time 150—will be emitted _before_ the `xs.Average()` observable is subscribed to.) It then verifies that the expected output is produced at the expected virtual time. (Specifically, it expects a single output immediately after the source completes, whose value is `3.0`, the average of the three input values that are produced after subscription, 3, 4, and 2.)
We would want a test for the asynchronous `Average` operator to test all the same things. There is exactly one detail of this test that makes it specific to the non-async version of Rx, and it's this call to `Start`:
```cs
var res = scheduler.Start(() =>
xs.Average()
);
```
That `Start` method's signature looks like this:
```cs
public ITestableObserver Start(Func> create)
```
so the call `cs.Average()` resolves to this method of the `Observable` class:
```cs
public static IObservable Average(this IObservable source)
```
What we would like to do is be able to generalize tests of this kind. We have some ideas about how this might be achieved, but haven't yet worked any of the ideas out thoroughly. Suggestions include:
* source code generators to create variants of the test
* sharing of source files (possibly via NuGet) along with use of `partial class` definitions enabling projects to plug in either an `IObservable`- or `IAsyncObervable`-flavoured element that all the test code refers to
* modifying the tests to use `IQbservable` and then using expression tree rewriting (a technique used a lot in http://github.com/reaqtive/reaqtor)
* introducing some new interface along the lines of `IReactiveTestTarget` defining the conceptually-common features (e.g., suitable `Average` methods in this case) that individual test projects can then implement on top of the underlying implementation to be tested
* it's conceivable that something clever involving generics and static interface virtuals might be workable
We have yet to prototype any of these, and it's not immediately obvious which approaches will work best. We would want to ensure that whatever we did didn't produce tests that are difficult to debug. We are also expecting that one tricky part to get right is handling cases where there do need to be differences. For example, ordinary Rx.NET's `Select` operator comes in two forms:
```cs
// Just the element:
public static IObservable Select(this IObservable source, Func selector);
// Element and index:
public static IObservable Select(this IObservable source, Func selector);
```
but in AsyncRx.NET, there are four:
```cs
// Just the element (non-async projection):
public static IAsyncObservable Select(this IAsyncObservable source, Func selector);
// Just the element (async projection):
public static IAsyncObservable Select(this IAsyncObservable source, Func> selector);
// Element and index (non-async projection):
public static IAsyncObservable Select(this IAsyncObservable source, Func selector);
// Element and index (async projection):
public static IAsyncObservable Select(this IAsyncObservable source, Func> selector);
```
More generally, any operator that accepts a callback is likely to have flavours that accept both an ordinary and an `async` callback. It's not obvious how we might take existing tests for `Select` like this:
https://github.com/dotnet/reactive/blob/2a8c658d8ba1af1fe21dd70e73fba4d12a85f04f/Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SelectTest.cs#L530-L532
and augment them to cover the versions that accept async projection callbacks.
It's possible that the kind of reuse we're hoping for is too ambitious. A fallback position is to use clipboard inheritance, but we're hoping to avoid that because it makes ongoing maintenance harder. We would much prefer it for there to be a single source of truth about what constitutes correctness for each operator.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.