├── .gitattributes ├── .github └── dependabot.yml ├── .gitignore ├── .travis.yml ├── .vsts-ci.yml ├── LICENSE ├── README.md ├── reactive-extensions-benchmarks ├── Program.cs ├── impl │ ├── ConcatMapEagerXPerf.cs │ ├── ConcatMapXPerf.cs │ ├── FastAndDirtyConfig.cs │ ├── FlatMapXPerf.cs │ ├── MergeMapXPerf.cs │ ├── ObserveOnPerf.cs │ ├── QueuePerf.cs │ └── XMapYPerf.cs └── reactive-extensions-benchmarks.csproj ├── reactive-extensions-test ├── CacheSubjectTest.cs ├── CompletableSubjectTest.cs ├── DisposableHelperTest.cs ├── ExceptionHelperTest.cs ├── HalfSerializerTest.cs ├── ImmediateSchedulerTest.cs ├── MaybeSubjectTest.cs ├── MonocastSubjectTest.cs ├── PublishSubjectTest.cs ├── SingleAssignmentDisposableTest.cs ├── SingleSubjectTest.cs ├── TestObserverTest.cs ├── TestSchedulerTest.cs ├── UnitTest.cs ├── ValidationHelperTest.cs ├── completable │ ├── CompletableAmbTest.cs │ ├── CompletableAndThenTest.cs │ ├── CompletableBlockingSubscribeTest.cs │ ├── CompletableCacheTest.cs │ ├── CompletableConcatMapTest.cs │ ├── CompletableConcatTest.cs │ ├── CompletableCreateTest.cs │ ├── CompletableDeferTest.cs │ ├── CompletableDelaySubscriptionTest.cs │ ├── CompletableDelayTest.cs │ ├── CompletableDoTest.cs │ ├── CompletableEmptyTest.cs │ ├── CompletableErrorTest.cs │ ├── CompletableFlatMapObservableTest.cs │ ├── CompletableFlatMapTest.cs │ ├── CompletableFromActionTest.cs │ ├── CompletableFromTaskTest.cs │ ├── CompletableHideTest.cs │ ├── CompletableIgnoreAllElementsTest.cs │ ├── CompletableIgnoreElementTest.cs │ ├── CompletableLambdaObserverTest.cs │ ├── CompletableMergeTest.cs │ ├── CompletableNeverTest.cs │ ├── CompletableObserveOnTest.cs │ ├── CompletableOnErrorCompleteTest.cs │ ├── CompletableOnErrorResumeNextTest.cs │ ├── CompletableOnTerminateDetachTest.cs │ ├── CompletableRepeatTest.cs │ ├── CompletableRepeatWhenTest.cs │ ├── CompletableRetryTest.cs │ ├── CompletableRetryWhenTest.cs │ ├── CompletableSubscribeOnTest.cs │ ├── CompletableSubscribeSafeTest.cs │ ├── CompletableSwitchMapTest.cs │ ├── CompletableTakeUntilTest.cs │ ├── CompletableTimeoutTest.cs │ ├── CompletableTimerTest.cs │ ├── CompletableToMaybeTest.cs │ ├── CompletableToObservableTest.cs │ ├── CompletableToSingleTest.cs │ ├── CompletableToTaskTest.cs │ ├── CompletableUnsubscribeOnTest.cs │ ├── CompletableUsingTest.cs │ ├── CompletableWaitTest.cs │ └── UnitTest1.cs ├── maybe │ ├── MaybeAmbTest.cs │ ├── MaybeBlockingSubscribeTest.cs │ ├── MaybeCacheTest.cs │ ├── MaybeConcatEagerTest.cs │ ├── MaybeConcatMapEagerTest.cs │ ├── MaybeConcatMapTest.cs │ ├── MaybeConcatTest.cs │ ├── MaybeCreateTest.cs │ ├── MaybeDefaultIfEmptyTest.cs │ ├── MaybeDeferTest.cs │ ├── MaybeDelaySubscriptionTest.cs │ ├── MaybeDelayTest.cs │ ├── MaybeDoTest.cs │ ├── MaybeElementAtTest.cs │ ├── MaybeEmptyTest.cs │ ├── MaybeErrorTest.cs │ ├── MaybeFilterTest.cs │ ├── MaybeFlatMapEnumerableTest.cs │ ├── MaybeFlatMapManyTest.cs │ ├── MaybeFlatMapObservableTest.cs │ ├── MaybeFlatMapTest.cs │ ├── MaybeFromActionTest.cs │ ├── MaybeFromFuncTest.cs │ ├── MaybeFromTaskTest.cs │ ├── MaybeHideTest.cs │ ├── MaybeJustTest.cs │ ├── MaybeLambdaObserverTest.cs │ ├── MaybeLastElementTest.cs │ ├── MaybeMapTest.cs │ ├── MaybeMergeTest.cs │ ├── MaybeNeverTest.cs │ ├── MaybeObserveOnTest.cs │ ├── MaybeOnErrorCompleteTest.cs │ ├── MaybeOnErrorResumeNextTest.cs │ ├── MaybeOnTerminateDetachTest.cs │ ├── MaybeRepeatTest.cs │ ├── MaybeRepeatWhenTest.cs │ ├── MaybeRetryTest.cs │ ├── MaybeRetryWhenTest.cs │ ├── MaybeSingleElementTest.cs │ ├── MaybeSubscribeOnTest.cs │ ├── MaybeSubscribeSafeTest.cs │ ├── MaybeSwitchIfEmptyTest.cs │ ├── MaybeSwitchMapTest.cs │ ├── MaybeTakeUntilTest.cs │ ├── MaybeTimeoutTest.cs │ ├── MaybeTimerTest.cs │ ├── MaybeToObservableTest.cs │ ├── MaybeToSingleTest.cs │ ├── MaybeToTaskTest.cs │ ├── MaybeUnsubscribeOnTest.cs │ ├── MaybeUsingTest.cs │ ├── MaybeWaitTest.cs │ ├── MaybeZipTest.cs │ └── UnitTest1.cs ├── observable │ ├── AutoConnectTest.cs │ ├── BlockingEnumerableTest.cs │ ├── BlockingSubscribeTest.cs │ ├── CacheTest.cs │ ├── CollectTest.cs │ ├── CombineLatestTest.cs │ ├── ConcatDeadlockTest.cs │ ├── ConcatManyTest.cs │ ├── ConcatMapEagerTest.cs │ ├── ConcatMapEnumerableTest.cs │ ├── CreateTest.cs │ ├── DoAfterNextTest.cs │ ├── DoAfterTerminateTest.cs │ ├── DoFinallyTest.cs │ ├── DoOnDisposeTest.cs │ ├── DoOnSubscribeTest.cs │ ├── IntervalRangeTest.cs │ ├── MergeManyTest.cs │ ├── ObserveOnTest.cs │ ├── RepeatPredicateTest.cs │ ├── RepeatWhenTest.cs │ ├── RetryPredicateTest.cs │ ├── RetryWhenTest.cs │ ├── SerializedObserverTest.cs │ ├── SerializedSubjectTest.cs │ ├── SwitchIfEmptyTest.cs │ ├── SwitchMapTest.cs │ ├── TakeUntilPredicateTest.cs │ ├── UnitTest1.cs │ ├── UnsubscribeOnTest.cs │ ├── WithLatestFromTest.cs │ └── ZipTest.cs ├── observablesource │ ├── ObservableSourceAllTest.cs │ ├── ObservableSourceAmbTest.cs │ ├── ObservableSourceAnyTest.cs │ ├── ObservableSourceArrayTest.cs │ ├── ObservableSourceAutoConnectTest.cs │ ├── ObservableSourceBlockingConsumerTest.cs │ ├── ObservableSourceBlockingEnumerableTest.cs │ ├── ObservableSourceBlockingSubscribeTest.cs │ ├── ObservableSourceBufferBoundaryTest.cs │ ├── ObservableSourceBufferTest.cs │ ├── ObservableSourceCacheTest.cs │ ├── ObservableSourceCollectTest.cs │ ├── ObservableSourceCombineLatestTest.cs │ ├── ObservableSourceComposeTest.cs │ ├── ObservableSourceConcatArrayTest.cs │ ├── ObservableSourceConcatEnumerableTest.cs │ ├── ObservableSourceConcatMapEagerTest.cs │ ├── ObservableSourceConcatMapEnumerableTest.cs │ ├── ObservableSourceConcatMapTest.cs │ ├── ObservableSourceConcatWithTest.cs │ ├── ObservableSourceCreateTest.cs │ ├── ObservableSourceDeferTest.cs │ ├── ObservableSourceDelaySelectorTest.cs │ ├── ObservableSourceDelaySubscriptionTest.cs │ ├── ObservableSourceDelayTest.cs │ ├── ObservableSourceElementAtTest.cs │ ├── ObservableSourceEmptyTest.cs │ ├── ObservableSourceEnumerableTest.cs │ ├── ObservableSourceErrorTest.cs │ ├── ObservableSourceFilterTest.cs │ ├── ObservableSourceFlatMapTest.cs │ ├── ObservableSourceFromActionTest.cs │ ├── ObservableSourceFromFuncTest.cs │ ├── ObservableSourceFromObservableTest.cs │ ├── ObservableSourceFromTaskTest.cs │ ├── ObservableSourceGroupByTest.cs │ ├── ObservableSourceIgnoreElementsTest.cs │ ├── ObservableSourceIntervalRangeTest.cs │ ├── ObservableSourceIsEmptyTest.cs │ ├── ObservableSourceJoinTest.cs │ ├── ObservableSourceJustTest.cs │ ├── ObservableSourceLambdaSubscribeTest.cs │ ├── ObservableSourceLastTest.cs │ ├── ObservableSourceMapTest.cs │ ├── ObservableSourceMathTest.cs │ ├── ObservableSourceMergeManyTest.cs │ ├── ObservableSourceMergeTest.cs │ ├── ObservableSourceMulticastTest.cs │ ├── ObservableSourceNeverTest.cs │ ├── ObservableSourceObserveOnTest.cs │ ├── ObservableSourceOnErrorResumeNextTest.cs │ ├── ObservableSourceOnTerminateDetachTest.cs │ ├── ObservableSourcePeekTest.cs │ ├── ObservableSourcePublishTest.cs │ ├── ObservableSourceRangeTest.cs │ ├── ObservableSourceReduceTest.cs │ ├── ObservableSourceRefCountTest.cs │ ├── ObservableSourceRepeatPredicateTest.cs │ ├── ObservableSourceRepeatWhenTest.cs │ ├── ObservableSourceReplayTest.cs │ ├── ObservableSourceRetryPredicateTest.cs │ ├── ObservableSourceRetryWhenTest.cs │ ├── ObservableSourceSingleTest.cs │ ├── ObservableSourceSkipLastTest.cs │ ├── ObservableSourceSkipLastTimedTest.cs │ ├── ObservableSourceSkipTest.cs │ ├── ObservableSourceSkipUntilTest.cs │ ├── ObservableSourceSkipWhileTest.cs │ ├── ObservableSourceSubscribeOnTest.cs │ ├── ObservableSourceSwitchIfEmptyTest.cs │ ├── ObservableSourceSwitchMapTest.cs │ ├── ObservableSourceTakeLastTest.cs │ ├── ObservableSourceTakeLastTimedTest.cs │ ├── ObservableSourceTakeTest.cs │ ├── ObservableSourceTakeUntilPredicateTest.cs │ ├── ObservableSourceTakeUntilTest.cs │ ├── ObservableSourceTakeWhileTest.cs │ ├── ObservableSourceTimeoutTest.cs │ ├── ObservableSourceTimerTest.cs │ ├── ObservableSourceToObservableTest.cs │ ├── ObservableSourceToTaskTest.cs │ ├── ObservableSourceUnsubscribeOnTest.cs │ ├── ObservableSourceUsingTest.cs │ ├── ObservableSourceWindowBoundaryTest.cs │ ├── ObservableSourceWindowTest.cs │ ├── ObservableSourceWithLatestFromTest.cs │ ├── ObservableSourceZipTest.cs │ ├── SerializedSignalObserverTest.cs │ └── UnitTest1.cs ├── reactive-extensions-test.csproj ├── single │ ├── SingleAmbTest.cs │ ├── SingleBlockingSubscribeTest.cs │ ├── SingleCacheTest.cs │ ├── SingleConcatEagerTest.cs │ ├── SingleConcatMapEagerTest.cs │ ├── SingleConcatMapTest.cs │ ├── SingleConcatTest.cs │ ├── SingleCreateTest.cs │ ├── SingleDeferTest.cs │ ├── SingleDelaySubscriptionTest.cs │ ├── SingleDelayTest.cs │ ├── SingleDoTest.cs │ ├── SingleElementAtOrDefaultTest.cs │ ├── SingleElementAtOrErrorTest.cs │ ├── SingleErrorTest.cs │ ├── SingleFilterTest.cs │ ├── SingleFlatMapEnumerableTest.cs │ ├── SingleFlatMapManyTest.cs │ ├── SingleFlatMapObservableTest.cs │ ├── SingleFlatMapTest.cs │ ├── SingleFromFuncTest.cs │ ├── SingleFromTaskTest.cs │ ├── SingleHideTest.cs │ ├── SingleJustTest.cs │ ├── SingleLambdaObserverTest.cs │ ├── SingleLastOrDefaultTest.cs │ ├── SingleLastOrErrorTest.cs │ ├── SingleMapTest.cs │ ├── SingleMergeTest.cs │ ├── SingleNeverTest.cs │ ├── SingleObserveOnTest.cs │ ├── SingleOnErrorResumeNextTest.cs │ ├── SingleOnTerminateDetachTest.cs │ ├── SingleRepeatTest.cs │ ├── SingleRepeatWhenTest.cs │ ├── SingleRetryTest.cs │ ├── SingleRetryWhenTest.cs │ ├── SingleSingleOrDefaultTest.cs │ ├── SingleSingleOrErrorTest.cs │ ├── SingleSubscribeOnTest.cs │ ├── SingleSubscribeSafeTest.cs │ ├── SingleSwitchMapTest.cs │ ├── SingleTakeUntilTest.cs │ ├── SingleTimeoutTest.cs │ ├── SingleTimerTest.cs │ ├── SingleToMaybeTest.cs │ ├── SingleToObservableTest.cs │ ├── SingleToTaskTest.cs │ ├── SingleUnsubscribeOnTest.cs │ ├── SingleUsingTest.cs │ ├── SingleWaitTest.cs │ ├── SingleZipTest.cs │ └── UnitTest1.cs └── tools │ ├── FailingEnumerable.cs │ ├── LookupTest.cs │ ├── SpscLinkedArrayQueueTest.cs │ ├── TestHelper.cs │ ├── TestHelper_VerifyDispose_Maybe.cs │ ├── TestHelper_VerifyDispose_Observable.cs │ ├── TestHelper_VerifyDispose_ObservableSource.cs │ └── TestHelper_VerifyDispose_Single.cs ├── reactive-extensions.sln └── reactive-extensions ├── CacheSubject.cs ├── CompletableSubject.cs ├── IAsyncEnumerable.cs ├── ICompletableSource.cs ├── IMaybeSource.cs ├── IObservableEmitter.cs ├── IObservableSource.cs ├── ISingleSource.cs ├── ISubjectExtensions.cs ├── MaybeSubject.cs ├── MonocastSubject.cs ├── PublishSubject.cs ├── SingleSubject.cs ├── TestObserver.cs ├── TestScheduler.cs ├── UnicastSubject.cs ├── completable ├── CompletableAmb.cs ├── CompletableAndThen.cs ├── CompletableBlockingConsumer.cs ├── CompletableBlockingObserver.cs ├── CompletableCache.cs ├── CompletableConcat.cs ├── CompletableConcatMap.cs ├── CompletableCreate.cs ├── CompletableDefer.cs ├── CompletableDelay.cs ├── CompletableDelaySubscription.cs ├── CompletableEmpty.cs ├── CompletableError.cs ├── CompletableFlatMap.cs ├── CompletableFlatMapObservable.cs ├── CompletableFromAction.cs ├── CompletableFromTask.cs ├── CompletableHide.cs ├── CompletableIgnoreAllElements.cs ├── CompletableIgnoreElement.cs ├── CompletableInnerObserver.cs ├── CompletableLambdaObserver.cs ├── CompletableMerge.cs ├── CompletableNever.cs ├── CompletableObserveOn.cs ├── CompletableOnErrorComplete.cs ├── CompletableOnErrorResumeNext.cs ├── CompletableOnTerminateDetach.cs ├── CompletablePeek.cs ├── CompletableRedoObserver.cs ├── CompletableRedoWhenObserver.cs ├── CompletableRepeat.cs ├── CompletableRepeatWhen.cs ├── CompletableRetry.cs ├── CompletableRetryWhen.cs ├── CompletableSafeObserver.cs ├── CompletableSubscribeOn.cs ├── CompletableSwitchMap.cs ├── CompletableTakeUntil.cs ├── CompletableTimeout.cs ├── CompletableTimer.cs ├── CompletableToMaybe.cs ├── CompletableToObservable.cs ├── CompletableToSingle.cs ├── CompletableToTask.cs ├── CompletableUnsubscribeOn.cs ├── CompletableUsing.cs └── CompletableWait.cs ├── extensions ├── CompletableSource.cs ├── MaybeSource.cs ├── ObservableSource.cs ├── ReactiveExtensions.cs └── SingleSource.cs ├── maybe ├── MaybeAmb.cs ├── MaybeBlockingConsumer.cs ├── MaybeBlockingObserver.cs ├── MaybeCache.cs ├── MaybeConcat.cs ├── MaybeConcatEager.cs ├── MaybeConcatEagerCoordinator.cs ├── MaybeConcatEagerEnumerable.cs ├── MaybeConcatMap.cs ├── MaybeConcatMapEager.cs ├── MaybeCreate.cs ├── MaybeDefaultIfEmpty.cs ├── MaybeDefer.cs ├── MaybeDelay.cs ├── MaybeDelaySubscription.cs ├── MaybeElementAt.cs ├── MaybeEmpty.cs ├── MaybeError.cs ├── MaybeFilter.cs ├── MaybeFlatMap.cs ├── MaybeFlatMapEnumerable.cs ├── MaybeFlatMapMany.cs ├── MaybeFlatMapObservable.cs ├── MaybeFromAction.cs ├── MaybeFromFunc.cs ├── MaybeFromTask.cs ├── MaybeHide.cs ├── MaybeInnerObserver.cs ├── MaybeJust.cs ├── MaybeLambdaObserver.cs ├── MaybeLastElement.cs ├── MaybeMap.cs ├── MaybeMerge.cs ├── MaybeMergeCoordinator.cs ├── MaybeMergeEnumerable.cs ├── MaybeNever.cs ├── MaybeObserveOn.cs ├── MaybeOnErrorComplete.cs ├── MaybeOnErrorResumeNext.cs ├── MaybeOnTerminateDetach.cs ├── MaybePeek.cs ├── MaybeRedoWhenObserver.cs ├── MaybeRepeat.cs ├── MaybeRepeatWhen.cs ├── MaybeRetry.cs ├── MaybeRetryWhen.cs ├── MaybeSafeObserver.cs ├── MaybeSingleElement.cs ├── MaybeSubscribeOn.cs ├── MaybeSwitchIfEmpty.cs ├── MaybeSwitchMap.cs ├── MaybeTakeUntil.cs ├── MaybeTimeout.cs ├── MaybeTimer.cs ├── MaybeToObservable.cs ├── MaybeToSingle.cs ├── MaybeToTask.cs ├── MaybeUnsubscribeOn.cs ├── MaybeUsing.cs ├── MaybeWait.cs └── MaybeZip.cs ├── observable ├── BaseObservable.cs ├── BaseObserver.cs ├── BlockingEnumerable.cs ├── BlockingObserver.cs ├── Cache.cs ├── Collect.cs ├── CombineLatest.cs ├── ConcatMany.cs ├── ConcatMapEager.cs ├── ConcatMapEnumerable.cs ├── Create.cs ├── DoAfterNext.cs ├── DoAfterTerminate.cs ├── DoFinally.cs ├── DoOnDispose.cs ├── DoOnSubscribe.cs ├── IInnerObserverSupport.cs ├── ImmediateScheduler.cs ├── InnerObserver.cs ├── IntervalRange.cs ├── MergeMany.cs ├── ObserveOn.cs ├── RedoObserver.cs ├── RepeatPredicate.cs ├── RetryPredicate.cs ├── SerializedObserver.cs ├── SerializedSubject.cs ├── SwitchIfEmpty.cs ├── SwitchMap.cs ├── UnsubscribeOn.cs ├── WithLatestFrom.cs └── Zip.cs ├── observablesource ├── BaseSignalObserver.cs ├── BasicFuseableObserver.cs ├── DeferredScalarDisposable.cs ├── IInnerSignalObserverSupport.cs ├── InnerSignalObserver.cs ├── LambdaSignalObserver.cs ├── ObservableSourceAll.cs ├── ObservableSourceAmb.cs ├── ObservableSourceAny.cs ├── ObservableSourceArray.cs ├── ObservableSourceAutoConnect.cs ├── ObservableSourceBlockingConsumer.cs ├── ObservableSourceBlockingEnumerable.cs ├── ObservableSourceBlockingObserver.cs ├── ObservableSourceBuffer.cs ├── ObservableSourceBufferBoundary.cs ├── ObservableSourceCache.cs ├── ObservableSourceCollect.cs ├── ObservableSourceCombineLatest.cs ├── ObservableSourceConcatArray.cs ├── ObservableSourceConcatEnumerable.cs ├── ObservableSourceConcatMap.cs ├── ObservableSourceConcatMapEager.cs ├── ObservableSourceConcatMapEnumerable.cs ├── ObservableSourceConcatWith.cs ├── ObservableSourceCreate.cs ├── ObservableSourceDefer.cs ├── ObservableSourceDelay.cs ├── ObservableSourceDelaySelector.cs ├── ObservableSourceDelaySubscription.cs ├── ObservableSourceElementAt.cs ├── ObservableSourceElementAtTask.cs ├── ObservableSourceEmpty.cs ├── ObservableSourceEnumerable.cs ├── ObservableSourceError.cs ├── ObservableSourceFilter.cs ├── ObservableSourceFlatMap.cs ├── ObservableSourceFromAction.cs ├── ObservableSourceFromFunc.cs ├── ObservableSourceFromObservable.cs ├── ObservableSourceFromTask.cs ├── ObservableSourceGroupBy.cs ├── ObservableSourceHide.cs ├── ObservableSourceIgnoreElements.cs ├── ObservableSourceIgnoreElementsTask.cs ├── ObservableSourceIntervalRange.cs ├── ObservableSourceIsEmpty.cs ├── ObservableSourceJoin.cs ├── ObservableSourceJust.cs ├── ObservableSourceLast.cs ├── ObservableSourceLastTask.cs ├── ObservableSourceMap.cs ├── ObservableSourceMerge.cs ├── ObservableSourceMergeMany.cs ├── ObservableSourceMulticast.cs ├── ObservableSourceNever.cs ├── ObservableSourceObserveOn.cs ├── ObservableSourceOnErrorResumeNext.cs ├── ObservableSourceOnTerminateDetach.cs ├── ObservableSourcePeek.cs ├── ObservableSourcePublish.cs ├── ObservableSourceRange.cs ├── ObservableSourceReduce.cs ├── ObservableSourceRefCount.cs ├── ObservableSourceRepeatPredicate.cs ├── ObservableSourceRepeatWhen.cs ├── ObservableSourceReplay.cs ├── ObservableSourceRetryPredicate.cs ├── ObservableSourceRetryWhen.cs ├── ObservableSourceSingle.cs ├── ObservableSourceSingleTask.cs ├── ObservableSourceSkip.cs ├── ObservableSourceSkipLast.cs ├── ObservableSourceSkipLastTimed.cs ├── ObservableSourceSkipUntil.cs ├── ObservableSourceSkipWhile.cs ├── ObservableSourceSubscribeOn.cs ├── ObservableSourceSwitchIfEmpty.cs ├── ObservableSourceSwitchMap.cs ├── ObservableSourceTake.cs ├── ObservableSourceTakeLast.cs ├── ObservableSourceTakeLastTimed.cs ├── ObservableSourceTakeUntil.cs ├── ObservableSourceTakeUntilPredicate.cs ├── ObservableSourceTakeWhile.cs ├── ObservableSourceTimeout.cs ├── ObservableSourceTimer.cs ├── ObservableSourceToObservable.cs ├── ObservableSourceUnsubscribeOn.cs ├── ObservableSourceUsing.cs ├── ObservableSourceWindow.cs ├── ObservableSourceWindowBoundary.cs ├── ObservableSourceWithLatestFrom.cs ├── ObservableSourceZip.cs ├── RedoSignalObserver.cs ├── RedoWhenSignalObserver.cs └── SerializedSignalObserver.cs ├── reactive-extensions.csproj ├── single ├── SingleAmb.cs ├── SingleBlockingConsumer.cs ├── SingleBlockingObserver.cs ├── SingleCache.cs ├── SingleConcat.cs ├── SingleConcatEager.cs ├── SingleConcatEagerCoordinator.cs ├── SingleConcatEagerEnumerable.cs ├── SingleConcatMap.cs ├── SingleConcatMapEager.cs ├── SingleCreate.cs ├── SingleDefer.cs ├── SingleDelay.cs ├── SingleDelaySubscription.cs ├── SingleElementAtOrDefault.cs ├── SingleElementAtOrError.cs ├── SingleError.cs ├── SingleFilter.cs ├── SingleFlatMap.cs ├── SingleFlatMapEnumerable.cs ├── SingleFlatMapMany.cs ├── SingleFlatMapObservable.cs ├── SingleFromFunc.cs ├── SingleFromTask.cs ├── SingleHide.cs ├── SingleInnerObserver.cs ├── SingleJust.cs ├── SingleLambdaObserver.cs ├── SingleLastOrDefault.cs ├── SingleLastOrError.cs ├── SingleMap.cs ├── SingleMerge.cs ├── SingleMergeCoordinator.cs ├── SingleMergeEnumerable.cs ├── SingleNever.cs ├── SingleObserveOn.cs ├── SingleOnErrorResumeNext.cs ├── SingleOnTerminateDetach.cs ├── SinglePeek.cs ├── SingleRedoWhenObserver.cs ├── SingleRepeat.cs ├── SingleRepeatWhen.cs ├── SingleRetry.cs ├── SingleRetryWhen.cs ├── SingleSafeObserver.cs ├── SingleSingleOrDefault.cs ├── SingleSingleOrError.cs ├── SingleSubscribeOn.cs ├── SingleSwitchMap.cs ├── SingleTakeUntil.cs ├── SingleTimeout.cs ├── SingleTimer.cs ├── SingleToMaybe.cs ├── SingleToObservable.cs ├── SingleToTask.cs ├── SingleUnsubscribeOn.cs ├── SingleUsing.cs ├── SingleWait.cs └── SingleZip.cs └── tools ├── BooleanDisposable.cs ├── DisposableHelper.cs ├── ExceptionHelper.cs ├── HalfSerializer.cs ├── Lookup.cs ├── SingleAssignmentDisposable.cs ├── SpscLinkedArrayQueue.cs └── ValidationHelper.cs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 20 9 | ignore: 10 | - dependency-name: Microsoft.NET.Test.Sdk 11 | versions: 12 | - 16.9.1 13 | - 16.9.4 14 | - dependency-name: nunit 15 | versions: 16 | - 3.13.1 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | dotnet: 2.1.200 3 | 4 | script: 5 | - dotnet test ./reactive-extensions-test/reactive-extensions-test.csproj -------------------------------------------------------------------------------- /.vsts-ci.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | vmImage: ubuntu-latest 3 | 4 | variables: 5 | BuildConfiguration: Debug 6 | 7 | steps: 8 | 9 | - task: DotNetCoreCLI@2 10 | inputs: 11 | command: pack 12 | packagesToPack: reactive-extensions/*.csproj 13 | configuration: $(BuildConfiguration) 14 | packDirectory: $(Build.ArtifactStagingDirectory)\artifacts 15 | verbosityPack: Minimal 16 | displayName: Pack 17 | 18 | - task: DotNetCoreCLI@2 19 | inputs: 20 | command: test 21 | projects: reactive-extensions-test/*.csproj 22 | arguments: -c $(BuildConfiguration) 23 | displayName: Run Tests 24 | 25 | - task: PublishBuildArtifacts@1 26 | displayName: Publish Package Artifacts 27 | inputs: 28 | pathToPublish: $(Build.ArtifactStagingDirectory)\artifacts 29 | artifactType: container 30 | artifactName: Packages 31 | condition: eq(variables['system.pullrequest.isfork'], false) 32 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using System; 3 | 4 | namespace akarnokd.reactive_extensions_benchmarks 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | // Yes, these have to be manually enabled and disabled! 11 | //BenchmarkRunner.Run(); 12 | //BenchmarkRunner.Run(); 13 | //BenchmarkRunner.Run(); 14 | //BenchmarkRunner.Run(); 15 | //BenchmarkRunner.Run(); 16 | //BenchmarkRunner.Run(); 17 | BenchmarkRunner.Run(); 18 | //BenchmarkRunner.Run(); 19 | //BenchmarkRunner.Run(); 20 | // -------------------------------------------------- 21 | Console.ReadLine(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/impl/ConcatMapXPerf.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Attributes.Jobs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Reactive.Linq; 9 | using akarnokd.reactive_extensions; 10 | 11 | namespace akarnokd.reactive_extensions_benchmarks 12 | { 13 | 14 | [Config(typeof(FastAndDirtyConfig))] 15 | public class ConcatMapXPerf 16 | { 17 | [Params(1, 10, 100, 1000, 10000, 100000)] 18 | public int N; 19 | 20 | IObservable concat; 21 | 22 | IObservable concatMap; 23 | 24 | [GlobalSetup] 25 | public void Setup() 26 | { 27 | var outer = new int[100000 / N]; 28 | var inner = new int[N]; 29 | 30 | concat = outer.ToObservable().Select(v => inner.ToObservable()).Concat(); 31 | 32 | concatMap = outer.ToObservable().ConcatMap(v => inner); 33 | } 34 | 35 | [Benchmark] 36 | public int Concat() 37 | { 38 | return concat.Wait(); 39 | } 40 | 41 | [Benchmark] 42 | public int ConcatMap() 43 | { 44 | return concatMap.Wait(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/impl/FastAndDirtyConfig.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Configs; 2 | using BenchmarkDotNet.Jobs; 3 | using BenchmarkDotNet.Horology; 4 | 5 | namespace akarnokd.reactive_extensions_benchmarks 6 | { 7 | public class FastAndDirtyConfig : ManualConfig 8 | { 9 | public FastAndDirtyConfig() 10 | { 11 | Add(Job.Default 12 | .WithLaunchCount(1) 13 | .WithIterationTime(TimeInterval.FromSeconds(1)) 14 | .WithWarmupCount(5) 15 | .WithTargetCount(5) 16 | ); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/impl/FlatMapXPerf.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Attributes.Jobs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Reactive.Linq; 9 | using akarnokd.reactive_extensions; 10 | 11 | namespace akarnokd.reactive_extensions_benchmarks 12 | { 13 | 14 | [Config(typeof(FastAndDirtyConfig))] 15 | public class FlatMapXPerf 16 | { 17 | [Params(1, 10, 100, 1000, 10000, 100000)] 18 | public int N; 19 | 20 | IObservable selectMany; 21 | 22 | //IObservable flatMap; 23 | 24 | [GlobalSetup] 25 | public void Setup() 26 | { 27 | var outer = new int[100000 / N]; 28 | var inner = new int[N]; 29 | 30 | selectMany = outer.ToObservable().SelectMany(v => inner.ToObservable()); 31 | 32 | // flatMap = outer.ToObservable().FlatMap(v => inner.ToObservable()); 33 | } 34 | 35 | [Benchmark] 36 | public int SelectMany() 37 | { 38 | return selectMany.Wait(); 39 | } 40 | 41 | /* 42 | [Benchmark] 43 | public int FlatMap() 44 | { 45 | return flatMap.Wait(); 46 | } 47 | */ 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/impl/MergeMapXPerf.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Attributes.Jobs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Reactive.Linq; 9 | using akarnokd.reactive_extensions; 10 | 11 | namespace akarnokd.reactive_extensions_benchmarks 12 | { 13 | 14 | [Config(typeof(FastAndDirtyConfig))] 15 | public class MergeMapXPerf 16 | { 17 | [Params(1, 10, 100, 1000, 10000, 100000)] 18 | public int N; 19 | 20 | IObservable merge; 21 | 22 | IObservable merge1; 23 | 24 | IObservable mergeMany; 25 | 26 | IObservable mergeMany1; 27 | 28 | [GlobalSetup] 29 | public void Setup() 30 | { 31 | var outer = new int[100000 / N]; 32 | var inner = new int[N]; 33 | 34 | merge = outer.ToObservable().Select(v => inner.ToObservable()).Merge(); 35 | 36 | merge1 = outer.ToObservable().Select(v => inner.ToObservable()).Merge(1); 37 | 38 | mergeMany = outer.ToObservable().Select(v => inner.ToObservable()).MergeMany(); 39 | 40 | mergeMany1 = outer.ToObservable().Select(v => inner.ToObservable()).MergeMany(maxConcurrency: 1); 41 | } 42 | 43 | [Benchmark] 44 | public int Merge() 45 | { 46 | return merge.Wait(); 47 | } 48 | 49 | [Benchmark] 50 | public int Merge_Max1() 51 | { 52 | return merge1.Wait(); 53 | } 54 | 55 | [Benchmark] 56 | public int MergeMany() 57 | { 58 | return mergeMany.Wait(); 59 | } 60 | 61 | [Benchmark] 62 | public int MergeMany_Max1() 63 | { 64 | return mergeMany1.Wait(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/impl/XMapYPerf.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Attributes.Jobs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Reactive.Linq; 9 | using akarnokd.reactive_extensions; 10 | 11 | namespace akarnokd.reactive_extensions_benchmarks 12 | { 13 | 14 | [Config(typeof(FastAndDirtyConfig))] 15 | public class XMapYPerf 16 | { 17 | [Params(1, 10, 100, 1000, 10000, 100000)] 18 | public int N; 19 | 20 | IObservable concat; 21 | 22 | IObservable concatMany; 23 | 24 | [GlobalSetup] 25 | public void Setup() 26 | { 27 | var outer = new int[100000 / N]; 28 | var inner = new int[N]; 29 | 30 | concat = outer.ToObservable().Select(v => inner.ToObservable()).Concat(); 31 | 32 | concatMany = outer.ToObservable().Select(v => inner.ToObservable()).ConcatMany(); 33 | } 34 | 35 | [Benchmark] 36 | public int Concat() 37 | { 38 | return concat.Wait(); 39 | } 40 | 41 | [Benchmark] 42 | public int ConcatMany() 43 | { 44 | return concatMany.Wait(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /reactive-extensions-benchmarks/reactive-extensions-benchmarks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net462 5 | reactive_extensions_benchmarks 6 | Exe 7 | true 8 | c:\users\akarnokd\.gradle\akarnokd.snk 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/ImmediateSchedulerTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test 6 | { 7 | [TestFixture] 8 | public class ImmediateScheduleTest 9 | { 10 | [Test] 11 | public void Direct() 12 | { 13 | var count = 0; 14 | ImmediateScheduler.INSTANCE.Schedule(1, (s, t) => { ++count; return DisposableHelper.EMPTY; }); 15 | 16 | Assert.AreEqual(1, count); 17 | } 18 | 19 | [Test] 20 | public void Delayed() 21 | { 22 | var count = 0; 23 | ImmediateScheduler.INSTANCE.Schedule(1, TimeSpan.FromMilliseconds(100), (s, t) => { ++count; return DisposableHelper.EMPTY; }); 24 | 25 | Assert.AreEqual(1, count); 26 | } 27 | 28 | [Test] 29 | public void DueDate() 30 | { 31 | var count = 0; 32 | ImmediateScheduler.INSTANCE.Schedule(1, DateTimeOffset.Now + TimeSpan.FromMilliseconds(100), (s, t) => { ++count; return DisposableHelper.EMPTY; }); 33 | 34 | Assert.AreEqual(1, count); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /reactive-extensions-test/SingleAssignmentDisposableTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Disposables; 5 | 6 | namespace akarnokd.reactive_extensions_test 7 | { 8 | [TestFixture] 9 | public class SingleAssignmentDisposableTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | var sad = new akarnokd.reactive_extensions.SingleAssignmentDisposable(); 15 | 16 | Assert.IsNull(sad.Disposable); 17 | 18 | var count = 0; 19 | 20 | sad.Disposable = Disposable.Create(() => count++); 21 | 22 | Assert.IsNotNull(sad.Disposable); 23 | 24 | sad.Dispose(); 25 | 26 | Assert.IsNotNull(sad.Disposable); 27 | 28 | Assert.AreEqual(DisposableHelper.EMPTY, sad.Disposable); 29 | } 30 | 31 | [Test] 32 | public void Multi_Assign() 33 | { 34 | var sad = new akarnokd.reactive_extensions.SingleAssignmentDisposable(); 35 | 36 | sad.Disposable = DisposableHelper.EMPTY; 37 | 38 | var count = 0; 39 | 40 | try 41 | { 42 | sad.Disposable = Disposable.Create(() => count++); 43 | 44 | Assert.Fail(); 45 | } 46 | catch (InvalidOperationException) 47 | { 48 | 49 | } 50 | 51 | Assert.AreEqual(1, count); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /reactive-extensions-test/UnitTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test 6 | { 7 | [TestFixture] 8 | public class UnitTest 9 | { 10 | [Test] 11 | public void TestMethod1() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableDeferTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableDeferTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | var count = 0; 14 | 15 | var c = CompletableSource.Defer(() => 16 | { 17 | count++; 18 | return CompletableSource.Empty(); 19 | }); 20 | 21 | for (int i = 0; i < 100; i++) 22 | { 23 | Assert.AreEqual(i, count); 24 | 25 | c.Test().AssertResult(); 26 | 27 | Assert.AreEqual(i + 1, count); 28 | } 29 | } 30 | 31 | [Test] 32 | public void Supplier_Crash() 33 | { 34 | var c = CompletableSource.Defer(() => 35 | { 36 | throw new InvalidOperationException(); 37 | }); 38 | 39 | c.Test().AssertFailure(typeof(InvalidOperationException)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableEmptyTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableEmptyTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | CompletableSource.Empty() 14 | .Test() 15 | .AssertResult(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableErrorTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | CompletableSource.Error(new InvalidOperationException()) 14 | .Test() 15 | .AssertFailure(typeof(InvalidOperationException)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableHideTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableHideTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | CompletableSource.Empty() 14 | .Hide() 15 | .Test() 16 | .AssertResult(); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | CompletableSource.Error(new InvalidOperationException()) 23 | .Hide() 24 | .Test() 25 | .AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Dispose() 30 | { 31 | var cs = new CompletableSubject(); 32 | 33 | var to = cs.Hide().Test(); 34 | 35 | Assert.True(cs.HasObserver()); 36 | 37 | to.Dispose(); 38 | 39 | Assert.False(cs.HasObserver()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableIgnoreAllElementsTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.completable 7 | { 8 | [TestFixture] 9 | public class CompletableIgnoreAllElementsTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | Observable.Range(1, 5) 15 | .IgnoreAllElements() 16 | .Test() 17 | .AssertResult(); 18 | } 19 | 20 | [Test] 21 | public void Error() 22 | { 23 | Observable.Range(1, 5).ConcatError(new InvalidOperationException()) 24 | .IgnoreAllElements() 25 | .Test() 26 | .AssertFailure(typeof(InvalidOperationException)); 27 | } 28 | 29 | 30 | [Test] 31 | public void Dispose() 32 | { 33 | var us = new UnicastSubject(); 34 | 35 | var to = us 36 | .IgnoreAllElements() 37 | .Test(); 38 | 39 | Assert.True(us.HasObserver()); 40 | 41 | to.Dispose(); 42 | 43 | Assert.False(us.HasObserver()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableNeverTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableNeverTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | CompletableSource.Never() 14 | .Test() 15 | .AssertSubscribed() 16 | .AssertEmpty(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableOnErrorCompleteTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableOnErrorCompleteTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | CompletableSource.Empty() 14 | .OnErrorComplete() 15 | .Test() 16 | .AssertResult(); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | CompletableSource.Error(new InvalidOperationException()) 23 | .OnErrorComplete() 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Dispose() 30 | { 31 | var cs = new CompletableSubject(); 32 | 33 | var to = cs 34 | .OnErrorComplete() 35 | .Test(); 36 | 37 | Assert.True(cs.HasObserver()); 38 | 39 | to.Dispose(); 40 | 41 | Assert.False(cs.HasObserver()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableSubscribeOnTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Threading; 5 | using System.Reactive.Concurrency; 6 | 7 | namespace akarnokd.reactive_extensions_test.completable 8 | { 9 | [TestFixture] 10 | public class CompletableSubscribeOnTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | var name = -1; 16 | 17 | CompletableSource.FromAction(() => 18 | { 19 | name = Thread.CurrentThread.ManagedThreadId; 20 | }) 21 | .SubscribeOn(NewThreadScheduler.Default) 22 | .Test() 23 | .AwaitDone(TimeSpan.FromSeconds(5)) 24 | .AssertResult(); 25 | 26 | Assert.AreNotEqual(-1, name); 27 | Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, name); 28 | } 29 | 30 | [Test] 31 | public void Error() 32 | { 33 | var name = -1; 34 | 35 | CompletableSource.FromAction(() => 36 | { 37 | name = Thread.CurrentThread.ManagedThreadId; 38 | throw new InvalidOperationException(); 39 | }) 40 | .SubscribeOn(NewThreadScheduler.Default) 41 | .Test() 42 | .AwaitDone(TimeSpan.FromSeconds(5)) 43 | .AssertFailure(typeof(InvalidOperationException)); 44 | 45 | Assert.AreNotEqual(-1, name); 46 | Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, name); 47 | } 48 | 49 | [Test] 50 | public void DisposeUpfront() 51 | { 52 | var name = ""; 53 | 54 | CompletableSource.FromAction(() => 55 | { 56 | name = Thread.CurrentThread.Name; 57 | throw new InvalidOperationException(); 58 | }) 59 | .SubscribeOn(NewThreadScheduler.Default) 60 | .Test(true) 61 | .AssertEmpty(); 62 | 63 | Assert.AreEqual("", name); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableTimerTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | 6 | namespace akarnokd.reactive_extensions_test.completable 7 | { 8 | [TestFixture] 9 | public class CompletableTimerTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | CompletableSource.Timer(TimeSpan.FromMilliseconds(100), NewThreadScheduler.Default) 15 | .Test() 16 | .AwaitDone(TimeSpan.FromSeconds(5)) 17 | .AssertResult(); 18 | } 19 | 20 | [Test] 21 | public void Dispose() 22 | { 23 | var sch = new TestScheduler(); 24 | var to = CompletableSource.Timer(TimeSpan.FromMilliseconds(100), sch) 25 | .Test(); 26 | 27 | Assert.True(sch.HasTasks()); 28 | 29 | sch.AdvanceTimeBy(50); 30 | 31 | Assert.True(sch.HasTasks()); 32 | to.AssertEmpty(); 33 | 34 | sch.AdvanceTimeBy(50); 35 | 36 | Assert.False(sch.HasTasks()); 37 | to.AssertResult(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableToMaybeTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableToMaybeTest 9 | { 10 | [Test] 11 | public void Completed_Complete() 12 | { 13 | CompletableSource.Empty() 14 | .ToMaybe() 15 | .Test() 16 | .AssertResult(); 17 | } 18 | 19 | [Test] 20 | public void Completed_Success() 21 | { 22 | CompletableSource.Empty() 23 | .ToMaybe(1) 24 | .Test() 25 | .AssertResult(1); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | CompletableSource.Error(new InvalidOperationException()) 32 | .ToMaybe(1) 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Dispose_Success() 39 | { 40 | var cs = new CompletableSubject(); 41 | 42 | var to = cs 43 | .ToMaybe(1) 44 | .Test(); 45 | 46 | Assert.True(cs.HasObserver()); 47 | 48 | to.Dispose(); 49 | 50 | Assert.False(cs.HasObserver()); 51 | } 52 | 53 | [Test] 54 | public void Dispose_Empty() 55 | { 56 | var cs = new CompletableSubject(); 57 | 58 | var to = cs 59 | .ToMaybe() 60 | .Test(); 61 | 62 | Assert.True(cs.HasObserver()); 63 | 64 | to.Dispose(); 65 | 66 | Assert.False(cs.HasObserver()); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableToObservableTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableToObservableTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | // do not make this var to ensure the target type is correct 14 | IObservable o = CompletableSource.Empty().ToObservable(); 15 | 16 | o.Test().AssertResult(); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | // do not make this var to ensure the target type is correct 23 | IObservable o = CompletableSource.Error(new InvalidOperationException()).ToObservable(); 24 | 25 | o.Test().AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Disposed() 30 | { 31 | var up = new CompletableSubject(); 32 | 33 | IObservable o = up.ToObservable(); 34 | 35 | var to = o.Test(); 36 | 37 | Assert.True(up.HasObserver()); 38 | 39 | to.Dispose(); 40 | 41 | Assert.False(up.HasObserver()); 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableToSingleTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class CompletableToSingleTest 9 | { 10 | [Test] 11 | public void Completed() 12 | { 13 | CompletableSource.Empty() 14 | .ToSingle(1) 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | CompletableSource.Error(new InvalidOperationException()) 23 | .ToSingle(1) 24 | .Test() 25 | .AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Dispose() 30 | { 31 | var cs = new CompletableSubject(); 32 | 33 | var to = cs 34 | .ToSingle(1) 35 | .Test(); 36 | 37 | Assert.True(cs.HasObserver()); 38 | 39 | to.Dispose(); 40 | 41 | Assert.False(cs.HasObserver()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/CompletableUnsubscribeOnTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | using System.Threading; 6 | 7 | namespace akarnokd.reactive_extensions_test.completable 8 | { 9 | [TestFixture] 10 | public class CompletableUnsubscribeOnTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | var name = ""; 16 | 17 | CompletableSource.Empty() 18 | .DoOnDispose(() => name = Thread.CurrentThread.Name) 19 | .UnsubscribeOn(NewThreadScheduler.Default) 20 | .Test() 21 | .AssertResult(); 22 | 23 | Assert.AreEqual("", name); 24 | } 25 | 26 | [Test] 27 | public void Error() 28 | { 29 | var name = ""; 30 | 31 | CompletableSource.Empty() 32 | .DoOnDispose(() => name = Thread.CurrentThread.Name) 33 | .UnsubscribeOn(NewThreadScheduler.Default) 34 | .Test() 35 | .AssertResult(); 36 | 37 | Assert.AreEqual("", name); 38 | } 39 | 40 | [Test] 41 | public void Dispose() 42 | { 43 | var name = -1; 44 | var cdl = new CountdownEvent(1); 45 | 46 | CompletableSource.Never() 47 | .DoOnDispose(() => 48 | { 49 | name = Thread.CurrentThread.ManagedThreadId; 50 | cdl.Signal(); 51 | }) 52 | .UnsubscribeOn(NewThreadScheduler.Default) 53 | .Test() 54 | .Dispose(); 55 | 56 | Assert.True(cdl.Wait(5000)); 57 | 58 | Assert.AreNotEqual(-1, name); 59 | Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, name); 60 | } 61 | 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /reactive-extensions-test/completable/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.completable 6 | { 7 | [TestFixture] 8 | public class UnitTest1 9 | { 10 | [Test] 11 | public void TestMethod1() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeDefaultIfEmptyTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeDefaultIfEmptyTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | MaybeSource.Just(1) 14 | .DefaultIfEmpty(2) 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Empty() 21 | { 22 | MaybeSource.Empty() 23 | .DefaultIfEmpty(2) 24 | .Test() 25 | .AssertResult(2); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | MaybeSource.Error(new InvalidOperationException()) 32 | .DefaultIfEmpty(2) 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Dispose() 39 | { 40 | TestHelper.VerifyDisposeMaybe(m => m.DefaultIfEmpty(1)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeDeferTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeDeferTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | var count = 0; 14 | 15 | var c = MaybeSource.Defer(() => 16 | { 17 | count++; 18 | return MaybeSource.Empty(); 19 | }); 20 | 21 | for (int i = 0; i < 100; i++) 22 | { 23 | Assert.AreEqual(i, count); 24 | 25 | c.Test().AssertResult(); 26 | 27 | Assert.AreEqual(i + 1, count); 28 | } 29 | } 30 | 31 | [Test] 32 | public void Success() 33 | { 34 | var count = 0; 35 | 36 | var c = MaybeSource.Defer(() => 37 | { 38 | count++; 39 | return MaybeSource.Just(count); 40 | }); 41 | 42 | for (int i = 0; i < 100; i++) 43 | { 44 | Assert.AreEqual(i, count); 45 | 46 | c.Test().AssertResult(i + 1); 47 | 48 | Assert.AreEqual(i + 1, count); 49 | } 50 | } 51 | 52 | [Test] 53 | public void Supplier_Crash() 54 | { 55 | var c = MaybeSource.Defer(() => 56 | { 57 | throw new InvalidOperationException(); 58 | }); 59 | 60 | c.Test().AssertFailure(typeof(InvalidOperationException)); 61 | } 62 | 63 | [Test] 64 | public void Dispose() 65 | { 66 | TestHelper.VerifyDisposeMaybe(m => MaybeSource.Defer(() => m)); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeEmptyTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeEmptyTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.Empty() 14 | .Test() 15 | .AssertResult(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeErrorTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.Error(new InvalidOperationException()) 14 | .Test() 15 | .AssertSubscribed() 16 | .AssertFailure(typeof(InvalidOperationException)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeFilterTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeFilterTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.Just(1) 14 | .Filter(v => true) 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Basic_Filtered() 21 | { 22 | MaybeSource.Just(1) 23 | .Filter(v => false) 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | MaybeSource.Error(new InvalidOperationException()) 32 | .Filter(v => true) 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Empty() 39 | { 40 | MaybeSource.Empty() 41 | .Filter(v => true) 42 | .Test() 43 | .AssertResult(); 44 | } 45 | 46 | [Test] 47 | public void Predicate_Crash() 48 | { 49 | MaybeSource.Just(1) 50 | .Filter(v => 51 | { 52 | throw new InvalidOperationException(); 53 | }) 54 | .Test() 55 | .AssertFailure(typeof(InvalidOperationException)); 56 | } 57 | 58 | [Test] 59 | public void Dispose() 60 | { 61 | TestHelper.VerifyDisposeSingle(m => m.Filter(v => true)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeFromFuncTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeFromFuncTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.FromFunc(() => 1) 14 | .Test() 15 | .AssertResult(1); 16 | } 17 | 18 | [Test] 19 | public void Error() 20 | { 21 | MaybeSource.FromFunc(() => { throw new InvalidOperationException(); }) 22 | .Test() 23 | .AssertFailure(typeof(InvalidOperationException)); 24 | } 25 | 26 | [Test] 27 | public void Dispose_Upfront() 28 | { 29 | var count = 0; 30 | 31 | MaybeSource.FromFunc(() => ++count) 32 | .Test(true) 33 | .AssertEmpty(); 34 | 35 | Assert.AreEqual(0, count); 36 | } 37 | 38 | [Test] 39 | public void Dispose_During() 40 | { 41 | var to = new TestObserver(); 42 | 43 | MaybeSource.FromFunc(() => { 44 | to.Dispose(); 45 | return 1; 46 | }) 47 | .SubscribeWith(to) 48 | .AssertEmpty(); 49 | 50 | Assert.True(to.IsDisposed()); 51 | } 52 | 53 | [Test] 54 | public void Dispose_During_Error() 55 | { 56 | var to = new TestObserver(); 57 | 58 | MaybeSource.FromFunc(() => { 59 | to.Dispose(); 60 | throw new InvalidOperationException(); 61 | }) 62 | .SubscribeWith(to) 63 | .AssertEmpty(); 64 | 65 | Assert.True(to.IsDisposed()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeHideTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeHideTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | MaybeSource.Just(1) 14 | .Hide() 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Complete() 21 | { 22 | MaybeSource.Empty() 23 | .Hide() 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | MaybeSource.Error(new InvalidOperationException()) 32 | .Hide() 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Dispose() 39 | { 40 | var cs = new MaybeSubject(); 41 | 42 | var to = cs.Hide().Test(); 43 | 44 | Assert.True(cs.HasObserver()); 45 | 46 | to.Dispose(); 47 | 48 | Assert.False(cs.HasObserver()); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeJustTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeJustTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.Just(1) 14 | .Test() 15 | .AssertResult(1); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeLastElementTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.maybe 7 | { 8 | [TestFixture] 9 | public class MaybeLastElementTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | Observable.Range(1, 5) 15 | .LastElement() 16 | .Test() 17 | .AssertResult(5); 18 | } 19 | 20 | [Test] 21 | public void Empty() 22 | { 23 | Observable.Empty() 24 | .LastElement() 25 | .Test() 26 | .AssertResult(); 27 | } 28 | 29 | [Test] 30 | public void Error() 31 | { 32 | Observable.Throw(new InvalidOperationException()) 33 | .LastElement() 34 | .Test() 35 | .AssertFailure(typeof(InvalidOperationException)); 36 | } 37 | 38 | [Test] 39 | public void Dispose() 40 | { 41 | TestHelper.VerifyDisposeObservable(o => o.LastElement()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeMapTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeMapTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.Just(1) 14 | .Map(v => "" + (v + 1)) 15 | .Test() 16 | .AssertResult("2"); 17 | } 18 | 19 | [Test] 20 | public void Empty() 21 | { 22 | MaybeSource.Empty() 23 | .Map(v => "" + (v + 1)) 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | MaybeSource.Error(new InvalidOperationException()) 32 | .Map(v => "" + (v + 1)) 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Mapper_Crash() 39 | { 40 | MaybeSource.Just(1) 41 | .Map(v => { throw new InvalidOperationException(); }) 42 | .Test() 43 | .AssertFailure(typeof(InvalidOperationException)); 44 | } 45 | 46 | [Test] 47 | public void Dispose() 48 | { 49 | TestHelper.VerifyDisposeMaybe(m => m.Map(v => v)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeNeverTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeNeverTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | MaybeSource.Never() 14 | .Test() 15 | .AssertSubscribed() 16 | .AssertEmpty(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeOnErrorCompleteTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeOnErrorCompleteTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | MaybeSource.Just(1) 14 | .OnErrorComplete() 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Empty() 21 | { 22 | MaybeSource.Empty() 23 | .OnErrorComplete() 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | MaybeSource.Error(new InvalidOperationException()) 32 | .OnErrorComplete() 33 | .Test() 34 | .AssertResult(); 35 | } 36 | 37 | [Test] 38 | public void Dispose() 39 | { 40 | TestHelper.VerifyDisposeMaybe(m => m.OnErrorComplete()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeSingleElementTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.maybe 7 | { 8 | [TestFixture] 9 | public class MaybeSingleElementTest 10 | { 11 | [Test] 12 | public void Basic_Empty() 13 | { 14 | Observable.Empty() 15 | .SingleElement() 16 | .Test() 17 | .AssertResult(); 18 | } 19 | 20 | [Test] 21 | public void Basic_Just() 22 | { 23 | Observable.Return(1) 24 | .SingleElement() 25 | .Test() 26 | .AssertResult(1); 27 | } 28 | 29 | [Test] 30 | public void Basic_Range() 31 | { 32 | Observable.Range(1, 5) 33 | .SingleElement() 34 | .Test() 35 | .AssertFailure(typeof(IndexOutOfRangeException)); 36 | } 37 | 38 | [Test] 39 | public void Error() 40 | { 41 | Observable.Throw(new InvalidOperationException()) 42 | .SingleElement() 43 | .Test() 44 | .AssertFailure(typeof(InvalidOperationException)); 45 | } 46 | 47 | [Test] 48 | public void Dispose() 49 | { 50 | TestHelper.VerifyDisposeObservable(o => o.SingleElement()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeTimerTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | 6 | namespace akarnokd.reactive_extensions_test.maybe 7 | { 8 | [TestFixture] 9 | public class MaybeTimerTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | MaybeSource.Timer(TimeSpan.FromMilliseconds(100), NewThreadScheduler.Default) 15 | .Test() 16 | .AwaitDone(TimeSpan.FromSeconds(5)) 17 | .AssertResult(0L); 18 | } 19 | 20 | [Test] 21 | public void Dispose() 22 | { 23 | var sch = new TestScheduler(); 24 | var to = MaybeSource.Timer(TimeSpan.FromMilliseconds(100), sch) 25 | .Test(); 26 | 27 | Assert.True(sch.HasTasks()); 28 | 29 | sch.AdvanceTimeBy(50); 30 | 31 | Assert.True(sch.HasTasks()); 32 | to.AssertEmpty(); 33 | 34 | sch.AdvanceTimeBy(50); 35 | 36 | Assert.False(sch.HasTasks()); 37 | to.AssertResult(0L); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeToObservableTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeToObservableTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | // do not make this var to ensure the target type is correct 14 | IObservable o = MaybeSource.Empty().ToObservable(); 15 | 16 | o.Test().AssertResult(); 17 | } 18 | 19 | [Test] 20 | public void Success() 21 | { 22 | // do not make this var to ensure the target type is correct 23 | IObservable o = MaybeSource.Just(1).ToObservable(); 24 | 25 | o.Test().AssertResult(1); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | // do not make this var to ensure the target type is correct 32 | IObservable o = MaybeSource.Error(new InvalidOperationException()).ToObservable(); 33 | 34 | o.Test().AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Disposed() 39 | { 40 | var up = new MaybeSubject(); 41 | 42 | IObservable o = up.ToObservable(); 43 | 44 | var to = o.Test(); 45 | 46 | Assert.True(up.HasObserver()); 47 | 48 | to.Dispose(); 49 | 50 | Assert.False(up.HasObserver()); 51 | 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/MaybeToSingleTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class MaybeToSingleTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | ISingleSource src = MaybeSource.Just(1) 14 | .ToSingle(); 15 | 16 | src.Test().AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | ISingleSource src = MaybeSource.Error(new InvalidOperationException()) 23 | .ToSingle(); 24 | 25 | src.Test().AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Empty() 30 | { 31 | ISingleSource src = MaybeSource.Empty() 32 | .ToSingle(); 33 | 34 | src.Test().AssertFailure(typeof(IndexOutOfRangeException)); 35 | } 36 | 37 | [Test] 38 | public void Dispose() 39 | { 40 | TestHelper.VerifyDisposeMaybe(m => m.ToSingle()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /reactive-extensions-test/maybe/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.maybe 6 | { 7 | [TestFixture] 8 | public class UnitTest1 9 | { 10 | [Test] 11 | public void TestMethod1() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/ConcatManyTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | using System.Reactive.Concurrency; 6 | 7 | namespace akarnokd.reactive_extensions_test.observable 8 | { 9 | [TestFixture] 10 | public class ConcatManyTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | new[] 16 | { 17 | Observable.Range(1, 5), 18 | Observable.Range(6, 5) 19 | } 20 | .ToObservable() 21 | .ConcatMany() 22 | .Test() 23 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 24 | } 25 | 26 | [Test] 27 | public void Take() 28 | { 29 | new[] 30 | { 31 | Observable.Range(1, 5), 32 | Observable.Range(6, 5) 33 | } 34 | .ToObservable(NewThreadScheduler.Default) 35 | .ConcatMany() 36 | .Take(7) 37 | .Test() 38 | .AwaitDone(TimeSpan.FromSeconds(5)) 39 | .AssertResult(1, 2, 3, 4, 5, 6, 7); 40 | } 41 | 42 | [Test] 43 | public void Error() 44 | { 45 | new[] 46 | { 47 | Observable.Range(1, 5), 48 | Observable.Throw(new InvalidOperationException()), 49 | Observable.Range(6, 5) 50 | } 51 | .ToObservable() 52 | .ConcatMany() 53 | .Test() 54 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/DoAfterNextTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | using System.Collections.Generic; 6 | 7 | namespace akarnokd.reactive_extensions_test.observable 8 | { 9 | [TestFixture] 10 | public class DoAfterNextTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | var list = new List(); 16 | 17 | var up = new UnicastSubject(); 18 | 19 | var ts = up.Do(v => { list.Add(v); }) 20 | .DoAfterNext(v => { list.Add(-v); }) 21 | .Test(); 22 | 23 | up.EmitAll(1, 2, 3, 4, 5); 24 | 25 | ts.AssertResult(1, 2, 3, 4, 5); 26 | 27 | Assert.AreEqual(new List() { 1, -1, 2, -2, 3, -3, 4, -4, 5, -5 }, list); 28 | } 29 | 30 | [Test] 31 | public void Handler_Crash() 32 | { 33 | var up = new UnicastSubject(); 34 | 35 | var ts = up 36 | .DoAfterNext(v => { throw new InvalidOperationException(); }) 37 | .Test(); 38 | 39 | up.EmitAll(1, 2, 3, 4, 5); 40 | 41 | ts.AssertFailure(typeof(InvalidOperationException), 1); 42 | 43 | Assert.False(up.HasObserver()); 44 | } 45 | 46 | [Test] 47 | public void Basic_Alternative() 48 | { 49 | var list = new List(); 50 | 51 | var up = new UnicastSubject(); 52 | 53 | var ts = up 54 | .DoAfterNext(v => { list.Add(-v); }) 55 | .Do(v => { list.Add(v); }) 56 | .Test(); 57 | 58 | up.EmitAll(1, 2, 3, 4, 5); 59 | 60 | ts.AssertResult(1, 2, 3, 4, 5); 61 | 62 | Assert.AreEqual(new List() { 1, -1, 2, -2, 3, -3, 4, -4, 5, -5 }, list); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/DoAfterTerminateTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Linq; 3 | using NUnit.Framework; 4 | using akarnokd.reactive_extensions; 5 | 6 | namespace akarnokd.reactive_extensions_test.observable 7 | { 8 | [TestFixture] 9 | public class DoAfterTerminateTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | var count = 0; 15 | var v = -1; 16 | 17 | Observable.Range(1, 5) 18 | .DoAfterTerminate(() => count = v) 19 | .Subscribe(u => v = u, e => { }, () => { v = 6; }); 20 | 21 | Assert.AreEqual(6, v); 22 | Assert.AreEqual(6, count); 23 | } 24 | 25 | [Test] 26 | public void Error() 27 | { 28 | var count = 0; 29 | var v = -1; 30 | 31 | Observable.Range(1, 5) 32 | .Concat(Observable.Throw(new InvalidOperationException())) 33 | .DoAfterTerminate(() => count = v) 34 | .Subscribe(u => v = u, e => { v = 6; }); 35 | 36 | Assert.AreEqual(6, v); 37 | Assert.AreEqual(6, count); 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/DoFinallyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observable 6 | { 7 | [TestFixture] 8 | public class DoFinallyTest 9 | { 10 | [Test] 11 | public void Basic_Complete() 12 | { 13 | var count = 0; 14 | 15 | var up = new UnicastSubject(); 16 | 17 | var ts = up.DoFinally(() => count++).Test(); 18 | 19 | up.EmitAll(1, 2, 3, 4, 5); 20 | 21 | ts.AssertResult(1, 2, 3, 4, 5); 22 | ts.Dispose(); 23 | 24 | Assert.AreEqual(1, count); 25 | } 26 | 27 | [Test] 28 | public void Basic_Error() 29 | { 30 | var count = 0; 31 | 32 | var up = new UnicastSubject(); 33 | 34 | var ts = up.DoFinally(() => count++).Test(); 35 | 36 | up.EmitError(new InvalidOperationException(), 1, 2, 3, 4, 5); 37 | 38 | ts.AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5); 39 | ts.Dispose(); 40 | 41 | Assert.AreEqual(1, count); 42 | } 43 | 44 | [Test] 45 | public void Basic_Dispose() 46 | { 47 | var count = 0; 48 | 49 | var up = new UnicastSubject(); 50 | 51 | var ts = up.DoFinally(() => count++).Test(); 52 | 53 | Assert.True(up.HasObserver()); 54 | 55 | ts.Dispose(); 56 | 57 | Assert.False(up.HasObserver()); 58 | 59 | Assert.AreEqual(1, count); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/DoOnDisposeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observable 6 | { 7 | [TestFixture] 8 | public class DoOnDisposeTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | var count = 0; 14 | 15 | var up = new UnicastSubject(); 16 | 17 | var d = up.DoOnDispose(() => count++).Subscribe(); 18 | 19 | Assert.True(up.HasObserver()); 20 | Assert.AreEqual(0, count); 21 | 22 | d.Dispose(); 23 | 24 | Assert.AreEqual(1, count); 25 | 26 | d.Dispose(); 27 | 28 | Assert.AreEqual(1, count); 29 | 30 | Assert.False(up.HasObserver()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/DoOnSubscribeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observable 6 | { 7 | [TestFixture] 8 | public class DoOnSubscribeTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | var count = 0; 14 | 15 | var up = new UnicastSubject(); 16 | 17 | var src = up.DoOnSubscribe(() => count++); 18 | 19 | for (int i = 1; i < 6; i++) { 20 | src.Test(); 21 | Assert.AreEqual(i, count); 22 | } 23 | } 24 | 25 | [Test] 26 | public void Basic_Handler_Crash() 27 | { 28 | var up = new UnicastSubject(); 29 | 30 | var src = up.DoOnSubscribe(() => { throw new InvalidOperationException(); }); 31 | 32 | src.Test().AssertFailure(typeof(InvalidOperationException)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/RepeatPredicateTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.observable 7 | { 8 | [TestFixture] 9 | public class RepeatPredicateTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | var count = 1; 15 | Observable.Return(1) 16 | .Repeat(() => count++ < 5) 17 | .Test() 18 | .AssertResult(1, 1, 1, 1, 1); 19 | } 20 | 21 | [Test] 22 | public void Error() 23 | { 24 | var count = 1; 25 | Observable.Throw(new InvalidOperationException()) 26 | .Repeat(() => count++ < 5) 27 | .Test() 28 | .AssertFailure(typeof(InvalidOperationException)); 29 | } 30 | 31 | 32 | [Test] 33 | public void Predicate_Crash() 34 | { 35 | Observable.Range(1, 5) 36 | .Repeat(() => { throw new InvalidOperationException(); }) 37 | .Test() 38 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5); 39 | } 40 | 41 | [Test] 42 | public void Basic_Long() 43 | { 44 | var count = 1; 45 | Observable.Return(1) 46 | .Repeat(() => count++ < 1000) 47 | .Test() 48 | .AssertValueCount(1000) 49 | .AssertNoError() 50 | .AssertCompleted(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/RetryPredicateTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | using System.Reflection; 6 | 7 | namespace akarnokd.reactive_extensions_test.observable 8 | { 9 | [TestFixture] 10 | public class RetryPredicateTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | Observable.Range(1, 5) 16 | .Retry((e, c) => true) 17 | .Test() 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Test] 22 | public void Basic_Wrong_Error() 23 | { 24 | Observable.Range(1, 5).Concat(Observable.Throw(new NotImplementedException())) 25 | .Retry((e, c) => typeof(InvalidOperationException).IsAssignableFrom(e)) 26 | .Test() 27 | .AssertFailure(typeof(NotImplementedException), 1, 2, 3, 4, 5); 28 | } 29 | 30 | 31 | [Test] 32 | public void Basic_Retry_Twice() 33 | { 34 | Observable.Range(1, 5).Concat(Observable.Throw(new NotImplementedException())) 35 | .Retry((e, c) => c != 2) 36 | .Test() 37 | .AssertFailure(typeof(NotImplementedException), 1, 2, 3, 4, 5, 1, 2, 3, 4, 5); 38 | } 39 | 40 | [Test] 41 | public void Predicate_Crash() 42 | { 43 | var to = Observable.Range(1, 5).Concat(Observable.Throw(new NotImplementedException())) 44 | .Retry((e, c) => { throw new InvalidOperationException(); }) 45 | .Test() 46 | .AssertFailure(typeof(AggregateException), 1, 2, 3, 4, 5) 47 | .AssertCompositeError(0, typeof(NotImplementedException)) 48 | .AssertCompositeError(1, typeof(InvalidOperationException)); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/TakeUntilPredicateTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.observable 7 | { 8 | [TestFixture] 9 | public class TakeUntilPredicatTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | Observable.Range(1, 5) 15 | .TakeUntil(v => false) 16 | .Test() 17 | .AssertResult(1, 2, 3, 4, 5); 18 | } 19 | 20 | [Test] 21 | public void Basic_Stop_Asap() 22 | { 23 | Observable.Range(1, 5) 24 | .TakeUntil(v => true) 25 | .Test() 26 | .AssertResult(1); 27 | } 28 | 29 | [Test] 30 | public void Until_Some_Value() 31 | { 32 | Observable.Range(1, 5) 33 | .TakeUntil(v => v == 3) 34 | .Test() 35 | .AssertResult(1, 2, 3); 36 | } 37 | 38 | [Test] 39 | public void Error() 40 | { 41 | Observable.Range(1, 5).ConcatError(new InvalidOperationException()) 42 | .TakeUntil(v => false) 43 | .Test() 44 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5); 45 | } 46 | 47 | [Test] 48 | public void StopPredicate_Crash() 49 | { 50 | Observable.Range(1, 5) 51 | .TakeUntil(v => { 52 | if (v == 3) 53 | { 54 | throw new InvalidOperationException(); 55 | } 56 | return false; 57 | }) 58 | .Test() 59 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 3); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /reactive-extensions-test/observable/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observable 6 | { 7 | [TestFixture] 8 | public class UnitTest1 9 | { 10 | [Test] 11 | public void TestMethod1() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceComposeTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceComposeTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Just(1) 14 | .Compose(v => v) 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceConcatWithTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceConcatWithTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Range(1, 5).Concat(ObservableSource.Range(6, 5)) 14 | .Test() 15 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 16 | } 17 | 18 | [Test] 19 | public void Error_First() 20 | { 21 | var count = 0; 22 | 23 | ObservableSource.Error(new InvalidOperationException()) 24 | .Concat(ObservableSource.FromFunc(() => ++count)) 25 | .Test() 26 | .AssertFailure(typeof(InvalidOperationException)); 27 | 28 | Assert.AreEqual(0, count); 29 | } 30 | 31 | [Test] 32 | public void Error_Second() 33 | { 34 | var count = 0; 35 | 36 | ObservableSource.Empty() 37 | .Concat(ObservableSource.Error(new InvalidOperationException())) 38 | .Test() 39 | .AssertFailure(typeof(InvalidOperationException)); 40 | 41 | Assert.AreEqual(0, count); 42 | } 43 | 44 | [Test] 45 | public void Dispose_First() 46 | { 47 | TestHelper.VerifyDisposeObservableSource(o => o.Concat(ObservableSource.Never())); 48 | } 49 | 50 | [Test] 51 | public void Dispose_Second() 52 | { 53 | TestHelper.VerifyDisposeObservableSource(o => ObservableSource.Empty().Concat(o)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceDeferTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceDeferTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | var count = 0; 14 | 15 | var source = ObservableSource.Defer(() => ObservableSource.Just(++count)); 16 | 17 | for (int i = 0; i < 10; i++) 18 | { 19 | source.Test().AssertResult(i + 1); 20 | } 21 | 22 | Assert.AreEqual(10, count); 23 | } 24 | 25 | [Test] 26 | public void Supplier_Crash() 27 | { 28 | ObservableSource.Defer(() => throw new InvalidOperationException()) 29 | .Test() 30 | .AssertFailure(typeof(InvalidOperationException)); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceEmptyTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceEmptyTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Empty() 14 | .Test() 15 | .AssertResult(); 16 | } 17 | 18 | [Test] 19 | public void Scalar() 20 | { 21 | var scalar = ObservableSource.Empty() as IStaticValue; 22 | 23 | Assert.AreEqual(default(int), scalar.GetValue(out var success)); 24 | Assert.False(success); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceErrorTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Error(new InvalidOperationException()) 14 | .Test() 15 | .AssertFailure(typeof(InvalidOperationException)); 16 | } 17 | 18 | [Test] 19 | public void Scalar() 20 | { 21 | var scalar = ObservableSource.Error(new InvalidOperationException()) as IDynamicValue; 22 | 23 | try 24 | { 25 | scalar.GetValue(out var success); 26 | Assert.Fail(); 27 | } 28 | catch (InvalidOperationException) 29 | { 30 | // okay 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceFromObservableTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | using System.Reactive.Subjects; 6 | 7 | namespace akarnokd.reactive_extensions_test.observablesource 8 | { 9 | [TestFixture] 10 | public class ObservableSourceFromObservableTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | IObservableSource source = Observable.Range(1, 5).ToObservableSource(); 16 | 17 | source.Test() 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Test] 22 | public void Error() 23 | { 24 | IObservableSource source = Observable.Throw(new InvalidOperationException()) 25 | .ToObservableSource(); 26 | 27 | source.Test() 28 | .AssertFailure(typeof(InvalidOperationException)); 29 | } 30 | 31 | [Test] 32 | public void Dispose() 33 | { 34 | var subj = new Subject(); 35 | 36 | IObservableSource source = subj.ToObservableSource(); 37 | 38 | var to = source.Test(); 39 | 40 | Assert.True(subj.HasObservers); 41 | 42 | to.AssertEmpty(); 43 | 44 | to.Dispose(); 45 | 46 | Assert.False(subj.HasObservers); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceJustTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceJustTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Just(1) 14 | .Test() 15 | .AssertResult(1); 16 | } 17 | 18 | [Test] 19 | public void Scalar() 20 | { 21 | var scalar = ObservableSource.Just(1) as IStaticValue; 22 | 23 | Assert.AreEqual(1, scalar.GetValue(out var success)); 24 | Assert.True(success); 25 | } 26 | 27 | [Test] 28 | public void Fusion_Sync() 29 | { 30 | ObservableSource.Just(1) 31 | .Test(fusionMode: FusionSupport.Sync) 32 | .AssertFuseable() 33 | .AssertFusionMode(FusionSupport.Sync) 34 | .AssertResult(1); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceNeverTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceNeverTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Never() 14 | .Test() 15 | .AssertEmpty(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceOnTerminateDetachTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceOnTerminateDetachTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Range(1, 5) 14 | .OnTerminateDetach() 15 | .Test() 16 | .AssertResult(1, 2, 3, 4, 5); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | ObservableSource.Range(1, 5).ConcatError(new InvalidOperationException()) 23 | .OnTerminateDetach() 24 | .Test() 25 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5); 26 | } 27 | 28 | [Test] 29 | public void Dispose() 30 | { 31 | TestHelper.VerifyDisposeObservableSource(o => o.OnTerminateDetach()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceSkipLastTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceSkipLastTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Range(1, 5) 14 | .SkipLast(2) 15 | .Test() 16 | .AssertResult(1, 2, 3); 17 | } 18 | 19 | [Test] 20 | public void All() 21 | { 22 | ObservableSource.Range(1, 5) 23 | .SkipLast(10) 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void None() 30 | { 31 | ObservableSource.Range(1, 5) 32 | .SkipLast(0) 33 | .Test() 34 | .AssertResult(1, 2, 3, 4, 5); 35 | } 36 | 37 | [Test] 38 | public void Error() 39 | { 40 | ObservableSource.Error(new InvalidOperationException()) 41 | .SkipLast(2) 42 | .Test() 43 | .AssertFailure(typeof(InvalidOperationException)); 44 | } 45 | 46 | [Test] 47 | public void Dispose() 48 | { 49 | TestHelper.VerifyDisposeObservableSource(o => o.SkipLast(2)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceSkipLastTimedTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | 6 | namespace akarnokd.reactive_extensions_test.observablesource 7 | { 8 | [TestFixture] 9 | public class ObservableSourceSkipLastTimedTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | var ts = new TestScheduler(); 15 | 16 | var subj = new PublishSubject(); 17 | 18 | var to = subj.SkipLast(TimeSpan.FromSeconds(5), ts).Test(); 19 | 20 | to.AssertEmpty(); 21 | 22 | subj.OnNext(1); 23 | 24 | ts.AdvanceTimeBy(1000); 25 | 26 | to.AssertEmpty(); 27 | 28 | subj.OnNext(2); 29 | 30 | ts.AdvanceTimeBy(4000); 31 | 32 | subj.OnNext(3); 33 | 34 | to.AssertValuesOnly(1); 35 | 36 | ts.AdvanceTimeBy(1000); 37 | 38 | subj.OnNext(4); 39 | 40 | to.AssertValuesOnly(1, 2); 41 | 42 | subj.OnCompleted(); 43 | 44 | to.AssertResult(1, 2); 45 | } 46 | 47 | [Test] 48 | public void Error() 49 | { 50 | var ts = new TestScheduler(); 51 | 52 | ObservableSource.Range(1, 5).ConcatError(new InvalidOperationException()) 53 | .SkipLast(TimeSpan.FromSeconds(5), ts) 54 | .Test() 55 | .AssertFailure(typeof(InvalidOperationException)); 56 | } 57 | 58 | [Test] 59 | public void Dispose() 60 | { 61 | TestHelper.VerifyDisposeObservableSource(o => o.SkipLast(TimeSpan.FromSeconds(1), CurrentThreadScheduler.Instance)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceSkipWhileTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceSkipWhileTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Range(1, 5) 14 | .SkipWhile(v => v < 3) 15 | .Test() 16 | .AssertResult(3, 4, 5); 17 | } 18 | 19 | [Test] 20 | public void Skip_All() 21 | { 22 | ObservableSource.Range(1, 5) 23 | .SkipWhile(v => v < 6) 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Skip_None() 30 | { 31 | ObservableSource.Range(1, 5) 32 | .SkipWhile(v => v < 1) 33 | .Test() 34 | .AssertResult(1, 2, 3, 4, 5); 35 | } 36 | 37 | [Test] 38 | public void Error() 39 | { 40 | ObservableSource.Error(new InvalidOperationException()) 41 | .SkipWhile(v => v < 1) 42 | .Test() 43 | .AssertFailure(typeof(InvalidOperationException)); 44 | } 45 | 46 | [Test] 47 | public void Predicate_Crash() 48 | { 49 | ObservableSource.Range(1, 5) 50 | .SkipWhile(v => throw new InvalidOperationException()) 51 | .Test() 52 | .AssertFailure(typeof(InvalidOperationException)); 53 | } 54 | 55 | [Test] 56 | public void Dispose() 57 | { 58 | TestHelper.VerifyDisposeObservableSource(o => o.SkipWhile(v => v < 3)); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceTakeTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class ObservableSourceTakeTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | ObservableSource.Range(1, 5) 14 | .Take(3) 15 | .Test() 16 | .AssertResult(1, 2, 3); 17 | } 18 | 19 | [Test] 20 | public void Double() 21 | { 22 | ObservableSource.Range(1, 5) 23 | .Take(3) 24 | .Take(2) 25 | .Test() 26 | .AssertResult(1, 2); 27 | } 28 | 29 | [Test] 30 | public void Zero() 31 | { 32 | ObservableSource.Range(1, 5) 33 | .Take(0) 34 | .Test() 35 | .AssertResult(); 36 | } 37 | 38 | [Test] 39 | public void Exact() 40 | { 41 | ObservableSource.Range(1, 5) 42 | .Take(5) 43 | .Test() 44 | .AssertResult(1, 2, 3, 4, 5); 45 | } 46 | 47 | [Test] 48 | public void Error() 49 | { 50 | ObservableSource.Error(new InvalidOperationException()) 51 | .Take(3) 52 | .Test() 53 | .AssertFailure(typeof(InvalidOperationException)); 54 | } 55 | 56 | [Test] 57 | public void Error_Take_Zero() 58 | { 59 | ObservableSource.Error(new InvalidOperationException()) 60 | .Take(0) 61 | .Test() 62 | .AssertResult(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/ObservableSourceToObservableTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | using System.Reactive.Subjects; 6 | 7 | namespace akarnokd.reactive_extensions_test.observablesource 8 | { 9 | [TestFixture] 10 | public class ObservableSourceToObservableTest 11 | { 12 | [Test] 13 | public void Basic() 14 | { 15 | IObservable source = ObservableSource.Range(1, 5).ToObservable(); 16 | 17 | source.Test() 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Test] 22 | public void Error() 23 | { 24 | IObservable source = ObservableSource.Error(new InvalidOperationException()) 25 | .ToObservable(); 26 | 27 | source.Test() 28 | .AssertFailure(typeof(InvalidOperationException)); 29 | } 30 | 31 | [Test] 32 | public void Dispose() 33 | { 34 | var subj = new PublishSubject(); 35 | 36 | IObservable source = subj.ToObservable(); 37 | 38 | var to = source.Test(); 39 | 40 | Assert.True(subj.HasObservers); 41 | 42 | to.AssertEmpty(); 43 | 44 | to.Dispose(); 45 | 46 | Assert.False(subj.HasObservers); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /reactive-extensions-test/observablesource/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.observablesource 6 | { 7 | [TestFixture] 8 | public class UnitTest1 9 | { 10 | [Test] 11 | public void TestMethod1() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactive-extensions-test/reactive-extensions-test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.0;net461 5 | netcoreapp2.0 6 | 7 | false 8 | true 9 | c:\users\akarnokd\.gradle\akarnokd.snk 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleDeferTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleDeferTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | var count = 0; 14 | 15 | var c = SingleSource.Defer(() => 16 | { 17 | count++; 18 | return SingleSource.Just(count); 19 | }); 20 | 21 | for (int i = 0; i < 100; i++) 22 | { 23 | Assert.AreEqual(i, count); 24 | 25 | c.Test().AssertResult(i + 1); 26 | 27 | Assert.AreEqual(i + 1, count); 28 | } 29 | } 30 | 31 | [Test] 32 | public void Supplier_Crash() 33 | { 34 | var c = SingleSource.Defer(() => 35 | { 36 | throw new InvalidOperationException(); 37 | }); 38 | 39 | c.Test().AssertFailure(typeof(InvalidOperationException)); 40 | } 41 | 42 | [Test] 43 | public void Dispose() 44 | { 45 | TestHelper.VerifyDisposeSingle(m => SingleSource.Defer(() => m)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleErrorTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | SingleSource.Error(new InvalidOperationException()) 14 | .Test() 15 | .AssertSubscribed() 16 | .AssertFailure(typeof(InvalidOperationException)); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleFilterTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleFilterTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | SingleSource.Just(1) 14 | .Filter(v => true) 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Basic_Filtered() 21 | { 22 | SingleSource.Just(1) 23 | .Filter(v => false) 24 | .Test() 25 | .AssertResult(); 26 | } 27 | 28 | [Test] 29 | public void Error() 30 | { 31 | SingleSource.Error(new InvalidOperationException()) 32 | .Filter(v => true) 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Predicate_Crash() 39 | { 40 | SingleSource.Just(1) 41 | .Filter(v => 42 | { 43 | throw new InvalidOperationException(); 44 | }) 45 | .Test() 46 | .AssertFailure(typeof(InvalidOperationException)); 47 | } 48 | 49 | [Test] 50 | public void Dispose() 51 | { 52 | TestHelper.VerifyDisposeSingle(m => m.Filter(v => true)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleFromFuncTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleFromFuncTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | SingleSource.FromFunc(() => 1) 14 | .Test() 15 | .AssertResult(1); 16 | } 17 | 18 | [Test] 19 | public void Error() 20 | { 21 | SingleSource.FromFunc(() => { throw new InvalidOperationException(); }) 22 | .Test() 23 | .AssertFailure(typeof(InvalidOperationException)); 24 | } 25 | 26 | [Test] 27 | public void Dispose_Upfront() 28 | { 29 | var count = 0; 30 | 31 | SingleSource.FromFunc(() => ++count) 32 | .Test(true) 33 | .AssertEmpty(); 34 | 35 | Assert.AreEqual(0, count); 36 | } 37 | 38 | [Test] 39 | public void Dispose_During() 40 | { 41 | var to = new TestObserver(); 42 | 43 | SingleSource.FromFunc(() => { 44 | to.Dispose(); 45 | return 1; 46 | }) 47 | .SubscribeWith(to) 48 | .AssertEmpty(); 49 | 50 | Assert.True(to.IsDisposed()); 51 | } 52 | 53 | [Test] 54 | public void Dispose_During_Error() 55 | { 56 | var to = new TestObserver(); 57 | 58 | SingleSource.FromFunc(() => { 59 | to.Dispose(); 60 | throw new InvalidOperationException(); 61 | }) 62 | .SubscribeWith(to) 63 | .AssertEmpty(); 64 | 65 | Assert.True(to.IsDisposed()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleHideTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleHideTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | SingleSource.Just(1) 14 | .Hide() 15 | .Test() 16 | .AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | SingleSource.Error(new InvalidOperationException()) 23 | .Hide() 24 | .Test() 25 | .AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Dispose() 30 | { 31 | var cs = new SingleSubject(); 32 | 33 | var to = cs.Hide().Test(); 34 | 35 | Assert.True(cs.HasObserver()); 36 | 37 | to.Dispose(); 38 | 39 | Assert.False(cs.HasObserver()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleJustTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleJustTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | SingleSource.Just(1) 14 | .Test() 15 | .AssertResult(1); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleLambdaObserverTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleLambdaObserverTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | var count = 0; 14 | 15 | SingleSource.Just(1) 16 | .Subscribe(v => { count = v; }, e => { count = 2; }); 17 | 18 | Assert.AreEqual(1, count); 19 | } 20 | 21 | [Test] 22 | public void Error() 23 | { 24 | var count = 0; 25 | 26 | SingleSource.Error(new InvalidOperationException()) 27 | .Subscribe(v => { count = v; }, e => { count = 2; }); 28 | 29 | Assert.AreEqual(2, count); 30 | } 31 | 32 | [Test] 33 | public void Dispose() 34 | { 35 | var count = 0; 36 | 37 | var ss = new SingleSubject(); 38 | 39 | var d = ss 40 | .Subscribe(v => { count = v; }, e => { count = 2; }); 41 | 42 | Assert.True(ss.HasObserver()); 43 | 44 | d.Dispose(); 45 | 46 | Assert.False(ss.HasObserver()); 47 | Assert.AreEqual(0, count); 48 | } 49 | 50 | [Test] 51 | public void OnSuccess_Crash() 52 | { 53 | var error = default(Exception); 54 | 55 | SingleSource.Just(1) 56 | .Subscribe(v => throw new InvalidOperationException(), e => error = e); 57 | 58 | Assert.Null(error); 59 | } 60 | 61 | [Test] 62 | public void OnError_Crash() 63 | { 64 | SingleSource.Error(new IndexOutOfRangeException()) 65 | .Subscribe(v => { }, v => throw new InvalidOperationException()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleLastOrDefaultTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.single 7 | { 8 | [TestFixture] 9 | public class SingleLastOrDefaultTest 10 | { 11 | [Test] 12 | public void Last() 13 | { 14 | Observable.Range(1, 5) 15 | .LastOrDefault(-100) 16 | .Test() 17 | .AssertResult(5); 18 | } 19 | 20 | [Test] 21 | public void Empty() 22 | { 23 | Observable.Empty() 24 | .LastOrDefault(-100) 25 | .Test() 26 | .AssertResult(-100); 27 | } 28 | 29 | [Test] 30 | public void Error() 31 | { 32 | Observable.Throw(new InvalidOperationException()) 33 | .LastOrDefault(-100) 34 | .Test() 35 | .AssertFailure(typeof(InvalidOperationException)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleLastOrErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.single 7 | { 8 | [TestFixture] 9 | public class SingleLastOrErrorTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | Observable.Range(1, 5) 15 | .LastOrError() 16 | .Test() 17 | .AssertResult(5); 18 | } 19 | 20 | [Test] 21 | public void Empty() 22 | { 23 | Observable.Empty() 24 | .LastOrError() 25 | .Test() 26 | .AssertFailure(typeof(IndexOutOfRangeException)); 27 | } 28 | 29 | [Test] 30 | public void Error() 31 | { 32 | Observable.Throw(new InvalidOperationException()) 33 | .LastOrError() 34 | .Test() 35 | .AssertFailure(typeof(InvalidOperationException)); 36 | } 37 | 38 | [Test] 39 | public void Dispose() 40 | { 41 | TestHelper.VerifyDisposeObservable(o => o.LastOrError()); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleMapTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleMapTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | SingleSource.Just(1) 14 | .Map(v => "" + (v + 1)) 15 | .Test() 16 | .AssertResult("2"); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | SingleSource.Error(new InvalidOperationException()) 23 | .Map(v => "" + (v + 1)) 24 | .Test() 25 | .AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Mapper_Crash() 30 | { 31 | SingleSource.Just(1) 32 | .Map(v => { throw new InvalidOperationException(); }) 33 | .Test() 34 | .AssertFailure(typeof(InvalidOperationException)); 35 | } 36 | 37 | [Test] 38 | public void Dispose() 39 | { 40 | TestHelper.VerifyDisposeSingle(m => m.Map(v => v)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleNeverTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleNeverTest 9 | { 10 | [Test] 11 | public void Basic() 12 | { 13 | SingleSource.Never() 14 | .Test() 15 | .AssertSubscribed() 16 | .AssertEmpty(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleObserveOnTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | using System.Threading; 6 | 7 | namespace akarnokd.reactive_extensions_test.single 8 | { 9 | [TestFixture] 10 | public class SingleObserveOnTest 11 | { 12 | 13 | [Test] 14 | public void Success() 15 | { 16 | var name = -1; 17 | 18 | SingleSource.Just(1) 19 | .ObserveOn(NewThreadScheduler.Default) 20 | .DoOnSuccess(v => name = Thread.CurrentThread.ManagedThreadId) 21 | .Test() 22 | .AwaitDone(TimeSpan.FromSeconds(5)) 23 | .AssertResult(1); 24 | 25 | Assert.AreNotEqual(-1, name); 26 | Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, name); 27 | } 28 | 29 | [Test] 30 | public void Error() 31 | { 32 | var name = -1; 33 | 34 | SingleSource.Error(new InvalidOperationException()) 35 | .ObserveOn(NewThreadScheduler.Default) 36 | .DoOnError(e => name = Thread.CurrentThread.ManagedThreadId) 37 | .Test() 38 | .AwaitDone(TimeSpan.FromSeconds(5)) 39 | .AssertFailure(typeof(InvalidOperationException)); 40 | 41 | Assert.AreNotEqual(-1, name); 42 | Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, name); 43 | } 44 | 45 | [Test] 46 | public void Dispose() 47 | { 48 | var cs = new SingleSubject(); 49 | 50 | cs.ObserveOn(NewThreadScheduler.Default) 51 | .Test(true) 52 | .AssertEmpty(); 53 | 54 | Assert.False(cs.HasObserver()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleSingleOrDefaultTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.single 7 | { 8 | [TestFixture] 9 | public class SingleSingleOrDefaultTest 10 | { 11 | [Test] 12 | public void Empty() 13 | { 14 | Observable.Empty() 15 | .SingleOrDefault(-100) 16 | .Test() 17 | .AssertResult(-100); 18 | } 19 | 20 | [Test] 21 | public void Just() 22 | { 23 | Observable.Return(1) 24 | .SingleOrDefault(-100) 25 | .Test() 26 | .AssertResult(1); 27 | } 28 | 29 | [Test] 30 | public void Range() 31 | { 32 | Observable.Range(1, 5) 33 | .SingleOrDefault(-100) 34 | .Test() 35 | .AssertFailure(typeof(IndexOutOfRangeException)); 36 | } 37 | 38 | [Test] 39 | public void Error() 40 | { 41 | Observable.Throw(new InvalidOperationException()) 42 | .SingleOrDefault(-100) 43 | .Test() 44 | .AssertFailure(typeof(InvalidOperationException)); 45 | } 46 | 47 | [Test] 48 | public void Error_Last() 49 | { 50 | Observable.Return(1).ConcatError(new InvalidOperationException()) 51 | .SingleOrDefault(-100) 52 | .Test() 53 | .AssertFailure(typeof(InvalidOperationException)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleSingleOrErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Linq; 5 | 6 | namespace akarnokd.reactive_extensions_test.single 7 | { 8 | [TestFixture] 9 | public class SingleSingleOrErrorTest 10 | { 11 | [Test] 12 | public void Basic_Empty() 13 | { 14 | Observable.Empty() 15 | .SingleOrError() 16 | .Test() 17 | .AssertFailure(typeof(IndexOutOfRangeException)); 18 | } 19 | 20 | [Test] 21 | public void Basic_Just() 22 | { 23 | Observable.Return(1) 24 | .SingleOrError() 25 | .Test() 26 | .AssertResult(1); 27 | } 28 | 29 | [Test] 30 | public void Basic_Range() 31 | { 32 | Observable.Range(1, 5) 33 | .SingleOrError() 34 | .Test() 35 | .AssertFailure(typeof(IndexOutOfRangeException)); 36 | } 37 | 38 | [Test] 39 | public void Error() 40 | { 41 | Observable.Throw(new InvalidOperationException()) 42 | .SingleOrError() 43 | .Test() 44 | .AssertFailure(typeof(InvalidOperationException)); 45 | } 46 | 47 | [Test] 48 | public void Dispose() 49 | { 50 | TestHelper.VerifyDisposeObservable(o => o.SingleOrError()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleTimerTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | 6 | namespace akarnokd.reactive_extensions_test.single 7 | { 8 | [TestFixture] 9 | public class SingleTimerTest 10 | { 11 | [Test] 12 | public void Basic() 13 | { 14 | SingleSource.Timer(TimeSpan.FromMilliseconds(100), NewThreadScheduler.Default) 15 | .Test() 16 | .AwaitDone(TimeSpan.FromSeconds(5)) 17 | .AssertResult(0L); 18 | } 19 | 20 | [Test] 21 | public void Dispose() 22 | { 23 | var sch = new TestScheduler(); 24 | var to = SingleSource.Timer(TimeSpan.FromMilliseconds(100), sch) 25 | .Test(); 26 | 27 | Assert.True(sch.HasTasks()); 28 | 29 | sch.AdvanceTimeBy(50); 30 | 31 | Assert.True(sch.HasTasks()); 32 | to.AssertEmpty(); 33 | 34 | sch.AdvanceTimeBy(50); 35 | 36 | Assert.False(sch.HasTasks()); 37 | to.AssertResult(0L); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleToMaybeTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleToMaybeTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | IMaybeSource src = SingleSource.Just(1) 14 | .ToMaybe(); 15 | 16 | src.Test().AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | IMaybeSource src = SingleSource.Error(new InvalidOperationException()) 23 | .ToMaybe(); 24 | 25 | src.Test().AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Dispose() 30 | { 31 | TestHelper.VerifyDisposeSingle(m => m.ToMaybe()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleToObservableTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class SingleToObservableTest 9 | { 10 | [Test] 11 | public void Success() 12 | { 13 | // do not make this var to ensure the target type is correct 14 | IObservable o = SingleSource.Just(1).ToObservable(); 15 | 16 | o.Test().AssertResult(1); 17 | } 18 | 19 | [Test] 20 | public void Error() 21 | { 22 | // do not make this var to ensure the target type is correct 23 | IObservable o = SingleSource.Error(new InvalidOperationException()).ToObservable(); 24 | 25 | o.Test().AssertFailure(typeof(InvalidOperationException)); 26 | } 27 | 28 | [Test] 29 | public void Disposed() 30 | { 31 | var up = new SingleSubject(); 32 | 33 | IObservable o = up.ToObservable(); 34 | 35 | var to = o.Test(); 36 | 37 | Assert.True(up.HasObserver()); 38 | 39 | to.Dispose(); 40 | 41 | Assert.False(up.HasObserver()); 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/SingleUnsubscribeOnTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | using System.Reactive.Concurrency; 5 | using System.Threading; 6 | 7 | namespace akarnokd.reactive_extensions_test.single 8 | { 9 | [TestFixture] 10 | public class SingleUnsubscribeOnTest 11 | { 12 | [Test] 13 | public void Success() 14 | { 15 | var name = ""; 16 | 17 | SingleSource.Just(1) 18 | .DoOnDispose(() => name = Thread.CurrentThread.Name) 19 | .UnsubscribeOn(NewThreadScheduler.Default) 20 | .Test() 21 | .AssertResult(1); 22 | 23 | Assert.AreEqual("", name); 24 | } 25 | 26 | [Test] 27 | public void Error() 28 | { 29 | var name = ""; 30 | 31 | SingleSource.Error(new InvalidOperationException()) 32 | .DoOnDispose(() => name = Thread.CurrentThread.Name) 33 | .UnsubscribeOn(NewThreadScheduler.Default) 34 | .Test() 35 | .AssertFailure(typeof(InvalidOperationException)); 36 | 37 | Assert.AreEqual("", name); 38 | } 39 | 40 | [Test] 41 | public void Dispose() 42 | { 43 | var name = -1; 44 | var cdl = new CountdownEvent(1); 45 | 46 | SingleSource.Never() 47 | .DoOnDispose(() => 48 | { 49 | name = Thread.CurrentThread.ManagedThreadId; 50 | cdl.Signal(); 51 | }) 52 | .UnsubscribeOn(NewThreadScheduler.Default) 53 | .Test() 54 | .Dispose(); 55 | 56 | Assert.True(cdl.Wait(5000)); 57 | 58 | Assert.AreNotEqual(-1, name); 59 | Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, name); 60 | } 61 | 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /reactive-extensions-test/single/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using akarnokd.reactive_extensions; 4 | 5 | namespace akarnokd.reactive_extensions_test.single 6 | { 7 | [TestFixture] 8 | public class UnitTest1 9 | { 10 | [Test] 11 | public void TestMethod1() 12 | { 13 | 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /reactive-extensions-test/tools/FailingEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace akarnokd.reactive_extensions_test 9 | { 10 | /// 11 | /// An enumerable/enumerator that is empty but can fail at 12 | /// various calls. 13 | /// 14 | /// The element type. 15 | /// Since 0.0.11 16 | public sealed class FailingEnumerable : IEnumerable, IEnumerator 17 | { 18 | readonly bool failGetEnumerable; 19 | 20 | readonly bool failMoveNext; 21 | 22 | readonly bool failOnDispose; 23 | 24 | public FailingEnumerable(bool failGetEnumerable, bool failMoveNext, bool failOnDispose) 25 | { 26 | this.failGetEnumerable = failGetEnumerable; 27 | this.failMoveNext = failMoveNext; 28 | this.failOnDispose = failOnDispose; 29 | } 30 | 31 | public T Current => default(T); 32 | 33 | object IEnumerator.Current => null; 34 | 35 | public void Dispose() 36 | { 37 | if (failOnDispose) 38 | { 39 | throw new InvalidOperationException(); 40 | } 41 | } 42 | 43 | public IEnumerator GetEnumerator() 44 | { 45 | if (failGetEnumerable) 46 | { 47 | throw new InvalidOperationException(); 48 | } 49 | return this; 50 | } 51 | 52 | public bool MoveNext() 53 | { 54 | if (failMoveNext) 55 | { 56 | throw new InvalidOperationException(); 57 | } 58 | return false; 59 | } 60 | 61 | public void Reset() 62 | { 63 | throw new NotSupportedException(); 64 | } 65 | 66 | IEnumerator IEnumerable.GetEnumerator() 67 | { 68 | return GetEnumerator(); 69 | } 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /reactive-extensions/IObservableEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Abstraction over an with 9 | /// the ability to check for a disposed state and 10 | /// register a disposable resource with it. 11 | /// 12 | /// The element type of the sequence. 13 | /// Since 0.0.5 14 | public interface IObservableEmitter : IObserver 15 | { 16 | /// 17 | /// Returns true if the downstream has disposed the sequence. 18 | /// 19 | /// True if the downstream has disposed the sequence. 20 | bool IsDisposed(); 21 | 22 | /// 23 | /// Associates a disposable resource with this emitter, 24 | /// disposing any previously set resource. The resource is 25 | /// then automatically disposed after the emitter has 26 | /// terminated or the downstream disposes the sequence. 27 | /// 28 | /// The resource to associate 29 | void SetResource(IDisposable resource); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /reactive-extensions/ISubjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Standard state-peeking, thread-safe methods for all subject types. 9 | /// 10 | /// Since 0.0.6 11 | public interface ISubjectExtensions 12 | { 13 | /// 14 | /// Returns true if there is at least one observer currently subscribed to 15 | /// this ISubject. 16 | /// 17 | /// True if there is an observer currently subscribed. 18 | bool HasObserver(); 19 | 20 | /// 21 | /// Returns true if this subject terminated normally. 22 | /// 23 | /// True if this subject terminated normally. 24 | bool HasCompleted(); 25 | 26 | /// 27 | /// Returns true if this subject terminated with an error. 28 | /// 29 | /// True if this subject terminated with an error. 30 | bool HasException(); 31 | 32 | /// 33 | /// Returns the terminal exception, if any. 34 | /// 35 | /// The terminal exception or null if the subject has not yet terminated or not with an error. 36 | Exception GetException(); 37 | 38 | /// 39 | /// Returns true if the subject has any observers. 40 | /// 41 | /// Since 0.0.17 42 | bool HasObservers { get; } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableDefer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Defers the creation of the actual completable source 9 | /// provided by a supplier function until a completable observer completes. 10 | /// 11 | /// Since 0.0.6 12 | internal sealed class CompletableDefer : ICompletableSource 13 | { 14 | readonly Func supplier; 15 | 16 | public CompletableDefer(Func supplier) 17 | { 18 | this.supplier = supplier; 19 | } 20 | 21 | public void Subscribe(ICompletableObserver observer) 22 | { 23 | var c = default(ICompletableSource); 24 | try 25 | { 26 | c = ValidationHelper.RequireNonNullRef(supplier(), "The supplier returned a null ICompletableSource"); 27 | } 28 | catch (Exception ex) 29 | { 30 | DisposableHelper.Error(observer, ex); 31 | return; 32 | } 33 | 34 | c.Subscribe(observer); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableEmpty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Completes immediately. 9 | /// 10 | internal sealed class CompletableEmpty : ICompletableSource 11 | { 12 | internal static readonly ICompletableSource INSTANCE = new CompletableEmpty(); 13 | 14 | public void Subscribe(ICompletableObserver observer) 15 | { 16 | observer.Complete(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Signals an exception immediately. 9 | /// 10 | internal sealed class CompletableError : ICompletableSource 11 | { 12 | readonly Exception error; 13 | 14 | public CompletableError(Exception error) 15 | { 16 | this.error = error; 17 | } 18 | 19 | public void Subscribe(ICompletableObserver observer) 20 | { 21 | observer.Error(error); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableFromAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Wraps and calls the given action for each individual 9 | /// completable observer then completes or fails the observer 10 | /// depending on the action completes normally or threw an exception. 11 | /// 12 | /// Since 0.0.6 13 | internal sealed class CompletableFromAction : ICompletableSource 14 | { 15 | readonly Action action; 16 | 17 | public CompletableFromAction(Action action) 18 | { 19 | this.action = action; 20 | } 21 | 22 | public void Subscribe(ICompletableObserver observer) 23 | { 24 | var bd = new BooleanDisposable(); 25 | observer.OnSubscribe(bd); 26 | 27 | if (bd.IsDisposed()) 28 | { 29 | return; 30 | } 31 | 32 | try 33 | { 34 | action(); 35 | } 36 | catch (Exception ex) 37 | { 38 | if (!bd.IsDisposed()) 39 | { 40 | observer.OnError(ex); 41 | } 42 | return; 43 | } 44 | 45 | if (!bd.IsDisposed()) 46 | { 47 | observer.OnCompleted(); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableHide.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Hides the identity of the upstream source and disposable. 9 | /// 10 | /// Since 0.0.9 11 | internal sealed class CompletableHide : ICompletableSource 12 | { 13 | readonly ICompletableSource source; 14 | 15 | public CompletableHide(ICompletableSource source) 16 | { 17 | this.source = source; 18 | } 19 | 20 | public void Subscribe(ICompletableObserver observer) 21 | { 22 | source.Subscribe(new HideObserver(observer)); 23 | } 24 | 25 | sealed class HideObserver : ICompletableObserver, IDisposable 26 | { 27 | readonly ICompletableObserver downstream; 28 | 29 | IDisposable upstream; 30 | 31 | public HideObserver(ICompletableObserver downstream) 32 | { 33 | this.downstream = downstream; 34 | } 35 | 36 | public void Dispose() 37 | { 38 | upstream.Dispose(); 39 | } 40 | 41 | public void OnCompleted() 42 | { 43 | downstream.OnCompleted(); 44 | } 45 | 46 | public void OnError(Exception error) 47 | { 48 | downstream.OnError(error); 49 | } 50 | 51 | public void OnSubscribe(IDisposable d) 52 | { 53 | upstream = d; 54 | downstream.OnSubscribe(this); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableInnerObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | /// 6 | /// A completable observer with a thread-safe deferred 7 | /// OnSubscribe and Dispose support. 8 | /// 9 | /// Since 0.0.8 10 | sealed class CompletableInnerObserver : ICompletableObserver, IDisposable 11 | { 12 | readonly ICompletableObserver downstream; 13 | 14 | IDisposable upstream; 15 | 16 | public CompletableInnerObserver(ICompletableObserver downstream) 17 | { 18 | this.downstream = downstream; 19 | } 20 | 21 | public void Dispose() 22 | { 23 | DisposableHelper.Dispose(ref upstream); 24 | } 25 | 26 | public void OnCompleted() 27 | { 28 | downstream.OnCompleted(); 29 | } 30 | 31 | public void OnError(Exception error) 32 | { 33 | downstream.OnError(error); 34 | } 35 | 36 | public void OnSubscribe(IDisposable d) 37 | { 38 | DisposableHelper.SetOnce(ref upstream, d); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableLambdaObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Calls the specific action callbacks upon receiving the terminal signals. 9 | /// 10 | /// Since 0.0.6 11 | internal sealed class CompletableLambdaObserver : ICompletableObserver, IDisposable 12 | { 13 | readonly Action onCompleted; 14 | 15 | readonly Action onError; 16 | 17 | IDisposable upstream; 18 | 19 | public CompletableLambdaObserver(Action onCompleted, Action onError) 20 | { 21 | this.onCompleted = onCompleted; 22 | this.onError = onError; 23 | } 24 | 25 | public void Dispose() 26 | { 27 | DisposableHelper.Dispose(ref upstream); 28 | } 29 | 30 | public void OnCompleted() 31 | { 32 | DisposableHelper.WeakDispose(ref upstream); 33 | try 34 | { 35 | onCompleted?.Invoke(); 36 | } 37 | catch (Exception) 38 | { 39 | // FIXME nowhere to put these 40 | } 41 | } 42 | 43 | public void OnError(Exception error) 44 | { 45 | DisposableHelper.WeakDispose(ref upstream); 46 | try 47 | { 48 | onError?.Invoke(error); 49 | } 50 | catch (Exception) 51 | { 52 | // FIXME nowhere to put these 53 | } 54 | } 55 | 56 | public void OnSubscribe(IDisposable d) 57 | { 58 | DisposableHelper.SetOnce(ref upstream, d); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableNever.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Never terminates. 9 | /// 10 | internal sealed class CompletableNever : ICompletableSource 11 | { 12 | internal static readonly ICompletableSource INSTANCE = new CompletableNever(); 13 | 14 | public void Subscribe(ICompletableObserver observer) 15 | { 16 | observer.OnSubscribe(DisposableHelper.EMPTY); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableOnErrorComplete.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Suppresses an upstream error and completes the completable observer 9 | /// instead. 10 | /// 11 | /// Since 0.0.8 12 | internal sealed class CompletableOnErrorComplete : ICompletableSource 13 | { 14 | readonly ICompletableSource source; 15 | 16 | public CompletableOnErrorComplete(ICompletableSource source) 17 | { 18 | this.source = source; 19 | } 20 | 21 | public void Subscribe(ICompletableObserver observer) 22 | { 23 | source.Subscribe(new OnErrorCompleteObserver(observer)); 24 | } 25 | 26 | sealed class OnErrorCompleteObserver : ICompletableObserver, IDisposable 27 | { 28 | readonly ICompletableObserver downstream; 29 | 30 | IDisposable upstream; 31 | 32 | public OnErrorCompleteObserver(ICompletableObserver downstream) 33 | { 34 | this.downstream = downstream; 35 | } 36 | 37 | public void Dispose() 38 | { 39 | upstream.Dispose(); 40 | } 41 | 42 | public void OnCompleted() 43 | { 44 | downstream.OnCompleted(); 45 | } 46 | 47 | public void OnError(Exception error) 48 | { 49 | downstream.OnCompleted(); 50 | } 51 | 52 | public void OnSubscribe(IDisposable d) 53 | { 54 | upstream = d; 55 | downstream.OnSubscribe(this); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableRedoObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace akarnokd.reactive_extensions 5 | { 6 | /// 7 | /// Abstract base class for repeatedly subscribing to a source 8 | /// upon a call to . 9 | /// Override or 10 | /// to perform this for the appropriate event. 11 | /// 12 | internal abstract class CompletableRedoObserver : ICompletableObserver, IDisposable 13 | { 14 | protected readonly ICompletableObserver downstream; 15 | 16 | readonly ICompletableSource source; 17 | 18 | IDisposable upstream; 19 | 20 | int wip; 21 | 22 | public CompletableRedoObserver(ICompletableObserver downstream, ICompletableSource source) 23 | { 24 | this.downstream = downstream; 25 | this.source = source; 26 | } 27 | 28 | public void Dispose() 29 | { 30 | DisposableHelper.Dispose(ref upstream); 31 | } 32 | 33 | public virtual void OnCompleted() 34 | { 35 | downstream.OnCompleted(); 36 | } 37 | 38 | public virtual void OnError(Exception error) 39 | { 40 | downstream.OnError(error); 41 | } 42 | 43 | public void OnSubscribe(IDisposable d) 44 | { 45 | DisposableHelper.Replace(ref upstream, d); 46 | } 47 | 48 | internal void Drain() 49 | { 50 | if (Interlocked.Increment(ref wip) == 1) 51 | { 52 | for (; ; ) 53 | { 54 | if (!DisposableHelper.IsDisposed(ref upstream)) 55 | { 56 | source.Subscribe(this); 57 | } 58 | 59 | if (Interlocked.Decrement(ref wip) == 0) 60 | { 61 | break; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableSafeObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Makes sure downstream exceptions are suppressed. 9 | /// 10 | internal sealed class CompletableSafeObserver : ICompletableObserver, IDisposable 11 | { 12 | readonly ICompletableObserver downstream; 13 | 14 | IDisposable upstream; 15 | 16 | public CompletableSafeObserver(ICompletableObserver downstream) 17 | { 18 | this.downstream = downstream; 19 | } 20 | 21 | public void Dispose() 22 | { 23 | upstream.Dispose(); 24 | } 25 | 26 | public void OnCompleted() 27 | { 28 | try 29 | { 30 | downstream.OnCompleted(); 31 | } 32 | catch (Exception) 33 | { 34 | // TODO what should happen with these? 35 | } 36 | } 37 | 38 | public void OnError(Exception error) 39 | { 40 | try 41 | { 42 | downstream.OnError(error); 43 | } 44 | catch (Exception) 45 | { 46 | // TODO what should happen with these? 47 | } 48 | } 49 | 50 | public void OnSubscribe(IDisposable d) 51 | { 52 | upstream = d; 53 | try 54 | { 55 | downstream.OnSubscribe(this); 56 | } 57 | catch (Exception) 58 | { 59 | d.Dispose(); 60 | // TODO what should happen with these? 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Concurrency; 4 | using System.Text; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | /// 9 | /// Completes after a specified time elapsed on the given scheduler. 10 | /// 11 | /// Since 0.0.6 12 | internal sealed class CompletableTimer : ICompletableSource 13 | { 14 | readonly TimeSpan time; 15 | 16 | readonly IScheduler scheduler; 17 | 18 | static readonly Func COMPLETE = 19 | (s, o) => { o.OnCompleted(); return DisposableHelper.EMPTY; }; 20 | 21 | public CompletableTimer(TimeSpan time, IScheduler scheduler) 22 | { 23 | this.time = time; 24 | this.scheduler = scheduler; 25 | } 26 | 27 | public void Subscribe(ICompletableObserver observer) 28 | { 29 | var sad = new SingleAssignmentDisposable(); 30 | observer.OnSubscribe(sad); 31 | 32 | sad.Disposable = scheduler.Schedule(observer, time, COMPLETE); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableToObservable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Exposes a completable source as a legacy observable. 9 | /// 10 | /// The element type of the observable sequence. 11 | /// Since 0.0.6 12 | internal sealed class CompletableToObservable : IObservable 13 | { 14 | readonly ICompletableSource source; 15 | 16 | public CompletableToObservable(ICompletableSource source) 17 | { 18 | this.source = source; 19 | } 20 | 21 | public IDisposable Subscribe(IObserver observer) 22 | { 23 | var parent = new ToObservableObserver(observer); 24 | source.Subscribe(parent); 25 | return parent; 26 | } 27 | 28 | internal sealed class ToObservableObserver : ICompletableObserver, IDisposable 29 | { 30 | readonly IObserver downstream; 31 | 32 | IDisposable upstream; 33 | 34 | public ToObservableObserver(IObserver downstream) 35 | { 36 | this.downstream = downstream; 37 | } 38 | 39 | public void Dispose() 40 | { 41 | DisposableHelper.Dispose(ref upstream); 42 | } 43 | 44 | public void OnCompleted() 45 | { 46 | DisposableHelper.WeakDispose(ref upstream); 47 | downstream.OnCompleted(); 48 | } 49 | 50 | public void OnError(Exception error) 51 | { 52 | DisposableHelper.WeakDispose(ref upstream); 53 | downstream.OnError(error); 54 | } 55 | 56 | public void OnSubscribe(IDisposable d) 57 | { 58 | DisposableHelper.SetOnce(ref upstream, d); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /reactive-extensions/completable/CompletableToTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | /// 10 | /// Subscribe to a completable source and expose the terminal 11 | /// signal as a . 12 | /// 13 | /// Since 0.0.9 14 | internal sealed class CompletableToTask : ICompletableObserver 15 | { 16 | readonly TaskCompletionSource tcs; 17 | 18 | public Task Task { get { return tcs.Task; } } 19 | 20 | IDisposable upstream; 21 | 22 | CancellationTokenRegistration reg; 23 | 24 | bool hasTokenSource; 25 | 26 | public CompletableToTask() 27 | { 28 | tcs = new TaskCompletionSource(); 29 | } 30 | 31 | internal void Init(CancellationTokenSource cts) 32 | { 33 | if (cts != null) 34 | { 35 | reg = cts.Token.Register(Dispose); 36 | hasTokenSource = true; 37 | } 38 | } 39 | 40 | public void OnCompleted() 41 | { 42 | tcs.TrySetResult(null); 43 | if (hasTokenSource) 44 | { 45 | reg.Dispose(); 46 | } 47 | } 48 | 49 | public void OnError(Exception error) 50 | { 51 | tcs.TrySetException(error); 52 | if (hasTokenSource) 53 | { 54 | reg.Dispose(); 55 | } 56 | } 57 | 58 | public void OnSubscribe(IDisposable d) 59 | { 60 | DisposableHelper.SetOnce(ref upstream, d); 61 | } 62 | 63 | void Dispose() 64 | { 65 | DisposableHelper.Dispose(ref upstream); 66 | tcs.TrySetCanceled(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeDefer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Defers the creation of the actual maybe source 9 | /// provided by a supplier function until a maybe observer completes. 10 | /// 11 | /// The success value type 12 | /// Since 0.0.11 13 | internal sealed class MaybeDefer : IMaybeSource 14 | { 15 | readonly Func> supplier; 16 | 17 | public MaybeDefer(Func> supplier) 18 | { 19 | this.supplier = supplier; 20 | } 21 | 22 | public void Subscribe(IMaybeObserver observer) 23 | { 24 | var c = default(IMaybeSource); 25 | try 26 | { 27 | c = ValidationHelper.RequireNonNullRef(supplier(), "The supplier returned a null IMaybeSource"); 28 | } 29 | catch (Exception ex) 30 | { 31 | DisposableHelper.Error(observer, ex); 32 | return; 33 | } 34 | 35 | c.Subscribe(observer); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeEmpty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Completes immediately. 9 | /// 10 | internal sealed class MaybeEmpty : IMaybeSource 11 | { 12 | internal static readonly IMaybeSource INSTANCE = new MaybeEmpty(); 13 | 14 | public void Subscribe(IMaybeObserver observer) 15 | { 16 | observer.Complete(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Signals an exception immediately. 9 | /// 10 | internal sealed class MaybeError : IMaybeSource 11 | { 12 | readonly Exception error; 13 | 14 | public MaybeError(Exception error) 15 | { 16 | this.error = error; 17 | } 18 | 19 | public void Subscribe(IMaybeObserver observer) 20 | { 21 | observer.Error(error); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeFromAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Wraps and calls the given action for each individual 9 | /// maybe observer then completes or fails the observer 10 | /// depending on the action completes normally or threw an exception. 11 | /// 12 | /// Since 0.0.11 13 | internal sealed class MaybeFromAction : IMaybeSource 14 | { 15 | readonly Action action; 16 | 17 | public MaybeFromAction(Action action) 18 | { 19 | this.action = action; 20 | } 21 | 22 | public void Subscribe(IMaybeObserver observer) 23 | { 24 | var bd = new BooleanDisposable(); 25 | observer.OnSubscribe(bd); 26 | 27 | if (bd.IsDisposed()) 28 | { 29 | return; 30 | } 31 | 32 | try 33 | { 34 | action(); 35 | } 36 | catch (Exception ex) 37 | { 38 | if (!bd.IsDisposed()) 39 | { 40 | observer.OnError(ex); 41 | } 42 | return; 43 | } 44 | 45 | if (!bd.IsDisposed()) 46 | { 47 | observer.OnCompleted(); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeFromFunc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Wraps and runs a function for each incoming 9 | /// maybe observer and signals the value returned 10 | /// by the function as the success event. 11 | /// 12 | /// The value type. 13 | /// Since 0.0.11 14 | internal sealed class MaybeFromFunc : IMaybeSource 15 | { 16 | readonly Func func; 17 | 18 | public MaybeFromFunc(Func func) 19 | { 20 | this.func = func; 21 | } 22 | 23 | public void Subscribe(IMaybeObserver observer) 24 | { 25 | var d = new BooleanDisposable(); 26 | observer.OnSubscribe(d); 27 | 28 | if (d.IsDisposed()) 29 | { 30 | return; 31 | } 32 | 33 | var v = default(T); 34 | 35 | try 36 | { 37 | v = func(); 38 | } 39 | catch (Exception ex) 40 | { 41 | if (!d.IsDisposed()) 42 | { 43 | observer.OnError(ex); 44 | } 45 | return; 46 | } 47 | 48 | if (!d.IsDisposed()) 49 | { 50 | observer.OnSuccess(v); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeHide.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Hides the identity of the upstream source and disposable. 9 | /// 10 | /// Since 0.0.9 11 | internal sealed class MaybeHide : IMaybeSource 12 | { 13 | readonly IMaybeSource source; 14 | 15 | public MaybeHide(IMaybeSource source) 16 | { 17 | this.source = source; 18 | } 19 | 20 | public void Subscribe(IMaybeObserver observer) 21 | { 22 | source.Subscribe(new HideObserver(observer)); 23 | } 24 | 25 | sealed class HideObserver : IMaybeObserver, IDisposable 26 | { 27 | readonly IMaybeObserver downstream; 28 | 29 | IDisposable upstream; 30 | 31 | public HideObserver(IMaybeObserver downstream) 32 | { 33 | this.downstream = downstream; 34 | } 35 | 36 | public void Dispose() 37 | { 38 | upstream.Dispose(); 39 | } 40 | 41 | public void OnCompleted() 42 | { 43 | downstream.OnCompleted(); 44 | } 45 | 46 | public void OnError(Exception error) 47 | { 48 | downstream.OnError(error); 49 | } 50 | 51 | public void OnSubscribe(IDisposable d) 52 | { 53 | upstream = d; 54 | downstream.OnSubscribe(this); 55 | } 56 | 57 | public void OnSuccess(T item) 58 | { 59 | downstream.OnSuccess(item); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeInnerObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | /// 6 | /// A maybe observer with a thread-safe deferred 7 | /// OnSubscribe and Dispose support. 8 | /// 9 | /// The success value type. 10 | /// Since 0.0.11 11 | internal sealed class MaybeInnerObserver : IMaybeObserver, IDisposable 12 | { 13 | readonly IMaybeObserver downstream; 14 | 15 | IDisposable upstream; 16 | 17 | public MaybeInnerObserver(IMaybeObserver downstream) 18 | { 19 | this.downstream = downstream; 20 | } 21 | 22 | public void Dispose() 23 | { 24 | DisposableHelper.Dispose(ref upstream); 25 | } 26 | 27 | public void OnCompleted() 28 | { 29 | downstream.OnCompleted(); 30 | } 31 | 32 | public void OnError(Exception error) 33 | { 34 | downstream.OnError(error); 35 | } 36 | 37 | public void OnSubscribe(IDisposable d) 38 | { 39 | DisposableHelper.SetOnce(ref upstream, d); 40 | } 41 | 42 | public void OnSuccess(T item) 43 | { 44 | downstream.OnSuccess(item); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeJust.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Succeed with an item. 9 | /// 10 | /// The type of the item. 11 | /// Since 0.0.9 12 | internal sealed class MaybeJust : IMaybeSource 13 | { 14 | readonly T item; 15 | 16 | public MaybeJust(T item) 17 | { 18 | this.item = item; 19 | } 20 | 21 | public void Subscribe(IMaybeObserver observer) 22 | { 23 | observer.OnSubscribe(DisposableHelper.EMPTY); 24 | observer.OnSuccess(item); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeNever.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Never terminates. 9 | /// 10 | internal sealed class MaybeNever : IMaybeSource 11 | { 12 | internal static readonly IMaybeSource INSTANCE = new MaybeNever(); 13 | 14 | public void Subscribe(IMaybeObserver observer) 15 | { 16 | observer.OnSubscribe(DisposableHelper.EMPTY); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeOnErrorComplete.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Suppresses an upstream error and completes the maybe observer 9 | /// instead. 10 | /// 11 | /// The success value type. 12 | /// Since 0.0.11 13 | internal sealed class MaybeOnErrorComplete : IMaybeSource 14 | { 15 | readonly IMaybeSource source; 16 | 17 | public MaybeOnErrorComplete(IMaybeSource source) 18 | { 19 | this.source = source; 20 | } 21 | 22 | public void Subscribe(IMaybeObserver observer) 23 | { 24 | source.Subscribe(new OnErrorCompleteObserver(observer)); 25 | } 26 | 27 | sealed class OnErrorCompleteObserver : IMaybeObserver, IDisposable 28 | { 29 | readonly IMaybeObserver downstream; 30 | 31 | IDisposable upstream; 32 | 33 | public OnErrorCompleteObserver(IMaybeObserver downstream) 34 | { 35 | this.downstream = downstream; 36 | } 37 | 38 | public void Dispose() 39 | { 40 | upstream.Dispose(); 41 | } 42 | 43 | public void OnCompleted() 44 | { 45 | downstream.OnCompleted(); 46 | } 47 | 48 | public void OnError(Exception error) 49 | { 50 | downstream.OnCompleted(); 51 | } 52 | 53 | public void OnSubscribe(IDisposable d) 54 | { 55 | upstream = d; 56 | downstream.OnSubscribe(this); 57 | } 58 | 59 | public void OnSuccess(T item) 60 | { 61 | downstream.OnSuccess(item); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Concurrency; 4 | using System.Text; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | /// 9 | /// Signals 0L after a specified time elapsed on the given scheduler. 10 | /// 11 | /// Since 0.0.11 12 | internal sealed class MaybeTimer : IMaybeSource 13 | { 14 | readonly TimeSpan time; 15 | 16 | readonly IScheduler scheduler; 17 | 18 | static readonly Func, IDisposable> COMPLETE = 19 | (s, o) => { o.OnSuccess(0L); return DisposableHelper.EMPTY; }; 20 | 21 | public MaybeTimer(TimeSpan time, IScheduler scheduler) 22 | { 23 | this.time = time; 24 | this.scheduler = scheduler; 25 | } 26 | 27 | public void Subscribe(IMaybeObserver observer) 28 | { 29 | var sad = new SingleAssignmentDisposable(); 30 | observer.OnSubscribe(sad); 31 | 32 | sad.Disposable = scheduler.Schedule(observer, time, COMPLETE); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /reactive-extensions/maybe/MaybeToSingle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Converts a maybe source into a single source, 9 | /// failing with an index out-of-range exception 10 | /// if the maybe source is empty 11 | /// 12 | /// The element type of the maybe source. 13 | /// Since 0.0.12 14 | internal sealed class MaybeToSingle : ISingleSource 15 | { 16 | readonly IMaybeSource source; 17 | 18 | public MaybeToSingle(IMaybeSource source) 19 | { 20 | this.source = source; 21 | } 22 | 23 | public void Subscribe(ISingleObserver observer) 24 | { 25 | source.Subscribe(new ToObservableObserver(observer)); 26 | } 27 | 28 | internal sealed class ToObservableObserver : IMaybeObserver, IDisposable 29 | { 30 | readonly ISingleObserver downstream; 31 | 32 | IDisposable upstream; 33 | 34 | public ToObservableObserver(ISingleObserver downstream) 35 | { 36 | this.downstream = downstream; 37 | } 38 | 39 | public void Dispose() 40 | { 41 | upstream.Dispose(); 42 | } 43 | 44 | public void OnCompleted() 45 | { 46 | downstream.OnError(new IndexOutOfRangeException("Empty IMaybeSource")); 47 | } 48 | 49 | public void OnError(Exception error) 50 | { 51 | downstream.OnError(error); 52 | } 53 | 54 | public void OnSuccess(T item) 55 | { 56 | downstream.OnSuccess(item); 57 | } 58 | 59 | public void OnSubscribe(IDisposable d) 60 | { 61 | upstream = d; 62 | downstream.OnSubscribe(this); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /reactive-extensions/observable/BaseObservable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | /// 6 | /// An abstract observable that hosts an upstream source observable, 7 | /// specifies a default Subscribe() behavior and allows creating a 8 | /// BaseObserver via a method to be implemented by subclasses. 9 | /// 10 | /// The upstream value type. 11 | /// The downstream value type 12 | internal abstract class BaseObservable : IObservable 13 | { 14 | protected readonly IObservable source; 15 | 16 | internal BaseObservable(IObservable source) 17 | { 18 | this.source = source; 19 | } 20 | 21 | public virtual IDisposable Subscribe(IObserver observer) 22 | { 23 | var parent = CreateObserver(observer); 24 | var d = source.Subscribe(parent); 25 | parent.OnSubscribe(d); 26 | return parent; 27 | } 28 | 29 | protected abstract BaseObserver CreateObserver(IObserver observer); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /reactive-extensions/observable/BaseObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace akarnokd.reactive_extensions 5 | { 6 | /// 7 | /// Base abstract class for implementing IObservers that talk to 8 | /// another IObserver and supports deferred dispose of an upstream 9 | /// set via OnSubscribe(). 10 | /// 11 | /// The upstream value type. 12 | /// The downstream value type. 13 | internal abstract class BaseObserver : IObserver, IDisposable 14 | { 15 | internal readonly IObserver downstream; 16 | 17 | internal IDisposable upstream; 18 | 19 | internal BaseObserver(IObserver downstream) 20 | { 21 | this.downstream = downstream; 22 | } 23 | 24 | public abstract void OnCompleted(); 25 | 26 | public abstract void OnError(Exception error); 27 | 28 | public abstract void OnNext(T value); 29 | 30 | internal virtual void OnSubscribe(IDisposable d) 31 | { 32 | if (Interlocked.CompareExchange(ref upstream, d, null) != null) 33 | { 34 | d?.Dispose(); 35 | } 36 | } 37 | 38 | internal bool IsDisposed() 39 | { 40 | return DisposableHelper.IsDisposed(ref upstream); 41 | } 42 | 43 | public virtual void Dispose() 44 | { 45 | DisposableHelper.Dispose(ref upstream); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /reactive-extensions/observable/DoAfterNext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | internal sealed class DoAfterNext : IObservable 6 | { 7 | readonly IObservable source; 8 | 9 | readonly Action handler; 10 | 11 | public DoAfterNext(IObservable source, Action handler) 12 | { 13 | this.source = source; 14 | this.handler = handler; 15 | } 16 | 17 | public IDisposable Subscribe(IObserver observer) 18 | { 19 | return source.Subscribe(new DoAfterNextObserver(observer, handler)); 20 | } 21 | 22 | sealed class DoAfterNextObserver : IObserver 23 | { 24 | readonly IObserver downstream; 25 | 26 | readonly Action handler; 27 | 28 | bool done; 29 | 30 | public DoAfterNextObserver(IObserver downstream, Action handler) 31 | { 32 | this.downstream = downstream; 33 | this.handler = handler; 34 | } 35 | 36 | public void OnCompleted() 37 | { 38 | if (done) 39 | { 40 | return; 41 | } 42 | downstream.OnCompleted(); 43 | } 44 | 45 | public void OnError(Exception error) 46 | { 47 | if (done) 48 | { 49 | return; 50 | } 51 | downstream.OnError(error); 52 | } 53 | 54 | public void OnNext(T value) 55 | { 56 | if (done) 57 | { 58 | return; 59 | } 60 | downstream.OnNext(value); 61 | try 62 | { 63 | handler(value); 64 | } 65 | catch (Exception ex) 66 | { 67 | done = true; 68 | downstream.OnError(ex); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /reactive-extensions/observable/DoAfterTerminate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | internal sealed class DoAfterTerminate : IObservable 6 | { 7 | readonly IObservable source; 8 | 9 | readonly Action handler; 10 | 11 | public DoAfterTerminate(IObservable source, Action handler) 12 | { 13 | this.source = source; 14 | this.handler = handler; 15 | } 16 | 17 | public IDisposable Subscribe(IObserver observer) 18 | { 19 | return source.Subscribe(new DoAfterTerminateObserver(observer, handler)); 20 | } 21 | 22 | sealed class DoAfterTerminateObserver : IObserver 23 | { 24 | readonly IObserver downstream; 25 | 26 | readonly Action handler; 27 | 28 | public DoAfterTerminateObserver(IObserver downstream, Action handler) 29 | { 30 | this.downstream = downstream; 31 | this.handler = handler; 32 | } 33 | 34 | public void OnCompleted() 35 | { 36 | try 37 | { 38 | downstream.OnCompleted(); 39 | } 40 | finally 41 | { 42 | handler(); 43 | } 44 | } 45 | 46 | public void OnError(Exception error) 47 | { 48 | try 49 | { 50 | downstream.OnError(error); 51 | } 52 | finally 53 | { 54 | handler(); 55 | } 56 | } 57 | 58 | public void OnNext(T value) 59 | { 60 | downstream.OnNext(value); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /reactive-extensions/observable/DoOnDispose.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace akarnokd.reactive_extensions 5 | { 6 | internal sealed class DoOnDispose : IObservable 7 | { 8 | readonly IObservable source; 9 | 10 | readonly Action handler; 11 | 12 | internal DoOnDispose(IObservable source, Action handler) 13 | { 14 | this.source = source; 15 | this.handler = handler; 16 | } 17 | 18 | public IDisposable Subscribe(IObserver observer) 19 | { 20 | return new DoOnDisposeDisposable(source.Subscribe(observer), handler); 21 | } 22 | 23 | internal sealed class DoOnDisposeDisposable : IDisposable 24 | { 25 | IDisposable disposable; 26 | 27 | Action handler; 28 | 29 | public DoOnDisposeDisposable(IDisposable disposable, Action handler) 30 | { 31 | this.disposable = disposable; 32 | Volatile.Write(ref this.handler, handler); 33 | } 34 | 35 | public void Dispose() 36 | { 37 | try 38 | { 39 | Interlocked.Exchange(ref handler, null)?.Invoke(); 40 | } 41 | finally 42 | { 43 | Interlocked.Exchange(ref disposable, null)?.Dispose(); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /reactive-extensions/observable/DoOnSubscribe.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | internal sealed class DoOnSubscribe : IObservable 6 | { 7 | readonly IObservable source; 8 | 9 | readonly Action handler; 10 | 11 | internal DoOnSubscribe(IObservable source, Action handler) 12 | { 13 | this.source = source; 14 | this.handler = handler; 15 | } 16 | 17 | public IDisposable Subscribe(IObserver observer) 18 | { 19 | try 20 | { 21 | handler(); 22 | } 23 | catch (Exception ex) 24 | { 25 | observer.OnError(ex); 26 | return DisposableHelper.EMPTY; 27 | } 28 | 29 | return source.Subscribe(observer); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /reactive-extensions/observable/IInnerObserverSupport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | /// 6 | /// Callback methods for handling signals of individual inner observers. 7 | /// 8 | /// The item types of the inner observers 9 | internal interface IInnerObserverSupport 10 | { 11 | void InnerNext(InnerObserver sender, U item); 12 | 13 | void InnerError(InnerObserver sender, Exception error); 14 | 15 | void InnerComplete(InnerObserver sender); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /reactive-extensions/observable/RedoObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | /// 9 | /// Observer that re-subscribes to a source upon a call 10 | /// to . 11 | /// 12 | /// The element type of the sequence. 13 | internal abstract class RedoObserver : BaseObserver 14 | { 15 | readonly IObservable source; 16 | 17 | int wip; 18 | 19 | internal RedoObserver(IObserver downstream, IObservable source) : base(downstream) 20 | { 21 | this.source = source; 22 | } 23 | 24 | public override void OnNext(T value) 25 | { 26 | downstream.OnNext(value); 27 | } 28 | 29 | internal void Next() 30 | { 31 | if (Interlocked.Increment(ref wip) != 1) 32 | { 33 | return; 34 | } 35 | 36 | for (; ; ) 37 | { 38 | var sad = new SingleAssignmentDisposable(); 39 | if (Interlocked.CompareExchange(ref upstream, sad, null) == null) 40 | { 41 | sad.Disposable = source.Subscribe(this); 42 | } 43 | 44 | if (Interlocked.Decrement(ref wip) == 0) 45 | { 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /reactive-extensions/observable/SerializedSubject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Subjects; 3 | 4 | namespace akarnokd.reactive_extensions 5 | { 6 | /// 7 | /// Wraps an ISubject and serializes access to its OnXXX methods. 8 | /// 9 | /// The upstream value type. 10 | internal sealed class SerializedSubject : SerializedObserver, ISubject 11 | { 12 | readonly ISubject subject; 13 | 14 | public SerializedSubject(ISubject subject) : base(subject) 15 | { 16 | this.subject = subject; 17 | } 18 | 19 | public IDisposable Subscribe(IObserver observer) 20 | { 21 | return subject.Subscribe(observer); 22 | } 23 | } 24 | 25 | /// 26 | /// Wraps an ISubject and serializes access to its OnXXX methods. 27 | /// 28 | /// The upstream value type. 29 | /// The subject's output value type. 30 | internal sealed class SerializedSubject : SerializedObserver, ISubject 31 | { 32 | readonly ISubject subject; 33 | 34 | public SerializedSubject(ISubject subject) : base(subject) 35 | { 36 | this.subject = subject; 37 | } 38 | 39 | public IDisposable Subscribe(IObserver observer) 40 | { 41 | return subject.Subscribe(observer); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/BaseSignalObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace akarnokd.reactive_extensions 5 | { 6 | /// 7 | /// Base abstract class for implementing ISignalObservers that talk to 8 | /// another IObserver and supports deferred dispose of an upstream 9 | /// set via OnSubscribe(). 10 | /// 11 | /// The upstream value type. 12 | /// The downstream value type. 13 | internal abstract class BaseSignalObserver : ISignalObserver, IDisposable 14 | { 15 | internal readonly ISignalObserver downstream; 16 | 17 | internal IDisposable upstream; 18 | 19 | internal BaseSignalObserver(ISignalObserver downstream) 20 | { 21 | this.downstream = downstream; 22 | } 23 | 24 | public abstract void OnCompleted(); 25 | 26 | public abstract void OnError(Exception error); 27 | 28 | public abstract void OnNext(T value); 29 | 30 | public virtual void OnSubscribe(IDisposable d) 31 | { 32 | upstream = d; 33 | downstream.OnSubscribe(this); 34 | } 35 | 36 | internal bool IsDisposed() 37 | { 38 | return DisposableHelper.IsDisposed(ref upstream); 39 | } 40 | 41 | public virtual void Dispose() 42 | { 43 | upstream.Dispose(); 44 | DisposableHelper.WeakDispose(ref upstream); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/IInnerSignalObserverSupport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | /// 6 | /// Callback methods for handling signals of individual inner observers. 7 | /// 8 | /// The item types of the inner observers 9 | internal interface IInnerSignalObserverSupport 10 | { 11 | void InnerNext(InnerSignalObserver sender, U item); 12 | 13 | void InnerError(InnerSignalObserver sender, Exception error); 14 | 15 | void InnerComplete(InnerSignalObserver sender); 16 | 17 | void Drain(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceAutoConnect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Subjects; 3 | using System.Threading; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Automatically connect the upstream IConnectableObservable once the 9 | /// specified number of IObservers have subscribed to this IObservable. 10 | /// 11 | /// The upstream value type. 12 | /// Since 0.0.22 13 | internal sealed class ObservableSourceAutoConnect : IObservableSource 14 | { 15 | readonly IConnectableObservableSource source; 16 | 17 | readonly int minObservers; 18 | 19 | readonly Action onConnect; 20 | 21 | int count; 22 | 23 | internal ObservableSourceAutoConnect(IConnectableObservableSource source, int minObservers, Action onConnect) 24 | { 25 | this.source = source; 26 | this.minObservers = minObservers; 27 | this.onConnect = onConnect; 28 | } 29 | 30 | public void Subscribe(ISignalObserver observer) 31 | { 32 | source.Subscribe(observer); 33 | 34 | if (Volatile.Read(ref count) < minObservers) 35 | { 36 | if (Interlocked.Increment(ref count) == minObservers) 37 | { 38 | var c = source.Connect(); 39 | onConnect?.Invoke(c); 40 | } 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceDefer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using static akarnokd.reactive_extensions.ValidationHelper; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | internal sealed class ObservableSourceDefer : IObservableSource { 10 | readonly Func> supplier; 11 | 12 | public ObservableSourceDefer(Func> supplier) 13 | { 14 | this.supplier = supplier; 15 | } 16 | 17 | public void Subscribe(ISignalObserver observer) 18 | { 19 | var source = default(IObservableSource); 20 | 21 | try 22 | { 23 | source = RequireNonNullRef(supplier(), "The supplier returned a null IObservableSource"); 24 | } 25 | catch (Exception ex) 26 | { 27 | DisposableHelper.Error(observer, ex); 28 | return; 29 | } 30 | 31 | source.Subscribe(observer); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceEmpty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceEmpty : IObservableSource, IStaticValue 8 | { 9 | internal static readonly IObservableSource Instance = new ObservableSourceEmpty(); 10 | 11 | private ObservableSourceEmpty() 12 | { 13 | // singleton 14 | } 15 | 16 | public T GetValue(out bool success) 17 | { 18 | success = false; 19 | return default(T); 20 | } 21 | 22 | public void Subscribe(ISignalObserver observer) 23 | { 24 | DisposableHelper.Complete(observer); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | internal sealed class ObservableSourceError : IObservableSource, IDynamicValue 9 | { 10 | readonly Exception error; 11 | 12 | public ObservableSourceError(Exception error) 13 | { 14 | this.error = error; ; 15 | } 16 | 17 | public T GetValue(out bool success) 18 | { 19 | throw error; 20 | } 21 | 22 | public void Subscribe(ISignalObserver observer) 23 | { 24 | DisposableHelper.Error(observer, error); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceFromAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceFromAction : IObservableSource, IDynamicValue 8 | { 9 | readonly Action action; 10 | 11 | public ObservableSourceFromAction(Action action) 12 | { 13 | this.action = action; 14 | } 15 | 16 | public T GetValue(out bool success) 17 | { 18 | action(); 19 | success = false; 20 | return default(T); 21 | } 22 | 23 | public void Subscribe(ISignalObserver observer) 24 | { 25 | var parent = new FromActionDisposable(observer); 26 | observer.OnSubscribe(parent); 27 | 28 | if (parent.IsDisposed()) 29 | { 30 | return; 31 | } 32 | 33 | try 34 | { 35 | action(); 36 | } 37 | catch (Exception ex) 38 | { 39 | if (!parent.IsDisposed()) 40 | { 41 | parent.Error(ex); 42 | } 43 | return; 44 | } 45 | 46 | parent.Complete(); 47 | } 48 | 49 | sealed class FromActionDisposable : DeferredScalarDisposable 50 | { 51 | internal FromActionDisposable(ISignalObserver downstream) : base(downstream) 52 | { 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceFromFunc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceFromFunc : IObservableSource, IDynamicValue 8 | { 9 | readonly Func supplier; 10 | 11 | public ObservableSourceFromFunc(Func supplier) 12 | { 13 | this.supplier = supplier; 14 | } 15 | 16 | public T GetValue(out bool success) 17 | { 18 | var v = supplier(); 19 | success = true; 20 | return v; 21 | } 22 | 23 | public void Subscribe(ISignalObserver observer) 24 | { 25 | var parent = new FromFuncDisposable(observer); 26 | observer.OnSubscribe(parent); 27 | 28 | if (parent.IsDisposed()) 29 | { 30 | return; 31 | } 32 | 33 | var v = default(T); 34 | try 35 | { 36 | v = supplier(); 37 | } 38 | catch (Exception ex) 39 | { 40 | if (!parent.IsDisposed()) 41 | { 42 | parent.Error(ex); 43 | } 44 | return; 45 | } 46 | 47 | parent.Complete(v); 48 | } 49 | 50 | sealed class FromFuncDisposable : DeferredScalarDisposable 51 | { 52 | internal FromFuncDisposable(ISignalObserver downstream) : base(downstream) 53 | { 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceFromObservable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceFromObservable : IObservableSource 8 | { 9 | readonly IObservable source; 10 | 11 | public ObservableSourceFromObservable(IObservable source) 12 | { 13 | this.source = source; 14 | } 15 | 16 | public void Subscribe(ISignalObserver observer) 17 | { 18 | var parent = new FromObserver(observer); 19 | observer.OnSubscribe(parent); 20 | 21 | parent.OnSubscribe(source.Subscribe(parent)); 22 | } 23 | 24 | sealed class FromObserver : IObserver, IDisposable 25 | { 26 | readonly ISignalObserver downstream; 27 | 28 | IDisposable upstream; 29 | 30 | public FromObserver(ISignalObserver downstream) 31 | { 32 | this.downstream = downstream; 33 | } 34 | 35 | public void Dispose() 36 | { 37 | DisposableHelper.Dispose(ref upstream); 38 | } 39 | 40 | public void OnCompleted() 41 | { 42 | downstream.OnCompleted(); 43 | Dispose(); 44 | } 45 | 46 | public void OnError(Exception error) 47 | { 48 | downstream.OnError(error); 49 | Dispose(); 50 | } 51 | 52 | public void OnNext(T value) 53 | { 54 | downstream.OnNext(value); 55 | } 56 | 57 | internal void OnSubscribe(IDisposable d) 58 | { 59 | DisposableHelper.SetOnce(ref upstream, d); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceHide.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceHide : IObservableSource 8 | { 9 | readonly IObservableSource source; 10 | 11 | public ObservableSourceHide(IObservableSource source) 12 | { 13 | this.source = source; 14 | } 15 | 16 | public void Subscribe(ISignalObserver observer) 17 | { 18 | source.Subscribe(new HideObserver(observer)); 19 | } 20 | 21 | sealed class HideObserver : ISignalObserver, IDisposable 22 | { 23 | readonly ISignalObserver downstream; 24 | 25 | IDisposable upstream; 26 | 27 | public HideObserver(ISignalObserver downstream) 28 | { 29 | this.downstream = downstream; 30 | } 31 | 32 | public void Dispose() 33 | { 34 | upstream.Dispose(); 35 | } 36 | 37 | public void OnCompleted() 38 | { 39 | downstream.OnCompleted(); 40 | } 41 | 42 | public void OnError(Exception ex) 43 | { 44 | downstream.OnError(ex); 45 | } 46 | 47 | public void OnNext(T item) 48 | { 49 | downstream.OnNext(item); 50 | } 51 | 52 | public void OnSubscribe(IDisposable d) 53 | { 54 | upstream = d; 55 | downstream.OnSubscribe(this); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceIgnoreElementsTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | internal sealed class ObservableSourceIgnoreElementsTask : ISignalObserver, IDisposable 10 | { 11 | readonly TaskCompletionSource tcs; 12 | 13 | IDisposable upstream; 14 | 15 | CancellationTokenRegistration reg; 16 | 17 | internal Task Task { get { return tcs.Task; } } 18 | 19 | public ObservableSourceIgnoreElementsTask(CancellationTokenSource cts) 20 | { 21 | tcs = new TaskCompletionSource(); 22 | if (cts != null) 23 | { 24 | reg = cts.Token.Register(@this => ((IDisposable)@this).Dispose(), this); 25 | } 26 | } 27 | 28 | public void Dispose() 29 | { 30 | if (DisposableHelper.Dispose(ref upstream)) 31 | { 32 | tcs.TrySetCanceled(); 33 | reg.Dispose(); 34 | } 35 | } 36 | 37 | public void OnCompleted() 38 | { 39 | tcs.TrySetResult(default(T)); 40 | reg.Dispose(); 41 | } 42 | 43 | public void OnError(Exception ex) 44 | { 45 | tcs.TrySetException(ex); 46 | reg.Dispose(); 47 | } 48 | 49 | public void OnNext(T item) 50 | { 51 | // deliberately ignored 52 | } 53 | 54 | public void OnSubscribe(IDisposable d) 55 | { 56 | DisposableHelper.SetOnce(ref upstream, d); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceIsEmpty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceIsEmpty : IObservableSource 8 | { 9 | readonly IObservableSource source; 10 | 11 | public ObservableSourceIsEmpty(IObservableSource source) 12 | { 13 | this.source = source; 14 | } 15 | 16 | public void Subscribe(ISignalObserver observer) 17 | { 18 | source.Subscribe(new IsEmptyObserver(observer)); 19 | } 20 | 21 | sealed class IsEmptyObserver : DeferredScalarDisposable, ISignalObserver 22 | { 23 | IDisposable upstream; 24 | 25 | internal IsEmptyObserver(ISignalObserver downstream) : base(downstream) 26 | { 27 | } 28 | 29 | public void OnCompleted() 30 | { 31 | Complete(true); 32 | } 33 | 34 | public void OnError(Exception ex) 35 | { 36 | Error(ex); 37 | } 38 | 39 | public void OnNext(T item) 40 | { 41 | upstream.Dispose(); 42 | Complete(false); 43 | } 44 | 45 | public void OnSubscribe(IDisposable d) 46 | { 47 | upstream = d; 48 | downstream.OnSubscribe(this); 49 | } 50 | 51 | public override void Dispose() 52 | { 53 | base.Dispose(); 54 | upstream.Dispose(); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceLastTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | internal sealed class ObservableSourceLastTask : ISignalObserver, IDisposable 10 | { 11 | readonly TaskCompletionSource tcs; 12 | 13 | IDisposable upstream; 14 | 15 | CancellationTokenRegistration reg; 16 | 17 | T last; 18 | bool hasLast; 19 | 20 | internal Task Task { get { return tcs.Task; } } 21 | 22 | public ObservableSourceLastTask(CancellationTokenSource cts) 23 | { 24 | tcs = new TaskCompletionSource(); 25 | if (cts != null) 26 | { 27 | reg = cts.Token.Register(@this => ((IDisposable)@this).Dispose(), this); 28 | } 29 | } 30 | 31 | public void Dispose() 32 | { 33 | if (DisposableHelper.Dispose(ref upstream)) 34 | { 35 | tcs.TrySetCanceled(); 36 | reg.Dispose(); 37 | } 38 | } 39 | 40 | public void OnCompleted() 41 | { 42 | if (hasLast) 43 | { 44 | reg.Dispose(); 45 | tcs.TrySetResult(last); 46 | } 47 | else 48 | { 49 | reg.Dispose(); 50 | tcs.TrySetException(new IndexOutOfRangeException()); 51 | } 52 | } 53 | 54 | public void OnError(Exception ex) 55 | { 56 | reg.Dispose(); 57 | tcs.TrySetException(ex); 58 | } 59 | 60 | public void OnNext(T item) 61 | { 62 | hasLast = true; 63 | last = item; 64 | } 65 | 66 | public void OnSubscribe(IDisposable d) 67 | { 68 | DisposableHelper.SetOnce(ref upstream, d); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceNever.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceNever : IObservableSource 8 | { 9 | internal static readonly IObservableSource Instance = new ObservableSourceNever(); 10 | 11 | private ObservableSourceNever() 12 | { 13 | // singleton 14 | } 15 | 16 | public void Subscribe(ISignalObserver observer) 17 | { 18 | observer.OnSubscribe(DisposableHelper.Empty()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceSkip.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceSkip : IObservableSource 8 | { 9 | readonly IObservableSource source; 10 | 11 | readonly long n; 12 | 13 | public ObservableSourceSkip(IObservableSource source, long n) 14 | { 15 | this.source = source; 16 | this.n = n; 17 | } 18 | 19 | public void Subscribe(ISignalObserver observer) 20 | { 21 | source.Subscribe(new SkipObserver(observer, n)); 22 | } 23 | 24 | sealed class SkipObserver : ISignalObserver, IDisposable 25 | { 26 | readonly ISignalObserver downstream; 27 | 28 | long remaining; 29 | 30 | IDisposable upstream; 31 | 32 | public SkipObserver(ISignalObserver downstream, long remaining) 33 | { 34 | this.downstream = downstream; 35 | this.remaining = remaining; 36 | } 37 | 38 | public void Dispose() 39 | { 40 | upstream.Dispose(); 41 | } 42 | 43 | public void OnCompleted() 44 | { 45 | downstream.OnCompleted(); 46 | } 47 | 48 | public void OnError(Exception ex) 49 | { 50 | downstream.OnError(ex); 51 | } 52 | 53 | public void OnNext(T item) 54 | { 55 | var r = remaining; 56 | if (r == 0) 57 | { 58 | downstream.OnNext(item); 59 | } 60 | else 61 | { 62 | remaining = r - 1; 63 | } 64 | } 65 | 66 | public void OnSubscribe(IDisposable d) 67 | { 68 | upstream = d; 69 | downstream.OnSubscribe(this); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/ObservableSourceToObservable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | internal sealed class ObservableSourceToObservable : IObservable 8 | { 9 | readonly IObservableSource source; 10 | 11 | public ObservableSourceToObservable(IObservableSource source) 12 | { 13 | this.source = source; 14 | } 15 | 16 | public IDisposable Subscribe(IObserver observer) 17 | { 18 | var parent = new FromObserver(observer); 19 | source.Subscribe(parent); 20 | return parent; 21 | } 22 | 23 | sealed class FromObserver : ISignalObserver, IDisposable 24 | { 25 | readonly IObserver downstream; 26 | 27 | IDisposable upstream; 28 | 29 | public FromObserver(IObserver downstream) 30 | { 31 | this.downstream = downstream; 32 | } 33 | 34 | public void Dispose() 35 | { 36 | DisposableHelper.Dispose(ref upstream); 37 | } 38 | 39 | public void OnCompleted() 40 | { 41 | downstream.OnCompleted(); 42 | } 43 | 44 | public void OnError(Exception error) 45 | { 46 | downstream.OnError(error); 47 | } 48 | 49 | public void OnNext(T value) 50 | { 51 | downstream.OnNext(value); 52 | } 53 | 54 | public void OnSubscribe(IDisposable d) 55 | { 56 | DisposableHelper.SetOnce(ref upstream, d); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /reactive-extensions/observablesource/RedoSignalObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | /// 9 | /// Observer that re-subscribes to a source upon a call 10 | /// to . 11 | /// 12 | /// The element type of the sequence. 13 | /// Since 0.0.22 14 | internal abstract class RedoSignalObserver : BaseSignalObserver 15 | { 16 | readonly IObservableSource source; 17 | 18 | int wip; 19 | 20 | internal RedoSignalObserver(ISignalObserver downstream, IObservableSource source) : base(downstream) 21 | { 22 | this.source = source; 23 | } 24 | 25 | public override void OnNext(T value) 26 | { 27 | downstream.OnNext(value); 28 | } 29 | 30 | public override void OnSubscribe(IDisposable d) 31 | { 32 | DisposableHelper.SetOnce(ref upstream, d); 33 | } 34 | 35 | public override void Dispose() 36 | { 37 | DisposableHelper.Dispose(ref upstream); 38 | } 39 | 40 | internal void Next() 41 | { 42 | if (Interlocked.Increment(ref wip) != 1) 43 | { 44 | return; 45 | } 46 | 47 | for (; ; ) 48 | { 49 | if (DisposableHelper.Replace(ref upstream, null)) 50 | { 51 | source.Subscribe(this); 52 | } 53 | 54 | if (Interlocked.Decrement(ref wip) == 0) 55 | { 56 | break; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /reactive-extensions/reactive-extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | 0.0.27-alpha 5 | Extension methods and classes for the dotnet/reactive library. 6 | David Karnok 7 | akarnokd.reactive_extensions 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | https://github.com/akarnokd/reactive-extensions 10 | https://github.com/akarnokd/reactive-extensions 11 | GitHub 12 | Copyright 2018, David Karnok 13 | Added strong name. 14 | reactive extensions 15 | true 16 | c:\users\akarnokd\.gradle\akarnokd.snk 17 | 18 | 19 | bin\Debug\netstandard2.0\reactive-extensions.xml 20 | 7.1 21 | 22 | 23 | bin\Release\netstandard2.0\reactive-extensions.xml 24 | 7.1 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleDefer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Defers the creation of the actual single source 9 | /// provided by a supplier function until a single observer completes. 10 | /// 11 | /// The success value type 12 | /// Since 0.0.11 13 | internal sealed class SingleDefer : ISingleSource 14 | { 15 | readonly Func> supplier; 16 | 17 | public SingleDefer(Func> supplier) 18 | { 19 | this.supplier = supplier; 20 | } 21 | 22 | public void Subscribe(ISingleObserver observer) 23 | { 24 | var c = default(ISingleSource); 25 | try 26 | { 27 | c = ValidationHelper.RequireNonNullRef(supplier(), "The supplier returned a null ISingleSource"); 28 | } 29 | catch (Exception ex) 30 | { 31 | DisposableHelper.Error(observer, ex); 32 | return; 33 | } 34 | 35 | c.Subscribe(observer); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Signals an exception immediately. 9 | /// 10 | internal sealed class SingleError : ISingleSource 11 | { 12 | readonly Exception error; 13 | 14 | public SingleError(Exception error) 15 | { 16 | this.error = error; 17 | } 18 | 19 | public void Subscribe(ISingleObserver observer) 20 | { 21 | observer.Error(error); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Tests the upstream's success value via a predicate 9 | /// and relays it if the predicate returns false, 10 | /// completes the downstream otherwise. 11 | /// 12 | /// The upstream value type. 13 | /// Since 0.0.11 14 | internal sealed class SingleFilter : IMaybeSource 15 | { 16 | readonly ISingleSource source; 17 | 18 | readonly Func predicate; 19 | 20 | public SingleFilter(ISingleSource source, Func predicate) 21 | { 22 | this.source = source; 23 | this.predicate = predicate; 24 | } 25 | 26 | public void Subscribe(IMaybeObserver observer) 27 | { 28 | source.Subscribe(new FilterObserver(observer, predicate)); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleFromFunc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Wraps and runs a function for each incoming 9 | /// single observer and signals the value returned 10 | /// by the function as the success event. 11 | /// 12 | /// The value type. 13 | /// Since 0.0.11 14 | internal sealed class SingleFromFunc : ISingleSource 15 | { 16 | readonly Func func; 17 | 18 | public SingleFromFunc(Func func) 19 | { 20 | this.func = func; 21 | } 22 | 23 | public void Subscribe(ISingleObserver observer) 24 | { 25 | var d = new BooleanDisposable(); 26 | observer.OnSubscribe(d); 27 | 28 | if (d.IsDisposed()) 29 | { 30 | return; 31 | } 32 | 33 | var v = default(T); 34 | 35 | try 36 | { 37 | v = func(); 38 | } 39 | catch (Exception ex) 40 | { 41 | if (!d.IsDisposed()) 42 | { 43 | observer.OnError(ex); 44 | } 45 | return; 46 | } 47 | 48 | if (!d.IsDisposed()) 49 | { 50 | observer.OnSuccess(v); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleFromTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | /// 10 | /// Relays the terminal event from a wrapped task. 11 | /// 12 | /// The value type of the task (ignored) 13 | /// Since 0.0.6 14 | internal sealed class SingleFromTask : ISingleSource 15 | { 16 | readonly Task task; 17 | 18 | static readonly Action, object> TASK = 19 | (task, self) => ((TaskDisposable)self).Run(task); 20 | 21 | public SingleFromTask(Task task) 22 | { 23 | this.task = task; 24 | } 25 | 26 | public void Subscribe(ISingleObserver observer) 27 | { 28 | var parent = new TaskDisposable(observer); 29 | observer.OnSubscribe(parent); 30 | 31 | task.ContinueWith(TASK, parent); 32 | } 33 | 34 | internal sealed class TaskDisposable : IDisposable 35 | { 36 | internal ISingleObserver downstream; 37 | 38 | public TaskDisposable(ISingleObserver downstream) 39 | { 40 | Volatile.Write(ref this.downstream, downstream); 41 | } 42 | 43 | public void Dispose() 44 | { 45 | Interlocked.Exchange(ref this.downstream, null); 46 | } 47 | 48 | internal void Run(Task task) 49 | { 50 | var ex = task.Exception; 51 | if (ex == null) 52 | { 53 | Volatile.Read(ref downstream)?.OnSuccess(task.Result); 54 | } 55 | else 56 | { 57 | Volatile.Read(ref downstream)?.OnError(ex); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleHide.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Hides the identity of the upstream source and disposable. 9 | /// 10 | /// Since 0.0.9 11 | internal sealed class SingleHide : ISingleSource 12 | { 13 | readonly ISingleSource source; 14 | 15 | public SingleHide(ISingleSource source) 16 | { 17 | this.source = source; 18 | } 19 | 20 | public void Subscribe(ISingleObserver observer) 21 | { 22 | source.Subscribe(new HideObserver(observer)); 23 | } 24 | 25 | sealed class HideObserver : ISingleObserver, IDisposable 26 | { 27 | readonly ISingleObserver downstream; 28 | 29 | IDisposable upstream; 30 | 31 | public HideObserver(ISingleObserver downstream) 32 | { 33 | this.downstream = downstream; 34 | } 35 | 36 | public void Dispose() 37 | { 38 | upstream.Dispose(); 39 | } 40 | 41 | public void OnError(Exception error) 42 | { 43 | downstream.OnError(error); 44 | } 45 | 46 | public void OnSubscribe(IDisposable d) 47 | { 48 | upstream = d; 49 | downstream.OnSubscribe(this); 50 | } 51 | 52 | public void OnSuccess(T item) 53 | { 54 | downstream.OnSuccess(item); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleInnerObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace akarnokd.reactive_extensions 4 | { 5 | /// 6 | /// A single observer with a thread-safe deferred 7 | /// OnSubscribe and Dispose support. 8 | /// 9 | /// The success value type. 10 | /// Since 0.0.11 11 | internal sealed class SingleInnerObserver : ISingleObserver, IDisposable 12 | { 13 | readonly ISingleObserver downstream; 14 | 15 | IDisposable upstream; 16 | 17 | public SingleInnerObserver(ISingleObserver downstream) 18 | { 19 | this.downstream = downstream; 20 | } 21 | 22 | public void Dispose() 23 | { 24 | DisposableHelper.Dispose(ref upstream); 25 | } 26 | 27 | public void OnError(Exception error) 28 | { 29 | downstream.OnError(error); 30 | } 31 | 32 | public void OnSubscribe(IDisposable d) 33 | { 34 | DisposableHelper.SetOnce(ref upstream, d); 35 | } 36 | 37 | public void OnSuccess(T item) 38 | { 39 | downstream.OnSuccess(item); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleJust.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Succeed with an item. 9 | /// 10 | /// The type of the item. 11 | /// Since 0.0.9 12 | internal sealed class SingleJust : ISingleSource 13 | { 14 | readonly T item; 15 | 16 | public SingleJust(T item) 17 | { 18 | this.item = item; 19 | } 20 | 21 | public void Subscribe(ISingleObserver observer) 22 | { 23 | observer.OnSubscribe(DisposableHelper.EMPTY); 24 | observer.OnSuccess(item); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleLambdaObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Calls the specific action callbacks upon receiving the terminal signals. 9 | /// 10 | /// Since 0.0.9 11 | internal sealed class SingleLambdaObserver : ISingleObserver, IDisposable 12 | { 13 | readonly Action onSuccess; 14 | 15 | readonly Action onError; 16 | 17 | IDisposable upstream; 18 | 19 | public SingleLambdaObserver(Action onSuccess, Action onError) 20 | { 21 | this.onSuccess = onSuccess; 22 | this.onError = onError; 23 | } 24 | 25 | public void Dispose() 26 | { 27 | DisposableHelper.Dispose(ref upstream); 28 | } 29 | 30 | public void OnError(Exception error) 31 | { 32 | DisposableHelper.WeakDispose(ref upstream); 33 | try 34 | { 35 | onError?.Invoke(error); 36 | } 37 | catch (Exception) 38 | { 39 | // FIXME nowhere to put these 40 | } 41 | } 42 | 43 | public void OnSubscribe(IDisposable d) 44 | { 45 | DisposableHelper.SetOnce(ref upstream, d); 46 | } 47 | 48 | public void OnSuccess(T item) 49 | { 50 | DisposableHelper.WeakDispose(ref upstream); 51 | try 52 | { 53 | onSuccess?.Invoke(item); 54 | } 55 | catch (Exception) 56 | { 57 | // FIXME nowhere to put these 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleNever.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Never terminates. 9 | /// 10 | internal sealed class SingleNever : ISingleSource 11 | { 12 | internal static readonly ISingleSource INSTANCE = new SingleNever(); 13 | 14 | public void Subscribe(ISingleObserver observer) 15 | { 16 | observer.OnSubscribe(DisposableHelper.EMPTY); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleSafeObserver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Makes sure downstream exceptions are suppressed. 9 | /// 10 | /// The success value type. 11 | /// Since 0.0.11 12 | internal sealed class SingleSafeObserver : ISingleObserver, IDisposable 13 | { 14 | readonly ISingleObserver downstream; 15 | 16 | IDisposable upstream; 17 | 18 | public SingleSafeObserver(ISingleObserver downstream) 19 | { 20 | this.downstream = downstream; 21 | } 22 | 23 | public void Dispose() 24 | { 25 | upstream.Dispose(); 26 | } 27 | 28 | public void OnError(Exception error) 29 | { 30 | try 31 | { 32 | downstream.OnError(error); 33 | } 34 | catch (Exception) 35 | { 36 | // TODO what should happen with these? 37 | } 38 | } 39 | 40 | public void OnSubscribe(IDisposable d) 41 | { 42 | upstream = d; 43 | try 44 | { 45 | downstream.OnSubscribe(this); 46 | } 47 | catch (Exception) 48 | { 49 | d.Dispose(); 50 | // TODO what should happen with these? 51 | } 52 | } 53 | 54 | public void OnSuccess(T item) 55 | { 56 | try 57 | { 58 | downstream.OnSuccess(item); 59 | } 60 | catch (Exception) 61 | { 62 | // TODO what should happen with these? 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Concurrency; 4 | using System.Text; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | /// 9 | /// Signals 0L after a specified time elapsed on the given scheduler. 10 | /// 11 | /// Since 0.0.11 12 | internal sealed class SingleTimer : ISingleSource 13 | { 14 | readonly TimeSpan time; 15 | 16 | readonly IScheduler scheduler; 17 | 18 | static readonly Func, IDisposable> COMPLETE = 19 | (s, o) => { o.OnSuccess(0L); return DisposableHelper.EMPTY; }; 20 | 21 | public SingleTimer(TimeSpan time, IScheduler scheduler) 22 | { 23 | this.time = time; 24 | this.scheduler = scheduler; 25 | } 26 | 27 | public void Subscribe(ISingleObserver observer) 28 | { 29 | var sad = new SingleAssignmentDisposable(); 30 | observer.OnSubscribe(sad); 31 | 32 | sad.Disposable = scheduler.Schedule(observer, time, COMPLETE); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleToMaybe.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Converts a single source into a single source, 9 | /// failing with an index out-of-range exception 10 | /// if the single source is empty 11 | /// 12 | /// The element type of the single source. 13 | /// Since 0.0.12 14 | internal sealed class SingleToMaybe : IMaybeSource 15 | { 16 | readonly ISingleSource source; 17 | 18 | public SingleToMaybe(ISingleSource source) 19 | { 20 | this.source = source; 21 | } 22 | 23 | public void Subscribe(IMaybeObserver observer) 24 | { 25 | source.Subscribe(new ToObservableObserver(observer)); 26 | } 27 | 28 | internal sealed class ToObservableObserver : ISingleObserver, IDisposable 29 | { 30 | readonly IMaybeObserver downstream; 31 | 32 | IDisposable upstream; 33 | 34 | public ToObservableObserver(IMaybeObserver downstream) 35 | { 36 | this.downstream = downstream; 37 | } 38 | 39 | public void Dispose() 40 | { 41 | upstream.Dispose(); 42 | } 43 | 44 | public void OnError(Exception error) 45 | { 46 | downstream.OnError(error); 47 | } 48 | 49 | public void OnSuccess(T item) 50 | { 51 | downstream.OnSuccess(item); 52 | } 53 | 54 | public void OnSubscribe(IDisposable d) 55 | { 56 | upstream = d; 57 | downstream.OnSubscribe(this); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleToObservable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// Exposes a single source as a legacy observable. 9 | /// 10 | /// The element type of the observable sequence. 11 | /// Since 0.0.12 12 | internal sealed class SingleToObservable : IObservable 13 | { 14 | readonly ISingleSource source; 15 | 16 | public SingleToObservable(ISingleSource source) 17 | { 18 | this.source = source; 19 | } 20 | 21 | public IDisposable Subscribe(IObserver observer) 22 | { 23 | var parent = new ToObservableObserver(observer); 24 | source.Subscribe(parent); 25 | return parent; 26 | } 27 | 28 | internal sealed class ToObservableObserver : ISingleObserver, IDisposable 29 | { 30 | readonly IObserver downstream; 31 | 32 | IDisposable upstream; 33 | 34 | public ToObservableObserver(IObserver downstream) 35 | { 36 | this.downstream = downstream; 37 | } 38 | 39 | public void Dispose() 40 | { 41 | DisposableHelper.Dispose(ref upstream); 42 | } 43 | 44 | public void OnError(Exception error) 45 | { 46 | DisposableHelper.WeakDispose(ref upstream); 47 | downstream.OnError(error); 48 | } 49 | 50 | public void OnSuccess(T item) 51 | { 52 | DisposableHelper.WeakDispose(ref upstream); 53 | downstream.OnNext(item); 54 | downstream.OnCompleted(); 55 | } 56 | 57 | public void OnSubscribe(IDisposable d) 58 | { 59 | DisposableHelper.SetOnce(ref upstream, d); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /reactive-extensions/single/SingleToTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | /// 10 | /// Subscribe to a single source and expose the terminal 11 | /// signal as a . 12 | /// 13 | /// The success value type. 14 | /// Since 0.0.11 15 | internal sealed class SingleToTask : ISingleObserver 16 | { 17 | readonly TaskCompletionSource tcs; 18 | 19 | public Task Task { get { return tcs.Task; } } 20 | 21 | IDisposable upstream; 22 | 23 | CancellationTokenRegistration reg; 24 | 25 | bool hasTokenSource; 26 | 27 | public SingleToTask() 28 | { 29 | tcs = new TaskCompletionSource(); 30 | } 31 | 32 | internal void Init(CancellationTokenSource cts) 33 | { 34 | if (cts != null) 35 | { 36 | reg = cts.Token.Register(Dispose); 37 | hasTokenSource = true; 38 | } 39 | } 40 | 41 | public void OnError(Exception error) 42 | { 43 | tcs.TrySetException(error); 44 | if (hasTokenSource) 45 | { 46 | reg.Dispose(); 47 | } 48 | } 49 | 50 | public void OnSuccess(T item) 51 | { 52 | tcs.TrySetResult(item); 53 | if (hasTokenSource) 54 | { 55 | reg.Dispose(); 56 | } 57 | } 58 | 59 | public void OnSubscribe(IDisposable d) 60 | { 61 | DisposableHelper.SetOnce(ref upstream, d); 62 | } 63 | 64 | void Dispose() 65 | { 66 | DisposableHelper.Dispose(ref upstream); 67 | tcs.TrySetCanceled(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /reactive-extensions/tools/BooleanDisposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace akarnokd.reactive_extensions 6 | { 7 | /// 8 | /// A basic disposable that can be checked for a disposed state. 9 | /// 10 | internal sealed class BooleanDisposable : IDisposable 11 | { 12 | volatile bool disposed; 13 | 14 | /// 15 | /// Returns true if this BooleanDisposable has been disposed. 16 | /// 17 | /// True if this BooleanDisposable has been disposed. 18 | public bool IsDisposed() 19 | { 20 | return disposed; 21 | } 22 | 23 | public void Dispose() 24 | { 25 | disposed = true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /reactive-extensions/tools/ExceptionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Linq; 6 | 7 | namespace akarnokd.reactive_extensions 8 | { 9 | /// 10 | /// Utility methods to deal with normal and composite exceptions and 11 | /// Exception fields atomically. 12 | /// 13 | internal static class ExceptionHelper 14 | { 15 | internal static readonly Exception TERMINATED = new Exception("No further exceptions"); 16 | 17 | internal static bool AddException(ref Exception field, Exception ex) 18 | { 19 | for (; ; ) 20 | { 21 | var a = Volatile.Read(ref field); 22 | if (a == TERMINATED) 23 | { 24 | return false; 25 | } 26 | var b = default(Exception); 27 | if (a == null) 28 | { 29 | b = ex; 30 | } 31 | else 32 | if (a is AggregateException g) 33 | { 34 | b = new AggregateException(g.InnerExceptions.Concat(new[] { ex })); 35 | } 36 | else 37 | { 38 | b = new AggregateException(a, ex); 39 | } 40 | 41 | if (Interlocked.CompareExchange(ref field, b, a) == a) 42 | { 43 | return true; 44 | } 45 | } 46 | } 47 | 48 | internal static Exception Terminate(ref Exception field) 49 | { 50 | var ex = Volatile.Read(ref field); 51 | if (ex != TERMINATED) 52 | { 53 | ex = Interlocked.Exchange(ref field, TERMINATED); 54 | } 55 | return ex; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /reactive-extensions/tools/SingleAssignmentDisposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | 6 | namespace akarnokd.reactive_extensions 7 | { 8 | /// 9 | /// A disposable contain that allows only one assignment to its property. 10 | /// 11 | internal sealed class SingleAssignmentDisposable : IDisposable 12 | { 13 | IDisposable disposable; 14 | 15 | public IDisposable Disposable 16 | { 17 | get 18 | { 19 | var d = Volatile.Read(ref disposable); 20 | if (d == DisposableHelper.DISPOSED) 21 | { 22 | return DisposableHelper.EMPTY; 23 | } 24 | return d; 25 | } 26 | set 27 | { 28 | if (Interlocked.CompareExchange(ref disposable, value, null) != null) 29 | { 30 | value?.Dispose(); 31 | var d = Volatile.Read(ref disposable); 32 | if (d != DisposableHelper.DISPOSED) 33 | { 34 | throw new InvalidOperationException("Disposable already set"); 35 | } 36 | } 37 | } 38 | } 39 | 40 | public void Dispose() 41 | { 42 | DisposableHelper.Dispose(ref disposable); 43 | } 44 | 45 | public bool IsDisposed() 46 | { 47 | return DisposableHelper.IsDisposed(ref disposable); 48 | } 49 | } 50 | } 51 | --------------------------------------------------------------------------------