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.
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:
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
:
var res = scheduler.Start(() => xs.Average() );
That Start
method's signature looks like this:
public ITestableObserver<T> Start<T>(Func<IObservable<T>> create)
so the call cs.Average()
resolves to this method of the Observable
class:
public static IObservable<double> Average(this IObservable<int> 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:
partial class
definitions enabling projects to plug in either an IObservable<T>
- or IAsyncObervable<T>
-flavoured element that all the test code refers toIQbservable<T>
and then using expression tree rewriting (a technique used a lot in http://github.com/reaqtive/reaqtor)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 testedWe 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:
// Just the element: public static IObservable<TResult> Select<TSource, TResult>(this IObservable<TSource> source, Func<TSource, TResult> selector); // Element and index: public static IObservable<TResult> Select<TSource, TResult>(this IObservable<TSource> source, Func<TSource, int, TResult> selector);
but in AsyncRx.NET, there are four:
// Just the element (non-async projection): public static IAsyncObservable<TResult> Select<TSource, TResult>(this IAsyncObservable<TSource> source, Func<TSource, TResult> selector); // Just the element (async projection): public static IAsyncObservable<TResult> Select<TSource, TResult>(this IAsyncObservable<TSource> source, Func<TSource, ValueTask<TResult>> selector); // Element and index (non-async projection): public static IAsyncObservable<TResult> Select<TSource, TResult>(this IAsyncObservable<TSource> source, Func<TSource, int, TResult> selector); // Element and index (async projection): public static IAsyncObservable<TResult> Select<TSource, TResult>(this IAsyncObservable<TSource> source, Func<TSource, int, ValueTask<TResult>> 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:
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.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4