├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── dotnet.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── async-enumerable-dotnet-benchmark ├── Program.cs └── async-enumerable-dotnet-benchmark.csproj ├── async-enumerable-dotnet-test ├── AllTest.cs ├── AmbTest.cs ├── AnyTest.cs ├── ArrayQueueTest.cs ├── AsyncEnumerableTest.cs ├── BufferBoundaryTest.cs ├── BufferTest.cs ├── CancellationHelperTest.cs ├── CollectTest.cs ├── CombineLatestTest.cs ├── ConcatMapEagerTest.cs ├── ConcatMapTest.cs ├── ConcatTest.cs ├── ConsumeTest.cs ├── CountTest.cs ├── CreateTest.cs ├── DebounceTest.cs ├── DefaultIfEmptyTest.cs ├── DeferTest.cs ├── DistinctTest.cs ├── DistinctUntilChangedTest.cs ├── DoOnDisposeTest.cs ├── DoOnNextTest.cs ├── ElementAtTest.cs ├── EmptyTest.cs ├── ErrorTest.cs ├── ExceptionHelperTest.cs ├── FilterTest.cs ├── FirstLastSingleAsyncTest.cs ├── FirstLastSingleTest.cs ├── FlatMapTest.cs ├── ForEachTest.cs ├── FromEnumerableTest.cs ├── FromObservableTest.cs ├── FromTaskFuncTest.cs ├── GroupByTest.cs ├── IgnoreElementsTest.cs ├── IntervalTest.cs ├── IsEmptyTest.cs ├── JustTest.cs ├── LatestTest.cs ├── LicenseHeader.cs ├── ListComparer.cs ├── MapTest.cs ├── MergeTest.cs ├── MergeWithTest.cs ├── MulticastAsyncEnumerableTest.cs ├── NeverTest.cs ├── OnErrorResumeNextTest.cs ├── PrefetchTest.cs ├── PublishTest.cs ├── QueueDrainHelperTest.cs ├── RangeTest.cs ├── ReduceTest.cs ├── RepeatTest.cs ├── ReplayAsyncEnumerableTest.cs ├── ReplayTest.cs ├── ResumeHelperTest.cs ├── RetryTest.cs ├── SampleTest.cs ├── ScanTest.cs ├── SkipLastTest.cs ├── SkipTest.cs ├── SkipUntilTest.cs ├── SkipWhileTest.cs ├── SlimResumeTest.cs ├── SwitchIfEmptyTest.cs ├── SwitchMapTest.cs ├── TakeLastTest.cs ├── TakeTest.cs ├── TakeUntilTest.cs ├── TakeWhileTest.cs ├── TestHelper.cs ├── TestHelperTest.cs ├── TestTaskRunnerTest.cs ├── TimeoutTest.cs ├── TimerTest.cs ├── ToCollectionTest.cs ├── ToEnumerableTest.cs ├── ToListTest.cs ├── ToObservableTest.cs ├── UnicastAsyncEnumerableTest.cs ├── UnitTest1.cs ├── UsingTest.cs ├── WithLatestFromTest.cs ├── ZipTest.cs └── async-enumerable-dotnet-test.csproj ├── async-enumerable-dotnet.sln └── async-enumerable-dotnet ├── AsyncEnumerable.cs ├── IAsyncEmitter.cs ├── IAsyncGroupedEnumerable.cs ├── MulticastAsyncEnumerable.cs ├── ReplayAsyncEnumerable.cs ├── TestTaskRunner.cs ├── UnicastAsyncEnumerable.cs ├── async-enumerable-dotnet.csproj └── impl ├── All.cs ├── Amb.cs ├── Any.cs ├── ArrayQueue.cs ├── Buffer.cs ├── BufferBoundary.cs ├── CancellationHelper.cs ├── Collect.cs ├── CombineLatest.cs ├── Concat.cs ├── ConcatMap.cs ├── ConcatMapEager.cs ├── Count.cs ├── CreateEmitter.cs ├── Debounce.cs ├── DefaultIfEmpty.cs ├── Defer.cs ├── Distinct.cs ├── DistinctUntilChanged.cs ├── DoOnDispose.cs ├── DoOnNext.cs ├── ElementAt.cs ├── Empty.cs ├── EmptyHelper.cs ├── Error.cs ├── ExceptionHelper.cs ├── Filter.cs ├── FirstLastSingle.cs ├── FirstLastSingleAsync.cs ├── FlatMap.cs ├── ForEach.cs ├── FromArray.cs ├── FromEnumerable.cs ├── FromObservable.cs ├── FromTaskFunc.cs ├── GroupBy.cs ├── IgnoreElements.cs ├── Interval.cs ├── IsEmpty.cs ├── Just.cs ├── Latest.cs ├── Map.cs ├── Merge.cs ├── Multicast.cs ├── Never.cs ├── OnErrorResumeNext.cs ├── Prefetch.cs ├── Publish.cs ├── QueueDrainHelper.cs ├── Range.cs ├── Reduce.cs ├── Repeat.cs ├── Replay.cs ├── ResumeHelper.cs ├── Retry.cs ├── Sample.cs ├── Scan.cs ├── Skip.cs ├── SkipLast.cs ├── SkipUntil.cs ├── SkipWhile.cs ├── SlimResume.cs ├── SwitchIfEmpty.cs ├── SwitchMap.cs ├── Take.cs ├── TakeLast.cs ├── TakeUntil.cs ├── TakeWhile.cs ├── Timeout.cs ├── Timer.cs ├── ToCollection.cs ├── ToEnumerable.cs ├── ToObservable.cs ├── Using.cs ├── WithLatestFrom.cs └── Zip.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 3.1.x 20 | - name: Restore dependencies 21 | run: dotnet restore 22 | - name: Build 23 | run: dotnet build --no-restore 24 | - name: Test 25 | run: dotnet test --no-build --verbosity normal 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | dotnet: 3.0.100 3 | mono: none 4 | # running in container causes test failures and 2x-3x longer build, use standalone instances 5 | sudo: required 6 | 7 | script: 8 | - dotnet test ./async-enumerable-dotnet-test/async-enumerable-dotnet-test.csproj -v m -------------------------------------------------------------------------------- /async-enumerable-dotnet-benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using async_enumerable_dotnet; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace async_enumerable_dotnet_benchmark 10 | { 11 | // ReSharper disable once ClassNeverInstantiated.Global 12 | // ReSharper disable once ArrangeTypeModifiers 13 | class Program 14 | { 15 | /// 16 | /// Don't worry about this program yet. I'm using it to 17 | /// diagnose await hangs and internal state that is otherwise 18 | /// hard (or I don't know how) to debug as an XUnit test. 19 | /// 20 | /// 21 | // ReSharper disable once UnusedParameter.Local 22 | // ReSharper disable once ArrangeTypeMemberModifiers 23 | static void Main(string[] args) 24 | { 25 | for (var j = 0; j < 100000; j++) 26 | { 27 | if (j % 10 == 0) 28 | { 29 | Console.WriteLine(j); 30 | } 31 | var list = TimeSequence(0, 200, 400, 600) 32 | .ConcatMapEager(v => AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(100))) 33 | .Take(1) 34 | .GetAsyncEnumerator(default); 35 | 36 | try 37 | { 38 | if (!list.MoveNextAsync().Result) 39 | { 40 | Console.WriteLine("Empty?"); 41 | } 42 | 43 | if (list.Current != 0) 44 | { 45 | Console.WriteLine(list.Current); 46 | Console.ReadLine(); 47 | break; 48 | } 49 | } 50 | finally 51 | { 52 | list.DisposeAsync().AsTask().Wait(); 53 | } 54 | } 55 | } 56 | 57 | private static IAsyncEnumerable TimeSequence(params long[] timestamps) 58 | { 59 | return AsyncEnumerable.FromArray(timestamps) 60 | .FlatMap(v => AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(v)).Map(w => v)); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-benchmark/async-enumerable-dotnet-benchmark.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | async_enumerable_dotnet_benchmark 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ArrayQueueTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet.impl; 7 | 8 | namespace async_enumerable_dotnet_test 9 | { 10 | public class ArrayQueueTest 11 | { 12 | [Fact] 13 | public void Normal() 14 | { 15 | for (var k = 2; k <= 128; k *= 2) 16 | { 17 | for (var j = 1; j < k * 4; j++) { 18 | var q = new ArrayQueue(k); 19 | 20 | for (var i = 0; i < j; i++) 21 | { 22 | q.Enqueue(i); 23 | Assert.True(q.Dequeue(out var v)); 24 | Assert.Equal(i, v); 25 | } 26 | 27 | Assert.False(q.Dequeue(out _)); 28 | } 29 | } 30 | } 31 | 32 | [Fact] 33 | public void Grow() 34 | { 35 | for (var k = 2; k <= 128; k *= 2) 36 | { 37 | for (var j = 1; j < k * 4; j++) 38 | { 39 | var q = new ArrayQueue(k); 40 | 41 | for (var i = 0; i < j; i++) 42 | { 43 | q.Enqueue(i); 44 | } 45 | 46 | for (var i = 0; i < j; i++) 47 | { 48 | Assert.True(q.Dequeue(out var v)); 49 | Assert.Equal(i, v); 50 | } 51 | 52 | Assert.False(q.Dequeue(out _)); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/CancellationHelperTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading; 6 | using Xunit; 7 | using async_enumerable_dotnet.impl; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class CancellationHelperTest 12 | { 13 | private CancellationTokenSource _cts; 14 | 15 | [Fact] 16 | public void Cancel() 17 | { 18 | Assert.True(CancellationHelper.Cancel(ref _cts)); 19 | 20 | Assert.Same(_cts, CancellationHelper.Cancelled); 21 | 22 | Assert.False(CancellationHelper.Cancel(ref _cts)); 23 | } 24 | 25 | [Fact] 26 | public void Replace_After_Cancel() 27 | { 28 | Assert.True(CancellationHelper.Cancel(ref _cts)); 29 | 30 | var cts = new CancellationTokenSource(); 31 | 32 | Assert.False(CancellationHelper.Replace(ref _cts, cts)); 33 | 34 | Assert.True(cts.IsCancellationRequested); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/CollectTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class CollectTest 14 | { 15 | [Fact] 16 | public async Task Normal() 17 | { 18 | await AsyncEnumerable.Range(1, 5) 19 | .Collect(() => new List(), (a, b) => a.Add(b)) 20 | .AssertResult(new List(new[] { 1, 2, 3, 4, 5 })); 21 | } 22 | 23 | [Fact] 24 | public async Task Empty() 25 | { 26 | await AsyncEnumerable.Empty() 27 | .Collect(() => new List(), (a, b) => a.Add(b)) 28 | .AssertResult(new List()); 29 | } 30 | 31 | [Fact] 32 | public async Task Initial_Crash() 33 | { 34 | await AsyncEnumerable.Empty() 35 | .Collect>(() => throw new InvalidOperationException(), (a, b) => a.Add(b)) 36 | .AssertFailure(typeof(InvalidOperationException)); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/CombineLatestTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Linq; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class CombineLatestTest 14 | { 15 | [Fact] 16 | public async Task Empty() 17 | { 18 | await AsyncEnumerable.CombineLatest(v => v.Sum()) 19 | .AssertResult(); 20 | } 21 | 22 | [Fact] 23 | public async Task Single() 24 | { 25 | await AsyncEnumerable.CombineLatest(v => v.Sum() + 1, 26 | AsyncEnumerable.Just(1)) 27 | .AssertResult(2); 28 | } 29 | 30 | [Fact] 31 | public async Task One_Item_Each() 32 | { 33 | await AsyncEnumerable.CombineLatest(v => v.Sum(), AsyncEnumerable.Just(1), AsyncEnumerable.Just(2)) 34 | .AssertResult(3); 35 | } 36 | 37 | [Fact] 38 | public async Task One_Is_Empty() 39 | { 40 | await AsyncEnumerable.CombineLatest(v => v.Sum(), AsyncEnumerable.Empty(), AsyncEnumerable.Just(2)) 41 | .AssertResult(); 42 | } 43 | 44 | [Fact] 45 | public async Task Two_Is_Empty() 46 | { 47 | await AsyncEnumerable.CombineLatest(v => v.Sum(), AsyncEnumerable.Just(1), AsyncEnumerable.Empty()) 48 | .AssertResult(); 49 | } 50 | 51 | [Fact] 52 | public async Task ZigZag() 53 | { 54 | var t = 200; 55 | if (Environment.GetEnvironmentVariable("CI") != null) 56 | { 57 | t = 2000; 58 | } 59 | await AsyncEnumerable.CombineLatest(v => v.Sum(), 60 | AsyncEnumerable.Interval(1, 5, TimeSpan.FromMilliseconds(t)), 61 | AsyncEnumerable.Interval(1, 5, TimeSpan.FromMilliseconds(t + t / 2), TimeSpan.FromMilliseconds(t)).Map(v => v * 10) 62 | ) 63 | .AssertResult(11, 12, 22, 23, 33, 34, 44, 45, 55); 64 | } 65 | 66 | [Fact] 67 | public async Task Second_Many() 68 | { 69 | var t = 200; 70 | if (Environment.GetEnvironmentVariable("CI") != null) 71 | { 72 | t = 2000; 73 | } 74 | await AsyncEnumerable.CombineLatest(v => v.Sum(), 75 | AsyncEnumerable.Just(10L), 76 | AsyncEnumerable.Interval(1, 5, TimeSpan.FromMilliseconds(t + t / 2), TimeSpan.FromMilliseconds(t)) 77 | ) 78 | .AssertResult(11, 12, 13, 14, 15); 79 | } 80 | 81 | [Fact] 82 | public async Task Error() 83 | { 84 | await AsyncEnumerable.CombineLatest(v => v.Sum(), 85 | AsyncEnumerable.Just(1), 86 | AsyncEnumerable.Just(2).ConcatWith( 87 | AsyncEnumerable.Error(new InvalidOperationException()) 88 | ) 89 | ) 90 | .AssertFailure(typeof(InvalidOperationException), 3); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ConcatMapTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ConcatMapTest 13 | { 14 | [Fact] 15 | public async Task Async_Normal() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .ConcatMap(v => AsyncEnumerable.Range(v * 10, 5)) 19 | .AssertResult( 20 | 10, 11, 12, 13, 14, 21 | 20, 21, 22, 23, 24, 22 | 30, 31, 32, 33, 34, 23 | 40, 41, 42, 43, 44, 24 | 50, 51, 52, 53, 54 25 | ); 26 | } 27 | 28 | [Fact] 29 | public async Task Async_Filter() 30 | { 31 | await AsyncEnumerable.Range(1, 10) 32 | .ConcatMap(v => v % 2 == 0 ? AsyncEnumerable.Just(v) : AsyncEnumerable.Empty()) 33 | .AssertResult( 34 | 2, 4, 6, 8, 10 35 | ); 36 | } 37 | 38 | [Fact] 39 | public async Task Async_Take() 40 | { 41 | await AsyncEnumerable.Range(1, 5) 42 | .ConcatMap(v => AsyncEnumerable.Range(v * 10, 5)) 43 | .Take(7) 44 | .AssertResult( 45 | 10, 11, 12, 13, 14, 46 | 20, 21 47 | ); 48 | } 49 | 50 | [Fact] 51 | public async Task Enumerable_Normal() 52 | { 53 | await AsyncEnumerable.Range(1, 5) 54 | .ConcatMap(v => Enumerable.Range(v * 10, 5)) 55 | .AssertResult( 56 | 10, 11, 12, 13, 14, 57 | 20, 21, 22, 23, 24, 58 | 30, 31, 32, 33, 34, 59 | 40, 41, 42, 43, 44, 60 | 50, 51, 52, 53, 54 61 | ); 62 | } 63 | 64 | [Fact] 65 | public async Task Enumerable_Filter() 66 | { 67 | await AsyncEnumerable.Range(1, 10) 68 | .ConcatMap(v => v % 2 == 0 ? new[] { v } : Enumerable.Empty()) 69 | .AssertResult( 70 | 2, 4, 6, 8, 10 71 | ); 72 | } 73 | 74 | [Fact] 75 | public async Task Enumerable_Take() 76 | { 77 | await AsyncEnumerable.Range(1, 5) 78 | .ConcatMap(v => Enumerable.Range(v * 10, 5)) 79 | .Take(7) 80 | .AssertResult( 81 | 10, 11, 12, 13, 14, 82 | 20, 21 83 | ); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ConcatTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ConcatTest 13 | { 14 | [Fact] 15 | public async Task Array_Normal() 16 | { 17 | await AsyncEnumerable.Concat( 18 | AsyncEnumerable.Range(1, 3), 19 | AsyncEnumerable.Empty(), 20 | AsyncEnumerable.FromArray(4, 5, 6, 7), 21 | AsyncEnumerable.Empty(), 22 | AsyncEnumerable.Just(8), 23 | AsyncEnumerable.FromEnumerable(new[] { 9, 10 }), 24 | AsyncEnumerable.Empty() 25 | ) 26 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 27 | } 28 | 29 | [Fact] 30 | public async Task Enumerable_Normal() 31 | { 32 | await AsyncEnumerable.Concat((IEnumerable>)new[] { 33 | AsyncEnumerable.Range(1, 3), 34 | AsyncEnumerable.Empty(), 35 | AsyncEnumerable.FromArray(4, 5, 6, 7), 36 | AsyncEnumerable.Empty(), 37 | AsyncEnumerable.Just(8), 38 | AsyncEnumerable.FromEnumerable(new[] { 9, 10 }), 39 | AsyncEnumerable.Empty() 40 | } 41 | ) 42 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 43 | } 44 | 45 | [Fact] 46 | public async Task ConcatWith_Normal() 47 | { 48 | await AsyncEnumerable.Range(1, 3) 49 | .ConcatWith(AsyncEnumerable.Range(4, 2)) 50 | .AssertResult(1, 2, 3, 4, 5); 51 | } 52 | 53 | [Fact] 54 | public async Task ConcatWith_Take() 55 | { 56 | await AsyncEnumerable.Range(1, 5) 57 | .Take(3) 58 | .ConcatWith(AsyncEnumerable.Range(4, 2)) 59 | .AssertResult(1, 2, 3, 4, 5); 60 | } 61 | 62 | [Fact] 63 | public async Task Async_Normal() 64 | { 65 | await 66 | AsyncEnumerable.FromArray( 67 | AsyncEnumerable.Range(1, 3), 68 | AsyncEnumerable.Empty(), 69 | AsyncEnumerable.FromArray(4, 5, 6, 7), 70 | AsyncEnumerable.Empty(), 71 | AsyncEnumerable.Just(8), 72 | AsyncEnumerable.FromEnumerable(new[] { 9, 10 }), 73 | AsyncEnumerable.Empty() 74 | ) 75 | .Concat() 76 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ConsumeTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using async_enumerable_dotnet; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class ConsumeTest 14 | { 15 | [Fact] 16 | public async Task Normal() 17 | { 18 | var push = new MulticastAsyncEnumerable(); 19 | 20 | var t1 = push.AssertResult(1, 2, 3, 4, 5); 21 | var t2 = push.AssertResult(1, 2, 3, 4, 5); 22 | 23 | await AsyncEnumerable.Range(1, 5) 24 | .Consume(push); 25 | 26 | await t1; 27 | await t2; 28 | } 29 | 30 | [Fact] 31 | public async Task Error() 32 | { 33 | var push = new MulticastAsyncEnumerable(); 34 | 35 | var t1 = push.AssertFailure(typeof(InvalidOperationException)); 36 | var t2 = push.AssertFailure(typeof(InvalidOperationException)); 37 | 38 | await AsyncEnumerable.Error(new InvalidOperationException()) 39 | .Consume(push); 40 | 41 | await t1; 42 | await t2; 43 | } 44 | 45 | [Fact] 46 | public async Task Cancel() 47 | { 48 | var cts = new CancellationTokenSource(); 49 | 50 | var push = new UnicastAsyncEnumerable(); 51 | 52 | var t = AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(500)) 53 | .Consume(push, cts.Token); 54 | 55 | await Task.Delay(100, cts.Token); 56 | 57 | cts.Cancel(); 58 | 59 | await t; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/CountTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class CountTest 13 | { 14 | [Fact] 15 | public async Task Empty() 16 | { 17 | await AsyncEnumerable.Empty() 18 | .Count() 19 | .AssertResult(0L); 20 | } 21 | 22 | [Fact] 23 | public async Task Just() 24 | { 25 | await AsyncEnumerable.Just(1) 26 | .Count() 27 | .AssertResult(1L); 28 | } 29 | 30 | [Fact] 31 | public async Task Range() 32 | { 33 | await AsyncEnumerable.Range(1, 100) 34 | .Count() 35 | .AssertResult(100L); 36 | } 37 | 38 | 39 | [Fact] 40 | public async Task Error() 41 | { 42 | await AsyncEnumerable.Range(1, 100).WithError(new InvalidOperationException()) 43 | .Count() 44 | .AssertFailure(typeof(InvalidOperationException)); 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/CreateTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | using System; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class CreateTest 13 | { 14 | [Fact] 15 | public async Task Empty() 16 | { 17 | var result = AsyncEnumerable.Create(async e => 18 | { 19 | await Task.CompletedTask; 20 | }); 21 | 22 | await result.AssertResult(); 23 | } 24 | 25 | [Fact] 26 | public async Task Range() 27 | { 28 | var result = AsyncEnumerable.Create(async e => 29 | { 30 | for (var i = 0; i < 10 && !e.DisposeAsyncRequested; i++) 31 | { 32 | await e.Next(i); 33 | } 34 | }); 35 | 36 | await result.AssertResult(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 37 | } 38 | 39 | [Fact] 40 | public async Task Range_Loop() 41 | { 42 | for (var j = 0; j < 1000; j++) 43 | { 44 | var result = AsyncEnumerable.Create(async e => 45 | { 46 | for (var i = 0; i < 10 && !e.DisposeAsyncRequested; i++) 47 | { 48 | await e.Next(i); 49 | } 50 | }); 51 | 52 | await result.AssertResult(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 53 | } 54 | } 55 | 56 | [Fact] 57 | public async Task Items_And_Error() 58 | { 59 | var result = AsyncEnumerable.Create(async e => 60 | { 61 | await e.Next(1); 62 | 63 | await e.Next(2); 64 | 65 | throw new InvalidOperationException(); 66 | }); 67 | 68 | await result.AssertFailure(typeof(InvalidOperationException), 1, 2); 69 | } 70 | 71 | [Fact] 72 | public async Task Take() 73 | { 74 | await AsyncEnumerable.Create(async e => 75 | { 76 | for (var i = 0; i < 10 && !e.DisposeAsyncRequested; i++) 77 | { 78 | await e.Next(i); 79 | } 80 | }) 81 | .Take(5) 82 | .AssertResult(0, 1, 2, 3, 4); 83 | } 84 | 85 | [Fact] 86 | public async Task Take_Loop() 87 | { 88 | for (var j = 0; j < 1000; j++) 89 | { 90 | await AsyncEnumerable.Create(async e => 91 | { 92 | for (var i = 0; i < 10 && !e.DisposeAsyncRequested; i++) 93 | { 94 | await e.Next(i); 95 | } 96 | }) 97 | .Take(5) 98 | .AssertResult(0, 1, 2, 3, 4); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/DefaultIfEmptyTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class DefaultIfEmptyTest 12 | { 13 | [Fact] 14 | public async Task NonEmpty() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .DefaultIfEmpty(100) 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Fact] 22 | public async Task Empty() 23 | { 24 | await AsyncEnumerable.Empty() 25 | .DefaultIfEmpty(100) 26 | .AssertResult(100); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/DeferTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class DeferTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | var count = 0; 18 | var result = AsyncEnumerable.Defer(() => 19 | { 20 | count++; 21 | return AsyncEnumerable.FromArray(1, 2, 3, 4, 5); 22 | }); 23 | 24 | Assert.Equal(0, count); 25 | 26 | await result.AssertResult(1, 2, 3, 4, 5); 27 | 28 | Assert.Equal(1, count); 29 | 30 | await result.AssertResult(1, 2, 3, 4, 5); 31 | 32 | Assert.Equal(2, count); 33 | } 34 | 35 | [Fact] 36 | public async Task Crash() 37 | { 38 | await AsyncEnumerable.Defer(() => throw new InvalidOperationException()) 39 | .AssertFailure(typeof(InvalidOperationException)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/DistinctTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class DistinctTest 14 | { 15 | [Fact] 16 | public async Task Empty() 17 | { 18 | await AsyncEnumerable.Empty() 19 | .Distinct() 20 | .AssertResult(); 21 | } 22 | 23 | [Fact] 24 | public async Task Normal() 25 | { 26 | await AsyncEnumerable.Range(1, 5) 27 | .Distinct() 28 | .AssertResult(1, 2, 3, 4, 5); 29 | } 30 | 31 | [Fact] 32 | public async Task Redundant() 33 | { 34 | await AsyncEnumerable.FromArray(1, 2, 3, 2, 1, 4, 5, 1, 5) 35 | .Distinct() 36 | .AssertResult(1, 2, 3, 4, 5); 37 | } 38 | 39 | [Fact] 40 | public async Task KeySelector() 41 | { 42 | await AsyncEnumerable.Range(1, 5) 43 | .Distinct(v => v % 3) 44 | .AssertResult(1, 2, 3); 45 | } 46 | 47 | [Fact] 48 | public async Task EqualityComparer() 49 | { 50 | await AsyncEnumerable.Range(1, 5) 51 | .Distinct(EqualityComparer.Default) 52 | .AssertResult(1, 2, 3, 4, 5); 53 | } 54 | 55 | [Fact] 56 | public async Task KeySelector_EqualityComparer() 57 | { 58 | await AsyncEnumerable.Range(1, 5) 59 | .Distinct(v => v % 3, EqualityComparer.Default) 60 | .AssertResult(1, 2, 3); 61 | } 62 | 63 | [Fact] 64 | public async Task Custom_Set() 65 | { 66 | await AsyncEnumerable.Range(1, 5) 67 | .Distinct(v => v % 3, () => new HashSet()) 68 | .AssertResult(1, 2, 3); 69 | } 70 | 71 | [Fact] 72 | public async Task Custom_Crash() 73 | { 74 | await AsyncEnumerable.Range(1, 5) 75 | .Distinct(v => v % 3, () => throw new InvalidOperationException()) 76 | .AssertFailure(typeof(InvalidOperationException)); 77 | } 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/DistinctUntilChangedTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class DistinctUntilChangedTest 13 | { 14 | [Fact] 15 | public async Task Empty() 16 | { 17 | await AsyncEnumerable.Empty() 18 | .DistinctUntilChanged() 19 | .AssertResult(); 20 | } 21 | 22 | [Fact] 23 | public async Task Just() 24 | { 25 | await AsyncEnumerable.Just(1) 26 | .DistinctUntilChanged() 27 | .AssertResult(1); 28 | } 29 | 30 | [Fact] 31 | public async Task Range() 32 | { 33 | await AsyncEnumerable.Range(1, 5) 34 | .DistinctUntilChanged() 35 | .AssertResult(1, 2, 3, 4, 5); 36 | } 37 | 38 | [Fact] 39 | public async Task Redundant() 40 | { 41 | await AsyncEnumerable.FromArray(1, 2, 3, 3, 2, 1, 1, 4, 5, 4, 4) 42 | .DistinctUntilChanged() 43 | .AssertResult(1, 2, 3, 2, 1, 4, 5, 4); 44 | } 45 | 46 | [Fact] 47 | public async Task Redundant_Comparer() 48 | { 49 | await AsyncEnumerable.FromArray(1, 2, 3, 3, 2, 1, 1, 4, 5, 4, 4) 50 | .DistinctUntilChanged(EqualityComparer.Default) 51 | .AssertResult(1, 2, 3, 2, 1, 4, 5, 4); 52 | } 53 | 54 | [Fact] 55 | public async Task KeySelector() 56 | { 57 | await AsyncEnumerable.Range(1, 10) 58 | .DistinctUntilChanged(k => k / 3) 59 | .AssertResult(1, 3, 6, 9); 60 | } 61 | 62 | [Fact] 63 | public async Task KeySelector_KeyComparer() 64 | { 65 | await AsyncEnumerable.Range(1, 10) 66 | .DistinctUntilChanged(k => k / 3, EqualityComparer.Default) 67 | .AssertResult(1, 3, 6, 9); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/DoOnNextTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | using System.Collections.Generic; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class DoOnNextTest 13 | { 14 | [Fact] 15 | public async Task Sync_Normal() 16 | { 17 | var list = new List(); 18 | await AsyncEnumerable.Range(1, 5) 19 | .DoOnNext(v => list.Add(v)) 20 | .AssertResult(1, 2, 3, 4, 5); 21 | 22 | Assert.Equal(new[] { 1, 2, 3, 4, 5 }, list); 23 | } 24 | 25 | [Fact] 26 | public async Task Async_Normal() 27 | { 28 | var list = new List(); 29 | await AsyncEnumerable.Range(1, 5) 30 | .DoOnNext(async v => 31 | { 32 | await Task.Delay(100); 33 | list.Add(v); 34 | }) 35 | .AssertResult(1, 2, 3, 4, 5); 36 | 37 | Assert.Equal(new[] { 1, 2, 3, 4, 5 }, list); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ElementAtTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ElementAtTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .ElementAt(2) 19 | .AssertResult(3); 20 | } 21 | 22 | [Fact] 23 | public async Task Normal_Default() 24 | { 25 | await AsyncEnumerable.Range(1, 5) 26 | .ElementAt(2, 100) 27 | .AssertResult(3); 28 | } 29 | 30 | [Fact] 31 | public async Task Missing() 32 | { 33 | await AsyncEnumerable.Range(1, 5) 34 | .ElementAt(10) 35 | .AssertFailure(typeof(IndexOutOfRangeException)); 36 | } 37 | 38 | [Fact] 39 | public async Task Missing_Default() 40 | { 41 | await AsyncEnumerable.Range(1, 5) 42 | .ElementAt(10, 100) 43 | .AssertResult(100); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/EmptyTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class EmptyTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | await AsyncEnumerable.Empty() 17 | .AssertResult(); 18 | } 19 | 20 | [Fact] 21 | public void Current() 22 | { 23 | Assert.Equal(0, AsyncEnumerable.Empty().GetAsyncEnumerator(default).Current); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ErrorTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ErrorTest 13 | { 14 | [Fact] 15 | public async Task Calls() 16 | { 17 | var en = AsyncEnumerable.Error(new InvalidOperationException()).GetAsyncEnumerator(default); 18 | 19 | try 20 | { 21 | await en.MoveNextAsync(); 22 | Assert.False(true, "Should have thrown"); 23 | } 24 | catch (InvalidOperationException) 25 | { 26 | // expected; 27 | } 28 | 29 | Assert.Equal(0, en.Current); 30 | Assert.False(await en.MoveNextAsync()); 31 | 32 | await en.DisposeAsync(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ExceptionHelperTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet.impl; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class ExceptionHelperTest 12 | { 13 | private Exception _error; 14 | 15 | [Fact] 16 | public void AddException() 17 | { 18 | var ex = new InvalidOperationException(); 19 | Assert.True(ExceptionHelper.AddException(ref _error, ex)); 20 | 21 | Assert.Same(ex, _error); 22 | } 23 | 24 | [Fact] 25 | public void AddException_Existing() 26 | { 27 | _error = new IndexOutOfRangeException(); 28 | var ex = new InvalidOperationException(); 29 | Assert.True(ExceptionHelper.AddException(ref _error, ex)); 30 | 31 | Assert.True(_error is AggregateException); 32 | 33 | var g = (AggregateException)_error; 34 | 35 | Assert.Equal(2, g.InnerExceptions.Count); 36 | 37 | Assert.True(g.InnerExceptions[0] is IndexOutOfRangeException); 38 | Assert.True(g.InnerExceptions[1] is InvalidOperationException); 39 | } 40 | 41 | [Fact] 42 | public void AddException_Terminated() 43 | { 44 | var ex = new InvalidOperationException(); 45 | 46 | Assert.True(ExceptionHelper.AddException(ref _error, ex)); 47 | 48 | Assert.Same(ExceptionHelper.Terminate(ref _error), ex); 49 | } 50 | 51 | [Fact] 52 | public void AddException_Terminated_2() 53 | { 54 | Assert.Same(ExceptionHelper.Terminate(ref _error), null); 55 | 56 | var ex = new InvalidOperationException(); 57 | 58 | Assert.False(ExceptionHelper.AddException(ref _error, ex)); 59 | } 60 | 61 | [Fact] 62 | public void Terminate() 63 | { 64 | Assert.Same(ExceptionHelper.Terminate(ref _error), null); 65 | 66 | Assert.Same(ExceptionHelper.Terminate(ref _error), ExceptionHelper.Terminated); 67 | } 68 | 69 | [Fact] 70 | public void Extract_Aggregated_Solo() 71 | { 72 | var ex = new InvalidOperationException(); 73 | 74 | Assert.Same(ExceptionHelper.Extract(new AggregateException(ex)), ex); 75 | } 76 | 77 | [Fact] 78 | public void Extract_Aggregated_Not_Solo() 79 | { 80 | var ex = new InvalidOperationException(); 81 | var ex2 = new IndexOutOfRangeException(); 82 | 83 | var g = new AggregateException(ex, ex2); 84 | 85 | Assert.Same(ExceptionHelper.Extract(g), g); 86 | } 87 | 88 | [Fact] 89 | public void Extract_Not_Aggregated() 90 | { 91 | var ex = new InvalidOperationException(); 92 | 93 | Assert.Same(ExceptionHelper.Extract(ex), ex); 94 | } 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/FilterTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class FilterTest 12 | { 13 | [Fact] 14 | public async Task Sync_Normal() 15 | { 16 | var result = AsyncEnumerable.Range(1, 10) 17 | .Filter(v => v % 2 == 0); 18 | 19 | await result.AssertResult(2, 4, 6, 8, 10); 20 | } 21 | 22 | [Fact] 23 | public async Task Async_Normal() 24 | { 25 | var result = AsyncEnumerable.Range(1, 10) 26 | .Filter(async v => 27 | { 28 | await Task.Delay(100); 29 | return v % 2 == 0; 30 | }); 31 | 32 | await result.AssertResult(2, 4, 6, 8, 10); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/FirstLastSingleAsyncTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class FirstLastSingleAsyncTest 13 | { 14 | [Fact] 15 | public async Task First() 16 | { 17 | Assert.Equal(1, await AsyncEnumerable.Range(1, 5).FirstAsync()); 18 | } 19 | 20 | [Fact] 21 | public async Task First_Empty() 22 | { 23 | await Assert.ThrowsAsync(() => AsyncEnumerable.Range(1, 0).FirstAsync().AsTask()); 24 | } 25 | 26 | [Fact] 27 | public async Task First_Or_Default() 28 | { 29 | Assert.Equal(10, await AsyncEnumerable.Range(1, 0).FirstAsync(10)); 30 | } 31 | 32 | [Fact] 33 | public async Task Last() 34 | { 35 | Assert.Equal(5, await AsyncEnumerable.Range(1, 5).LastAsync()); 36 | } 37 | 38 | [Fact] 39 | public async Task Last_Empty() 40 | { 41 | await Assert.ThrowsAsync(() => AsyncEnumerable.Range(1, 0).LastAsync().AsTask()); 42 | } 43 | 44 | [Fact] 45 | public async Task Last_Or_Default() 46 | { 47 | Assert.Equal(10, await AsyncEnumerable.Range(1, 0).LastAsync(10)); 48 | } 49 | 50 | [Fact] 51 | public async Task Single() 52 | { 53 | Assert.Equal(0, await AsyncEnumerable.Just(0).SingleAsync()); 54 | } 55 | 56 | [Fact] 57 | public async Task Single_Empty() 58 | { 59 | await Assert.ThrowsAsync(() => AsyncEnumerable.Empty().SingleAsync().AsTask()); 60 | } 61 | 62 | [Fact] 63 | public async Task Single_Or_Default() 64 | { 65 | Assert.Equal(10, await AsyncEnumerable.Empty().SingleAsync(10)); 66 | } 67 | 68 | [Fact] 69 | public async Task Single_Too_Many() 70 | { 71 | await Assert.ThrowsAsync(() => AsyncEnumerable.Range(1, 5).SingleAsync().AsTask()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ForEachTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ForEachTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | var sum = 0; 18 | await AsyncEnumerable.Range(1, 5) 19 | .ForEach(v => sum += v, onComplete: () => sum += 100); 20 | 21 | Assert.Equal(115, sum); 22 | } 23 | 24 | [Fact] 25 | public async Task Error() 26 | { 27 | var error = default(Exception); 28 | await AsyncEnumerable.Error(new InvalidOperationException()) 29 | .ForEach(onError: e => error = e); 30 | 31 | Assert.NotNull(error); 32 | Assert.True(error is InvalidOperationException); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/FromEnumerableTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class FromEnumerableTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | var result = AsyncEnumerable.FromEnumerable(Enumerable.Range(1, 5)) 18 | ; 19 | 20 | await result.AssertResult(1, 2, 3, 4, 5); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/FromTaskFuncTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class FromTaskFuncTest 12 | { 13 | [Fact] 14 | public async Task Func_Normal() 15 | { 16 | var source = AsyncEnumerable.FromTask(async () => 17 | { 18 | await Task.Delay(100); 19 | return 1; 20 | }); 21 | 22 | await source.AssertResult(1); 23 | } 24 | 25 | [Fact] 26 | public async Task Task_Normal() 27 | { 28 | var source = AsyncEnumerable.FromTask(Task.Delay(100).ContinueWith(t => 1)); 29 | 30 | await source.AssertResult(1); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/IgnoreElementsTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class IgnoreElementsTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .IgnoreElements() 18 | .AssertResult(); 19 | } 20 | 21 | [Fact] 22 | public async Task Empty() 23 | { 24 | await AsyncEnumerable.Empty() 25 | .IgnoreElements() 26 | .AssertResult(); 27 | } 28 | 29 | [Fact] 30 | public void Current() 31 | { 32 | Assert.Equal(0, AsyncEnumerable.Range(1, 5) 33 | .IgnoreElements().GetAsyncEnumerator(default).Current); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/IntervalTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class IntervalTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | await AsyncEnumerable.Interval(TimeSpan.FromMilliseconds(100)) 18 | .Take(5) 19 | .AssertResult(0, 1, 2, 3, 4); 20 | } 21 | 22 | [Fact] 23 | public async Task Normal_initial() 24 | { 25 | await AsyncEnumerable.Interval(TimeSpan.FromMilliseconds(50), TimeSpan.FromMilliseconds(100)) 26 | .Take(5) 27 | .AssertResult(0, 1, 2, 3, 4); 28 | } 29 | 30 | [Fact] 31 | public async Task Range() 32 | { 33 | await AsyncEnumerable.Interval(1, 5, TimeSpan.FromMilliseconds(200), TimeSpan.FromMilliseconds(100)) 34 | .AssertResult(1, 2, 3, 4, 5); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/IsEmptyTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class IsEmptyTest 13 | { 14 | [Fact] 15 | public async Task NonEmpty() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .IsEmpty() 19 | .AssertResult(false); 20 | } 21 | 22 | [Fact] 23 | public async Task Empty() 24 | { 25 | await AsyncEnumerable.Empty() 26 | .IsEmpty() 27 | .AssertResult(true); 28 | } 29 | 30 | [Fact] 31 | public async Task Error() 32 | { 33 | await AsyncEnumerable.Error(new InvalidOperationException()) 34 | .IsEmpty() 35 | .AssertFailure(typeof(InvalidOperationException)); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/JustTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class JustTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | await AsyncEnumerable.Just(1) 17 | .AssertResult(1); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/LatestTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class LatestTest 13 | { 14 | [Fact] 15 | public async Task Skip_All() 16 | { 17 | var en = AsyncEnumerable.Range(1, 5).Latest().GetAsyncEnumerator(default); 18 | 19 | try 20 | { 21 | await Task.Delay(200); 22 | 23 | Assert.True(await en.MoveNextAsync()); 24 | 25 | Assert.Equal(5, en.Current); 26 | 27 | Assert.False(await en.MoveNextAsync()); 28 | } 29 | finally 30 | { 31 | await en.DisposeAsync(); 32 | } 33 | } 34 | 35 | [Fact] 36 | public async Task Normal() 37 | { 38 | var push = new MulticastAsyncEnumerable(); 39 | var en = push.Latest().GetAsyncEnumerator(default); 40 | 41 | try 42 | { 43 | await push.Next(1); 44 | 45 | Assert.True(await en.MoveNextAsync()); 46 | 47 | Assert.Equal(1, en.Current); 48 | 49 | await push.Next(2); 50 | await push.Next(3); 51 | 52 | await Task.Delay(200); 53 | 54 | Assert.True(await en.MoveNextAsync()); 55 | 56 | Assert.Equal(3, en.Current); 57 | 58 | await push.Next(4); 59 | await push.Complete(); 60 | 61 | Assert.True(await en.MoveNextAsync()); 62 | 63 | Assert.Equal(4, en.Current); 64 | Assert.False(await en.MoveNextAsync()); 65 | } 66 | finally 67 | { 68 | await en.DisposeAsync(); 69 | } 70 | } 71 | 72 | [Fact] 73 | public async Task Error() 74 | { 75 | await AsyncEnumerable.Error(new InvalidOperationException()) 76 | .Latest() 77 | .AssertFailure(typeof(InvalidOperationException)); 78 | } 79 | 80 | [Fact] 81 | public async Task Take() 82 | { 83 | await AsyncEnumerable.Interval(1, 5, TimeSpan.FromMilliseconds(200)) 84 | .Latest() 85 | .Take(1) 86 | .AssertResult(1L); 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/LicenseHeader.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | using Xunit; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | /// 13 | /// This test class checks all source *.cs files for missing header and 14 | /// adds them. Note that it deliberately fails the first time it finds 15 | /// such files. 16 | /// 17 | public class LicenseHeader 18 | { 19 | private const string HeaderLines = "// Copyright (c) David Karnok & Contributors.\r\n// Licensed under the Apache 2.0 License.\r\n// See LICENSE file in the project root for full license information.\r\n\r\n"; 20 | 21 | [Fact] 22 | public void CheckHeaderMainSources() 23 | { 24 | VisitSources(FindPath("async-enumerable-dotnet/") + "async-enumerable-dotnet/"); 25 | } 26 | 27 | [Fact] 28 | public void CheckHeaderBenchmarkSources() 29 | { 30 | VisitSources(FindPath("async-enumerable-dotnet/") + "async-enumerable-dotnet-benchmark/"); 31 | } 32 | 33 | [Fact] 34 | public void CheckHeaderTestSources() 35 | { 36 | VisitSources(FindPath("async-enumerable-dotnet/") + "async-enumerable-dotnet-test/"); 37 | } 38 | 39 | private static string FindPath(string subProject) 40 | { 41 | var dir = Directory.GetCurrentDirectory().Replace("\\", "/"); 42 | var idx = dir.LastIndexOf(subProject, StringComparison.InvariantCultureIgnoreCase); 43 | if (idx < 0) 44 | { 45 | throw new ArgumentException("Could not find " + subProject + " in dir"); 46 | } 47 | return dir.Substring(0, idx + subProject.Length); 48 | } 49 | 50 | private static void VisitSources(string path) 51 | { 52 | var found = false; 53 | 54 | var ci = Environment.GetEnvironmentVariable("CI") != null; 55 | 56 | var sb = new StringBuilder(); 57 | 58 | foreach (var entry in Directory.EnumerateFiles(path, "*.cs", SearchOption.AllDirectories)) 59 | { 60 | var entryForward = entry.Replace("\\", "/"); 61 | if (entryForward.Contains("AssemblyInfo") 62 | || entryForward.Contains("Temporary") 63 | || entryForward.Contains("/obj/") 64 | || entryForward.Contains("/Debug/") 65 | || entryForward.Contains("/Release/")) 66 | { 67 | continue; 68 | } 69 | 70 | var text = File.ReadAllText(entry, Encoding.UTF8); 71 | if (!text.Contains("\r\n")) 72 | { 73 | text = text.Replace("\n", "\r\n"); 74 | } 75 | if (!text.StartsWith(HeaderLines)) 76 | { 77 | sb.Append(entry).Append("\r\n"); 78 | found = true; 79 | if (!ci) 80 | { 81 | File.WriteAllText(entry, HeaderLines + text, Encoding.UTF8); 82 | } 83 | } 84 | } 85 | 86 | if (found) 87 | { 88 | throw new InvalidOperationException("Missing header found and added. Please rebuild the project of " + path + "\r\n" + sb); 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ListComparer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace async_enumerable_dotnet_test 9 | { 10 | internal sealed class ListComparer : IEqualityComparer> 11 | { 12 | internal static readonly ListComparer Default = new ListComparer(); 13 | 14 | public bool Equals(IList x, IList y) 15 | { 16 | return x.SequenceEqual(y); 17 | } 18 | 19 | public int GetHashCode(IList obj) 20 | { 21 | var hash = 19; 22 | 23 | unchecked 24 | { 25 | hash = obj.Aggregate(hash, (current, t) => current * 31 + (t != null ? t.GetHashCode() : 0)); 26 | } 27 | 28 | return hash; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/MapTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class MapTest 12 | { 13 | [Fact] 14 | public async Task Sync_Normal() 15 | { 16 | var result = AsyncEnumerable.Range(1, 5) 17 | .Map(v => v * v); 18 | 19 | await result.AssertResult(1, 4, 9, 16, 25); 20 | } 21 | 22 | [Fact] 23 | public async Task Async_Normal() 24 | { 25 | var result = AsyncEnumerable.Range(1, 5) 26 | .Map(async v => 27 | { 28 | await Task.Delay(100); 29 | return v * v; 30 | }); 31 | 32 | await result.AssertResult(1, 4, 9, 16, 25); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/MergeWithTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class MergeWithTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .MergeWith(AsyncEnumerable.Range(6, 5)) 18 | .AssertResultSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/NeverTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | 8 | namespace async_enumerable_dotnet_test 9 | { 10 | public class NeverTest 11 | { 12 | /* 13 | [Fact(Skip = "The task of MoveNextAsync never completes and thus DisposeAsync won't either")] 14 | public async void Never() 15 | { 16 | await AsyncEnumerable.Never() 17 | .Timeout(TimeSpan.FromMilliseconds(100)) 18 | .AssertFailure(typeof(TimeoutException)); 19 | } 20 | */ 21 | 22 | [Fact] 23 | public void Normal() 24 | { 25 | var en = AsyncEnumerable.Never().GetAsyncEnumerator(default); 26 | 27 | Assert.Equal(0, AsyncEnumerable.Never().GetAsyncEnumerator(default).Current); 28 | 29 | // no await as the test would never end otherwise 30 | 31 | en.MoveNextAsync(); 32 | en.DisposeAsync(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/OnErrorResumeNextTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class OnErrorResumeNextTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .OnErrorResumeNext(e => AsyncEnumerable.Range(6, 5)) 19 | .AssertResult(1, 2, 3, 4, 5); 20 | } 21 | 22 | [Fact] 23 | public async Task Error_Switch() 24 | { 25 | await AsyncEnumerable.Error(new Exception()) 26 | .OnErrorResumeNext(e => AsyncEnumerable.Range(6, 5)) 27 | .AssertResult(6, 7, 8, 9, 10); 28 | } 29 | 30 | [Fact] 31 | public async Task Handler_Crash() 32 | { 33 | await AsyncEnumerable.Error(new InvalidOperationException()) 34 | .OnErrorResumeNext(e => throw e) 35 | .AssertFailure(typeof(AggregateException)); 36 | } 37 | 38 | [Fact] 39 | public async Task Error_DisposeSource() 40 | { 41 | var disposed = false; 42 | await AsyncEnumerable.Error(new Exception()) 43 | .DoOnDispose(() => disposed = true) 44 | .OnErrorResumeNext(e => AsyncEnumerable.Empty()) 45 | .AssertResult(); 46 | Assert.True(disposed); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/PrefetchTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using async_enumerable_dotnet; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class PrefetchTest 14 | { 15 | [Fact] 16 | public async Task Normal() 17 | { 18 | await AsyncEnumerable.Range(1, 10) 19 | .Prefetch(2) 20 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 21 | } 22 | 23 | [Fact] 24 | public async Task Normal_1() 25 | { 26 | await AsyncEnumerable.Range(1, 10) 27 | .Prefetch(1) 28 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 29 | } 30 | 31 | [Fact] 32 | public async Task Error() 33 | { 34 | await AsyncEnumerable.Range(1, 10) 35 | .WithError(new InvalidOperationException()) 36 | .Prefetch(2) 37 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 38 | } 39 | 40 | [Fact] 41 | public async Task Fetch_More_Than_The_Length() 42 | { 43 | await AsyncEnumerable.Range(1, 10) 44 | .Prefetch(20) 45 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 46 | } 47 | 48 | [Fact] 49 | public async Task Long_Fetch_1000() 50 | { 51 | await AsyncEnumerable.Range(1, 1_000_000) 52 | .Prefetch(1000) 53 | .Last() 54 | .AssertResult(1_000_000); 55 | } 56 | 57 | [Fact] 58 | public async Task Long_Fetch_1000_Limit_500() 59 | { 60 | await AsyncEnumerable.Range(1, 1_000_000) 61 | .Prefetch(1000, 500) 62 | .Last() 63 | .AssertResult(1_000_000); 64 | } 65 | 66 | [Fact] 67 | public async Task Take() 68 | { 69 | await AsyncEnumerable.Interval(1, 5, TimeSpan.FromMilliseconds(200)) 70 | .Prefetch(128) 71 | .Take(1) 72 | .AssertResult(1L); 73 | } 74 | 75 | [Fact] 76 | public async Task Cancel() 77 | { 78 | await AsyncEnumerable.FromTask(Task.FromCanceled(new CancellationToken(true))) 79 | .Prefetch(1) 80 | .AssertFailure(typeof(OperationCanceledException)); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/PublishTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class PublishTest 13 | { 14 | [Fact] 15 | public async Task Simple() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .Publish(a => a.Map(v => v + 1)) 19 | .AssertResult(2, 3, 4, 5, 6); 20 | } 21 | 22 | [Fact] 23 | public async Task Direct() 24 | { 25 | await AsyncEnumerable.Range(1, 5) 26 | .Publish(a => a) 27 | .AssertResult(1, 2, 3, 4, 5); 28 | } 29 | 30 | [Fact] 31 | public async Task Take() 32 | { 33 | await AsyncEnumerable.Range(1, 5) 34 | .Publish(a => a.Map(v => v + 1)) 35 | .Take(3) 36 | .AssertResult(2, 3, 4); 37 | } 38 | 39 | [Fact] 40 | public async Task Take_Inner() 41 | { 42 | await AsyncEnumerable.Range(1, 5) 43 | .Publish(a => a.Take(3)) 44 | .AssertResult(1, 2, 3); 45 | } 46 | 47 | [Fact] 48 | public async Task Take_Concat_Inner() 49 | { 50 | await AsyncEnumerable.Range(1, 5) 51 | .Publish(a => a.Take(3).ConcatWith(AsyncEnumerable.Range(4, 2))) 52 | .AssertResult(1, 2, 3, 4, 5); 53 | } 54 | 55 | [Fact] 56 | public async Task Multicast_Merge() 57 | { 58 | await AsyncEnumerable.Range(1, 5) 59 | .Publish(a => a.Take(3).MergeWith(a.Skip(3))) 60 | .AssertResultSet(1, 2, 3, 4, 5); 61 | } 62 | 63 | [Fact] 64 | public async Task Multicast_Concat() 65 | { 66 | await AsyncEnumerable.Range(1, 5) 67 | .Publish(a => a.Take(3).ConcatWith(a)) 68 | .AssertThen(list => { 69 | // concat may not switch fast enough so publish just runs through 70 | Assert.True(list.Count >= 3, "Items missing: " + list); 71 | }); 72 | } 73 | 74 | 75 | [Fact] 76 | public async Task Unrelated() 77 | { 78 | await AsyncEnumerable.Range(1, 5) 79 | .Publish(a => AsyncEnumerable.Range(1, 5) 80 | .Take(3) 81 | .ConcatWith(AsyncEnumerable.Range(4, 2)) 82 | ) 83 | .AssertResult(1, 2, 3, 4, 5); 84 | } 85 | 86 | 87 | [Fact] 88 | public async Task Handler_Crash() 89 | { 90 | await AsyncEnumerable.Range(1, 5) 91 | .Publish(v => throw new InvalidOperationException()) 92 | .AssertFailure(typeof(InvalidOperationException)); 93 | } 94 | 95 | 96 | [Fact] 97 | public async Task Error() 98 | { 99 | await AsyncEnumerable.Error(new InvalidOperationException()) 100 | .Publish(v => v.Map(w => w + 1)) 101 | .AssertFailure(typeof(InvalidOperationException)); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/QueueDrainHelperTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | using async_enumerable_dotnet.impl; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class QueueDrainHelperTest 13 | { 14 | private int _disposeWip; 15 | private Exception _disposeError; 16 | private readonly TaskCompletionSource _disposeTask = new TaskCompletionSource(); 17 | 18 | [Fact] 19 | public void DisposeCrash() 20 | { 21 | var task = Task.FromException(new InvalidOperationException()); 22 | _disposeWip = 1; 23 | 24 | QueueDrainHelper.DisposeHandler(task, ref _disposeWip, ref _disposeError, _disposeTask); 25 | 26 | Assert.Equal(0, _disposeWip); 27 | Assert.Null(_disposeError); 28 | 29 | Assert.True(_disposeTask.Task.IsFaulted); 30 | 31 | Assert.True(_disposeTask.Task.Exception.InnerExceptions[0] is InvalidOperationException); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/RangeTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class RangeTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | var result = AsyncEnumerable.Range(1, 5); 17 | 18 | await result.AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Fact] 22 | public async Task Empty() 23 | { 24 | var result = AsyncEnumerable.Range(1, 0); 25 | 26 | await result.AssertResult(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ReduceTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ReduceTest 13 | { 14 | [Fact] 15 | public async Task NoSeed_Normal() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .Reduce((a, b) => a + b) 19 | .AssertResult(15); 20 | } 21 | 22 | [Fact] 23 | public async Task NoSeed_Empty() 24 | { 25 | await AsyncEnumerable.Empty() 26 | .Reduce((a, b) => a + b) 27 | .AssertResult(); 28 | } 29 | 30 | [Fact] 31 | public async Task WithSeed_Normal() 32 | { 33 | await AsyncEnumerable.Range(1, 5) 34 | .Reduce(() => 10, (a, b) => a + b) 35 | .AssertResult(25); 36 | } 37 | 38 | [Fact] 39 | public async Task WithSeed_Empty() 40 | { 41 | await AsyncEnumerable.Empty() 42 | .Reduce(() => 10, (a, b) => a + b) 43 | .AssertResult(10); 44 | } 45 | 46 | [Fact] 47 | public async Task WithSeed_Crash() 48 | { 49 | await AsyncEnumerable.Empty() 50 | .Reduce(() => throw new InvalidOperationException(), (a, b) => a + b) 51 | .AssertFailure(typeof(InvalidOperationException)); 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/RepeatTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class RepeatTest 12 | { 13 | [Fact] 14 | public async Task Unlimited() 15 | { 16 | await AsyncEnumerable.Just(1) 17 | .Repeat() 18 | .Take(5) 19 | .AssertResult(1, 1, 1, 1, 1); 20 | } 21 | 22 | [Fact] 23 | public async Task Limited() 24 | { 25 | await AsyncEnumerable.Range(1, 2) 26 | .Repeat(3) 27 | .AssertResult(1, 2, 1, 2, 1, 2); 28 | } 29 | 30 | [Fact] 31 | public async Task Limited_Condition() 32 | { 33 | await AsyncEnumerable.Range(1, 2) 34 | .Repeat(n => n < 2) 35 | .AssertResult(1, 2, 1, 2, 1, 2); 36 | } 37 | 38 | [Fact] 39 | public async Task Limited_Condition_Task() 40 | { 41 | await AsyncEnumerable.Range(1, 2) 42 | .Repeat(async n => { 43 | await Task.Delay(100); 44 | return n < 2; 45 | }) 46 | .AssertResult(1, 2, 1, 2, 1, 2); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ReplayTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ReplayTest 13 | { 14 | [Fact] 15 | public async Task All_Direct() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .Replay(v => v) 19 | .AssertResult(1, 2, 3, 4, 5); 20 | } 21 | 22 | [Fact] 23 | public async Task All_Simple() 24 | { 25 | await AsyncEnumerable.Range(1, 5) 26 | .Replay(v => v.Map(w => w + 1)) 27 | .AssertResult(2, 3, 4, 5, 6); 28 | } 29 | 30 | [Fact] 31 | public async Task All_Take() 32 | { 33 | await AsyncEnumerable.Range(1, 5) 34 | .Replay(v => v.Take(3)) 35 | .AssertResult(1, 2, 3); 36 | } 37 | 38 | [Fact] 39 | public async Task All_Recombine() 40 | { 41 | await AsyncEnumerable.Range(1, 5) 42 | .Replay(v => v.Take(3).ConcatWith(v.Skip(3))) 43 | .AssertResult(1, 2, 3, 4, 5); 44 | } 45 | 46 | [Fact] 47 | public async Task All_Twice() 48 | { 49 | await AsyncEnumerable.Range(1, 5) 50 | .Replay(v => v.ConcatWith(v)) 51 | .AssertResult(1, 2, 3, 4, 5, 1, 2, 3, 4, 5); 52 | } 53 | 54 | [Fact] 55 | public async Task All_Handler_Crash() 56 | { 57 | await AsyncEnumerable.Range(1, 5) 58 | .Replay(v => throw new InvalidOperationException()) 59 | .AssertFailure(typeof(InvalidOperationException)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ResumeHelperTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using System.Threading.Tasks; 8 | using async_enumerable_dotnet.impl; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ResumeHelperTest 13 | { 14 | private TaskCompletionSource _tcs; 15 | 16 | [Fact] 17 | public void Value_Cancelled() 18 | { 19 | var source = new TaskCompletionSource(); 20 | source.TrySetCanceled(); 21 | 22 | ResumeHelper.Complete(ref _tcs, new ValueTask(source.Task)); 23 | 24 | Assert.True(_tcs.Task.IsCanceled); 25 | } 26 | 27 | [Fact] 28 | public void Value_Error() 29 | { 30 | var source = new TaskCompletionSource(); 31 | var ex = new InvalidOperationException(); 32 | source.TrySetException(ex); 33 | 34 | ResumeHelper.Complete(ref _tcs, new ValueTask(source.Task)); 35 | 36 | Assert.True(_tcs.Task.IsFaulted); 37 | Assert.Same(ex, ExceptionHelper.Extract(ExceptionHelper.Extract(_tcs.Task.Exception))); 38 | } 39 | 40 | 41 | [Fact] 42 | public void Value_Success() 43 | { 44 | var source = new TaskCompletionSource(); 45 | source.TrySetResult(true); 46 | 47 | ResumeHelper.Complete(ref _tcs, new ValueTask(source.Task)); 48 | 49 | Assert.True(_tcs.Task.IsCompleted); 50 | Assert.True(_tcs.Task.Result); 51 | } 52 | 53 | [Fact] 54 | public async Task Async_Cancelled() 55 | { 56 | var source = new TaskCompletionSource(); 57 | 58 | ResumeHelper.Complete(ref _tcs, new ValueTask(source.Task)); 59 | 60 | source.TrySetCanceled(); 61 | 62 | try 63 | { 64 | await _tcs.Task; 65 | } 66 | catch (Exception) 67 | { 68 | // ignored 69 | } 70 | 71 | Assert.True(_tcs.Task.IsCanceled); 72 | } 73 | 74 | [Fact] 75 | public async Task Async_Error() 76 | { 77 | var source = new TaskCompletionSource(); 78 | 79 | ResumeHelper.Complete(ref _tcs, new ValueTask(source.Task)); 80 | 81 | var ex = new InvalidOperationException(); 82 | source.TrySetException(ex); 83 | 84 | try 85 | { 86 | await _tcs.Task; 87 | } 88 | catch (AggregateException g) 89 | { 90 | Assert.Same(ex, ExceptionHelper.Extract(ExceptionHelper.Extract(g))); 91 | } 92 | 93 | Assert.True(_tcs.Task.IsFaulted); 94 | } 95 | 96 | 97 | [Fact] 98 | public async Task Async_Success() 99 | { 100 | var source = new TaskCompletionSource(); 101 | 102 | ResumeHelper.Complete(ref _tcs, new ValueTask(source.Task)); 103 | 104 | source.TrySetResult(true); 105 | 106 | await _tcs.Task; 107 | 108 | Assert.True(_tcs.Task.IsCompleted); 109 | Assert.True(_tcs.Task.Result); 110 | } 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/RetryTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class RetryTest 13 | { 14 | [Fact] 15 | public async Task Retry_Unlimited() 16 | { 17 | await AsyncEnumerable.Range(1, 2) 18 | .WithError(new Exception()) 19 | .Retry() 20 | .Take(6) 21 | .AssertResult(1, 2, 1, 2, 1, 2); 22 | } 23 | 24 | [Fact] 25 | public async Task Retry_Max() 26 | { 27 | await AsyncEnumerable.Range(1, 2) 28 | .WithError(new InvalidOperationException()) 29 | .Retry(3) 30 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 1, 2, 1, 2); 31 | } 32 | 33 | [Fact] 34 | public async Task Retry_Condition() 35 | { 36 | await AsyncEnumerable.Range(1, 2) 37 | .WithError(new InvalidOperationException()) 38 | .Retry((idx, ex) => idx < 2) 39 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 1, 2, 1, 2); 40 | } 41 | 42 | [Fact] 43 | public async Task Retry_Condition_False() 44 | { 45 | await AsyncEnumerable.Range(1, 2) 46 | .WithError(new InvalidOperationException()) 47 | .Retry((idx, ex) => false) 48 | .AssertFailure(typeof(InvalidOperationException), 1, 2); 49 | } 50 | 51 | [Fact] 52 | public async Task Retry_Condition_Task() 53 | { 54 | await AsyncEnumerable.Range(1, 2) 55 | .WithError(new InvalidOperationException()) 56 | .Retry(async (idx, ex) => { 57 | await Task.Delay(100); 58 | return idx < 2; 59 | }) 60 | .AssertFailure(typeof(InvalidOperationException), 1, 2, 1, 2, 1, 2); 61 | } 62 | 63 | [Fact] 64 | public async Task Retry_Condition_Task_False() 65 | { 66 | await AsyncEnumerable.Range(1, 2) 67 | .WithError(new InvalidOperationException()) 68 | .Retry(async (idx, ex) => 69 | { 70 | await Task.Delay(100); 71 | return false; 72 | }) 73 | .AssertFailure(typeof(InvalidOperationException), 1, 2); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SampleTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class SampleTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | var t = 200; 18 | if (Environment.GetEnvironmentVariable("CI") != null) 19 | { 20 | t = 2000; 21 | } 22 | 23 | await AsyncEnumerable.Range(1, 5) 24 | .FlatMap(v => 25 | AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(t * v - t / 2)) 26 | .Map(w => v) 27 | ) 28 | .Sample(TimeSpan.FromMilliseconds(t * 2)) 29 | .AssertResult(2, 4); 30 | } 31 | 32 | [Fact] 33 | public async Task Last() 34 | { 35 | await AsyncEnumerable.Range(1, 5) 36 | .Sample(TimeSpan.FromMilliseconds(500)) 37 | .AssertResult(); 38 | } 39 | 40 | [Fact] 41 | public async Task Normal_EmitLast() 42 | { 43 | var t = 200; 44 | if (Environment.GetEnvironmentVariable("CI") != null) 45 | { 46 | t = 2000; 47 | } 48 | 49 | await AsyncEnumerable.Range(1, 5) 50 | .FlatMap(v => 51 | AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(t * v - t / 2)) 52 | .Map(w => v) 53 | ) 54 | .Sample(TimeSpan.FromMilliseconds(t * 2), true) 55 | .AssertResult(2, 4, 5); 56 | } 57 | 58 | [Fact] 59 | public async Task Last_EmitLast() 60 | { 61 | await AsyncEnumerable.Range(1, 5) 62 | .Sample(TimeSpan.FromMilliseconds(500), true) 63 | .AssertResult(5); 64 | } 65 | 66 | [Fact] 67 | public async Task Error() 68 | { 69 | await AsyncEnumerable.Error(new InvalidOperationException()) 70 | .Sample(TimeSpan.FromMilliseconds(500)) 71 | .AssertFailure(typeof(InvalidOperationException)); 72 | } 73 | 74 | [Fact] 75 | public async Task Error_EmitLast() 76 | { 77 | await AsyncEnumerable.Error(new InvalidOperationException()) 78 | .Sample(TimeSpan.FromMilliseconds(500), true) 79 | .AssertFailure(typeof(InvalidOperationException)); 80 | } 81 | 82 | [Fact] 83 | public async Task Take() 84 | { 85 | await TestHelper.TimeSequence(100, 300) 86 | .Sample(TimeSpan.FromMilliseconds(200)) 87 | .Take(1) 88 | .AssertResult(100); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ScanTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class ScanTest 13 | { 14 | [Fact] 15 | public async Task NoSeed_Some() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .Scan((a, b) => a + b) 19 | .AssertResult(1, 3, 6, 10, 15); 20 | } 21 | 22 | [Fact] 23 | public async Task NoSeed_One() 24 | { 25 | await AsyncEnumerable.Just(1) 26 | .Scan((a, b) => a + b) 27 | .AssertResult(1); 28 | } 29 | 30 | [Fact] 31 | public async Task NoSeed_None() 32 | { 33 | await AsyncEnumerable.Empty() 34 | .Scan((a, b) => a + b) 35 | .AssertResult(); 36 | } 37 | 38 | [Fact] 39 | public async Task Seed_Some() 40 | { 41 | await AsyncEnumerable.Range(1, 5) 42 | .Scan(() => 100, (a, b) => a + b) 43 | .AssertResult(100, 101, 103, 106, 110, 115); 44 | } 45 | 46 | [Fact] 47 | public async Task Seed_One() 48 | { 49 | await AsyncEnumerable.Just(1) 50 | .Scan(() => 100, (a, b) => a + b) 51 | .AssertResult(100, 101); 52 | } 53 | 54 | [Fact] 55 | public async Task Seed_None() 56 | { 57 | await AsyncEnumerable.Empty() 58 | .Scan(() => 100, (a, b) => a + b) 59 | .AssertResult(100); 60 | } 61 | 62 | [Fact] 63 | public async Task Seed_Crash() 64 | { 65 | await AsyncEnumerable.Empty() 66 | .Scan(() => throw new InvalidOperationException(), (a, b) => a + b) 67 | .AssertFailure(typeof(InvalidOperationException)); 68 | } 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SkipLastTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class SkipLastTest 12 | { 13 | [Fact] 14 | public async Task Skip_None() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .SkipLast(0) 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Fact] 22 | public async Task Skip_Some() 23 | { 24 | await AsyncEnumerable.Range(1, 5) 25 | .SkipLast(2) 26 | .AssertResult(1, 2, 3); 27 | } 28 | 29 | [Fact] 30 | public async Task Skip_All() 31 | { 32 | await AsyncEnumerable.Range(1, 5) 33 | .SkipLast(5) 34 | .AssertResult(); 35 | } 36 | 37 | [Fact] 38 | public async Task Skip_More() 39 | { 40 | await AsyncEnumerable.Range(1, 5) 41 | .SkipLast(10) 42 | .AssertResult(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SkipTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class SkipTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | await AsyncEnumerable.Range(1, 10) 17 | .Skip(5) 18 | .AssertResult(6, 7, 8, 9, 10); 19 | } 20 | 21 | [Fact] 22 | public async Task Zero() 23 | { 24 | await AsyncEnumerable.Range(1, 10) 25 | .Skip(0) 26 | .AssertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 27 | } 28 | 29 | [Fact] 30 | public async Task All() 31 | { 32 | await AsyncEnumerable.Range(1, 10) 33 | .Skip(10) 34 | .AssertResult(); 35 | } 36 | 37 | [Fact] 38 | public async Task More() 39 | { 40 | await AsyncEnumerable.Range(1, 10) 41 | .Skip(11) 42 | .AssertResult(); 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SkipUntilTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class SkipUntilTest 13 | { 14 | [Fact] 15 | public async Task Skip_All() 16 | { 17 | await AsyncEnumerable.Range(1, 5) 18 | .SkipUntil(AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(250))) 19 | .AssertResult(); 20 | } 21 | 22 | [Fact] 23 | public async Task Skip_None() 24 | { 25 | await AsyncEnumerable.Range(1, 5) 26 | .ConcatMap(v => AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(100)).Map(w => v)) 27 | .SkipUntil(AsyncEnumerable.Empty()) 28 | .AssertResult(1, 2, 3, 4, 5); 29 | } 30 | 31 | [Fact] 32 | public async Task Skip_Some() 33 | { 34 | var scale = 200; 35 | if (Environment.GetEnvironmentVariable("CI") != null) 36 | { 37 | scale = 2000; 38 | } 39 | await AsyncEnumerable.Range(1, 5) 40 | .FlatMap(v => AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(v * scale)).Map(w => v)) 41 | // ReSharper disable once PossibleLossOfFraction 42 | .SkipUntil(AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(5 * scale / 2))) 43 | .AssertResult(3, 4, 5); 44 | } 45 | 46 | [Fact] 47 | public async Task Error_Main() 48 | { 49 | await AsyncEnumerable.Error(new InvalidOperationException()) 50 | .SkipUntil(AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(200))) 51 | .AssertFailure(typeof(InvalidOperationException)); 52 | } 53 | 54 | [Fact] 55 | public async Task Error_Other() 56 | { 57 | await AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(200)) 58 | .SkipUntil(AsyncEnumerable.Error(new InvalidOperationException())) 59 | .AssertFailure(typeof(InvalidOperationException)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SkipWhileTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class SkipWhileTest 12 | { 13 | [Fact] 14 | public async Task Skip_All() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .SkipWhile(v => v < 6) 18 | .AssertResult(); 19 | } 20 | 21 | [Fact] 22 | public async Task Skip_Some() 23 | { 24 | await AsyncEnumerable.Range(1, 5) 25 | .SkipWhile(v => v < 3) 26 | .AssertResult(3, 4, 5); 27 | } 28 | 29 | [Fact] 30 | public async Task Skip_None() 31 | { 32 | await AsyncEnumerable.Range(1, 5) 33 | .SkipWhile(v => v < 1) 34 | .AssertResult(1, 2, 3, 4, 5); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SlimResumeTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet.impl; 8 | using System.Threading.Tasks; 9 | using System.Threading; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class SlimResumeTest 14 | { 15 | [Fact] 16 | public async Task ReadyUpfront() 17 | { 18 | var rsm = new SlimResume(); 19 | rsm.Signal(); 20 | 21 | await rsm; 22 | } 23 | 24 | [Fact] 25 | public async Task Completed() 26 | { 27 | var rsm = SlimResume.Completed; 28 | 29 | await rsm; 30 | } 31 | 32 | [Fact] 33 | public async Task SignalLater() 34 | { 35 | var rsm = new SlimResume(); 36 | 37 | var t = Task.Delay(100) 38 | .ContinueWith(t0 => rsm.Signal()); 39 | 40 | await rsm; 41 | 42 | await t; 43 | } 44 | 45 | 46 | [Fact] 47 | public async Task Race() 48 | { 49 | for (var i = 0; i < 10_000; i++) 50 | { 51 | var rsm = new SlimResume(); 52 | 53 | var wip = 2; 54 | 55 | var t1 = Task.Factory.StartNew(() => 56 | { 57 | if (Interlocked.Decrement(ref wip) != 0) 58 | { 59 | while (Volatile.Read(ref wip) != 0) { } 60 | } 61 | 62 | rsm.Signal(); 63 | }, TaskCreationOptions.LongRunning); 64 | 65 | var t2 = Task.Factory.StartNew(async () => 66 | { 67 | if (Interlocked.Decrement(ref wip) != 0) 68 | { 69 | while (Volatile.Read(ref wip) != 0) { } 70 | } 71 | 72 | await rsm; 73 | }, TaskCreationOptions.LongRunning); 74 | 75 | await t1; 76 | 77 | await t2; 78 | } 79 | } 80 | 81 | [Fact] 82 | public void OneAwaiterMax() 83 | { 84 | var rsm = new SlimResume(); 85 | 86 | rsm.OnCompleted(() => { }); 87 | 88 | try 89 | { 90 | rsm.OnCompleted(() => { }); 91 | Assert.False(true, "Should have thrown"); 92 | } 93 | catch (InvalidOperationException) 94 | { 95 | // expected 96 | } 97 | } 98 | 99 | [Fact] 100 | public void DoubleSignal() 101 | { 102 | SlimResume.Completed.Signal(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/SwitchIfEmptyTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class SwitchIfEmptyTest 12 | { 13 | [Fact] 14 | public async Task NonEmpty() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .SwitchIfEmpty(AsyncEnumerable.Range(11, 5)) 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Fact] 22 | public async Task Empty() 23 | { 24 | await AsyncEnumerable.Empty() 25 | .SwitchIfEmpty(AsyncEnumerable.Range(11, 5)) 26 | .AssertResult(11, 12, 13, 14, 15); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TakeLastTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class TakeLastTest 12 | { 13 | [Fact] 14 | public async Task More() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .TakeLast(10) 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Fact] 22 | public async Task All() 23 | { 24 | await AsyncEnumerable.Range(1, 5) 25 | .TakeLast(5) 26 | .AssertResult(1, 2, 3, 4, 5); 27 | } 28 | 29 | [Fact] 30 | public async Task Some() 31 | { 32 | await AsyncEnumerable.Range(1, 5) 33 | .TakeLast(2) 34 | .AssertResult(4, 5); 35 | } 36 | 37 | [Fact] 38 | public async Task None() 39 | { 40 | await AsyncEnumerable.Range(1, 5) 41 | .TakeLast(0) 42 | .AssertResult(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TakeTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class TakeTest 12 | { 13 | [Fact] 14 | public async Task Normal() 15 | { 16 | var result = AsyncEnumerable.FromArray(1, 2, 3, 4, 5) 17 | .Take(3) 18 | ; 19 | 20 | await result.AssertResult(1, 2, 3); 21 | } 22 | 23 | [Fact] 24 | public async Task More() 25 | { 26 | var result = AsyncEnumerable.FromArray(1, 2, 3, 4, 5) 27 | .Take(6) 28 | ; 29 | 30 | await result.AssertResult(1, 2, 3, 4, 5); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TakeUntilTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class TakeUntilTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | var t = 500; 18 | if (Environment.GetEnvironmentVariable("CI") != null) 19 | { 20 | t = 3000; 21 | } 22 | var disposedMain = 0; 23 | var disposedOther = 0; 24 | 25 | await AsyncEnumerable.Range(1, 5) 26 | .DoOnDispose(() => disposedMain++) 27 | .TakeUntil( 28 | AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(t)) 29 | .DoOnDispose(() => disposedOther += 2) 30 | ) 31 | .AssertResult(1, 2, 3, 4, 5); 32 | 33 | Assert.Equal(1, disposedMain); 34 | Assert.Equal(2, disposedOther); 35 | } 36 | 37 | [Fact] 38 | public async Task Until() 39 | { 40 | var disposedMain = 0; 41 | var disposedOther = 0; 42 | 43 | await AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(500)) 44 | .DoOnDispose(() => disposedMain++) 45 | .TakeUntil( 46 | AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(100)) 47 | .DoOnDispose(() => disposedOther += 2) 48 | ) 49 | .AssertResult(); 50 | 51 | Assert.Equal(1, disposedMain); 52 | Assert.Equal(2, disposedOther); 53 | } 54 | 55 | [Fact] 56 | public async Task MainError() 57 | { 58 | await AsyncEnumerable.Error(new InvalidOperationException()) 59 | .TakeUntil(AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(200))) 60 | .AssertFailure(typeof(InvalidOperationException)); 61 | 62 | } 63 | 64 | 65 | [Fact] 66 | public async Task OtherError() 67 | { 68 | await AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(200)) 69 | .TakeUntil(AsyncEnumerable.Error(new InvalidOperationException())) 70 | .AssertFailure(typeof(InvalidOperationException)); 71 | 72 | } 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TakeWhileTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class TakeWhileTest 12 | { 13 | [Fact] 14 | public async Task All_Pass() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .TakeWhile(v => true) 18 | .AssertResult(1, 2, 3, 4, 5); 19 | } 20 | 21 | [Fact] 22 | public async Task Some_Pass() 23 | { 24 | await AsyncEnumerable.Range(1, 5) 25 | .TakeWhile(v => v < 4) 26 | .AssertResult(1, 2, 3); 27 | } 28 | 29 | [Fact] 30 | public async Task None_Pass() 31 | { 32 | await AsyncEnumerable.Range(1, 5) 33 | .TakeWhile(v => false) 34 | .AssertResult(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TestHelperTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class TestHelperTest 13 | { 14 | [Fact] 15 | public async Task AssertResultSet() 16 | { 17 | await AsyncEnumerable.Range(1, 3) 18 | .AssertResultSet(1, 2, 3); 19 | } 20 | 21 | [Fact] 22 | public async Task AssertResultSet_List() 23 | { 24 | await AsyncEnumerable.FromArray(new List(new [] { 1, 2, 3 })) 25 | .AssertResultSet( 26 | ListComparer.Default, 27 | new List(new[] { 1, 2, 3 })); 28 | } 29 | 30 | [Fact] 31 | public void HashSet_Contains() 32 | { 33 | var set = new HashSet>(ListComparer.Default) {new List(new[] {1, 2, 3})}; 34 | 35 | 36 | Assert.Contains(new List(new[] { 1, 2, 3 }), set); 37 | 38 | Assert.True(set.Remove(new List(new[] { 1, 2, 3 }))); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TimeoutTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using async_enumerable_dotnet; 6 | using System; 7 | using Xunit; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class TimeoutTest 13 | { 14 | [Fact] 15 | public async Task NoTimeout() 16 | { 17 | var result = AsyncEnumerable.FromArray(1, 2, 3, 4, 5) 18 | .Timeout(TimeSpan.FromMilliseconds(200)) 19 | ; 20 | 21 | await result.AssertResult(1, 2, 3, 4, 5); 22 | } 23 | 24 | [Fact] 25 | public async Task HasTimeout() 26 | { 27 | var disposed = 0; 28 | 29 | var result = AsyncEnumerable.Timer(TimeSpan.FromSeconds(1)) 30 | .DoOnDispose(() => disposed++) 31 | .Timeout(TimeSpan.FromMilliseconds(100)) 32 | ; 33 | 34 | await result.AssertFailure(typeof(TimeoutException)); 35 | 36 | Assert.Equal(1, disposed); 37 | } 38 | 39 | [Fact] 40 | public async Task Error() 41 | { 42 | await AsyncEnumerable.Error(new InvalidOperationException()) 43 | .Timeout(TimeSpan.FromMilliseconds(10000)) 44 | .AssertFailure(typeof(InvalidOperationException)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/TimerTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading; 7 | using Xunit; 8 | using async_enumerable_dotnet; 9 | using System.Threading.Tasks; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class TimerTest 14 | { 15 | [Fact] 16 | public async Task Token() 17 | { 18 | var value = 0; 19 | 20 | var cts = new CancellationTokenSource(); 21 | #pragma warning disable 4014 22 | AsyncEnumerable.Timer(TimeSpan.FromSeconds(200), cts.Token) 23 | .DoOnNext(v => value = 1) 24 | .GetAsyncEnumerator(default) 25 | .MoveNextAsync(); 26 | #pragma warning restore 4014 27 | 28 | // ReSharper disable once MethodSupportsCancellation 29 | await Task.Delay(100); 30 | 31 | cts.Cancel(); 32 | 33 | // ReSharper disable once MethodSupportsCancellation 34 | await Task.Delay(200); 35 | 36 | Assert.Equal(0, value); 37 | } 38 | 39 | [Fact] 40 | public async Task Normal() 41 | { 42 | var cts = new CancellationTokenSource(); 43 | await AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(100), cts.Token) 44 | .AssertResult(0L); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ToCollectionTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | using async_enumerable_dotnet; 8 | 9 | using Xunit; 10 | using System.Threading.Tasks; 11 | 12 | namespace async_enumerable_dotnet_test 13 | { 14 | public class ToCollectionTest 15 | { 16 | [Fact] 17 | public async Task ToListAllowsEmptySource() 18 | { 19 | var result = await AsyncEnumerable.FromArray(new int[0]).ToListAsync(); 20 | Assert.NotNull(result); 21 | Assert.Empty(result); 22 | } 23 | 24 | [Fact] 25 | public async Task ToListAlwaysReturnsNewInstance() 26 | { 27 | var source = AsyncEnumerable.FromArray(new int[0]); 28 | var result1 = await source.ToListAsync(); 29 | var result2 = await source.ToListAsync(); 30 | Assert.NotSame(result1, result2); 31 | } 32 | 33 | [Fact] 34 | public async Task ToListReturnsAllItems() 35 | { 36 | var result = await AsyncEnumerable.Range(0, 10).ToListAsync(); 37 | var usedNumbers = new HashSet(result); 38 | Assert.Equal(10, result.Count); 39 | for (var i = 0; i != 10; ++i) 40 | { 41 | Assert.Contains(i, usedNumbers); 42 | } 43 | } 44 | 45 | [Fact] 46 | public async Task ToArrayAllowsEmptySource() 47 | { 48 | var result = await AsyncEnumerable.FromArray(new int[0]).ToArrayAsync(); 49 | Assert.NotNull(result); 50 | Assert.Empty(result); 51 | } 52 | 53 | [Fact] 54 | public async Task ToArrayAlwaysReturnsNewInstanceForEmptySource() 55 | { 56 | var source = AsyncEnumerable.FromArray(new int[0]); 57 | var result1 = await source.ToArrayAsync(); 58 | var result2 = await source.ToArrayAsync(); 59 | Assert.Same(result1, result2); 60 | } 61 | 62 | [Fact] 63 | public async Task ToArrayReturnsAllItems() 64 | { 65 | var result = await AsyncEnumerable.Range(0, 10).ToArrayAsync(); 66 | var usedNumbers = new HashSet(result); 67 | Assert.Equal(10, result.Length); 68 | for (var i = 0; i != 10; ++i) 69 | { 70 | Assert.Contains(i, usedNumbers); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ToEnumerableTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections; 7 | using Xunit; 8 | using async_enumerable_dotnet; 9 | using System.Linq; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class ToEnumerableTest 14 | { 15 | [Fact] 16 | public void Normal() 17 | { 18 | var list = AsyncEnumerable.Range(1, 5).ToEnumerable().ToList(); 19 | 20 | Assert.Equal(new[] { 1, 2, 3, 4, 5 }, list); 21 | } 22 | 23 | [Fact] 24 | public void NonGeneric() 25 | { 26 | IEnumerable en = AsyncEnumerable.Range(1, 5).ToEnumerable(); 27 | 28 | var enumerator = en.GetEnumerator(); 29 | 30 | for (var i = 1; i <= 5; i++) 31 | { 32 | Assert.True(enumerator.MoveNext()); 33 | Assert.Equal(i, enumerator.Current); 34 | } 35 | Assert.False(enumerator.MoveNext()); 36 | 37 | try 38 | { 39 | enumerator.Reset(); 40 | Assert.False(true, "Should have thrown"); 41 | } 42 | catch (InvalidOperationException) 43 | { 44 | // expected 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ToListTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class ToListTest 14 | { 15 | [Fact] 16 | public async Task Normal() 17 | { 18 | await AsyncEnumerable.Range(1, 5) 19 | .ToList() 20 | .AssertResult(new List(new[] { 1, 2, 3, 4, 5 })); 21 | } 22 | 23 | [Fact] 24 | public async Task Empty() 25 | { 26 | await AsyncEnumerable.Empty() 27 | .ToList() 28 | .AssertResult(new List()); 29 | } 30 | 31 | [Fact] 32 | public async Task Error() 33 | { 34 | await AsyncEnumerable.Error(new InvalidOperationException()) 35 | .ToList() 36 | .AssertFailure(typeof(InvalidOperationException)); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ToObservableTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | using System.Collections.Generic; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class ToObservableTest 14 | { 15 | [Fact] 16 | public async Task Normal() 17 | { 18 | var result = AsyncEnumerable.Range(1, 5) 19 | .ToObservable(); 20 | 21 | var consumer = new BasicObserver(); 22 | 23 | using (result.Subscribe(consumer)) 24 | { 25 | await consumer.TerminateTask; 26 | 27 | Assert.Equal(new List(new[] {1, 2, 3, 4, 5}), consumer.Values); 28 | Assert.Null(consumer.Error); 29 | Assert.True(consumer.Completed); 30 | } 31 | } 32 | 33 | [Fact] 34 | public async Task Take() 35 | { 36 | var consumer = new BasicObserver(); 37 | 38 | using (AsyncEnumerable.Timer(TimeSpan.FromMilliseconds(200)) 39 | .ToObservable() 40 | .Subscribe(consumer)) 41 | { 42 | await Task.Delay(100); 43 | } 44 | 45 | await Task.Delay(200); 46 | 47 | Assert.Empty(consumer.Values); 48 | Assert.Null(consumer.Error); 49 | Assert.False(consumer.Completed); 50 | } 51 | 52 | [Fact] 53 | public async Task Error() 54 | { 55 | var ex = new InvalidOperationException(); 56 | var result = AsyncEnumerable.Error(ex) 57 | .ToObservable(); 58 | 59 | var consumer = new BasicObserver(); 60 | 61 | using (result.Subscribe(consumer)) 62 | { 63 | await consumer.TerminateTask; 64 | 65 | Assert.Empty(consumer.Values); 66 | Assert.Same(ex, consumer.Error); 67 | Assert.False(consumer.Completed); 68 | } 69 | } 70 | 71 | private sealed class BasicObserver : IObserver 72 | { 73 | internal readonly IList Values = new List(); 74 | internal bool Completed; 75 | internal Exception Error; 76 | 77 | private readonly TaskCompletionSource _terminate = new TaskCompletionSource(); 78 | 79 | public void OnCompleted() 80 | { 81 | Completed = true; 82 | _terminate.TrySetResult(true); 83 | } 84 | 85 | public void OnError(Exception error) 86 | { 87 | Error = error; 88 | _terminate.TrySetResult(true); 89 | } 90 | 91 | public void OnNext(T value) 92 | { 93 | Values.Add(value); 94 | } 95 | 96 | public Task TerminateTask => _terminate.Task; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet_test 10 | { 11 | public class UnitTest1 12 | { 13 | [Fact] 14 | public async Task Test1() 15 | { 16 | await AsyncEnumerable.Range(1, 5) 17 | .AssertResult(1, 2, 3, 4, 5); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/UsingTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using Xunit; 7 | using async_enumerable_dotnet; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class UsingTest 13 | { 14 | [Fact] 15 | public async Task Normal() 16 | { 17 | var cleanup = 0; 18 | 19 | var result = AsyncEnumerable.Using(() => 1, 20 | v => AsyncEnumerable.FromArray(v), 21 | v => cleanup = v); 22 | 23 | await result.AssertResult(1); 24 | 25 | Assert.Equal(1, cleanup); 26 | } 27 | 28 | [Fact] 29 | public async Task ResourceSupplier_Crash() 30 | { 31 | await AsyncEnumerable.Using(() => throw new InvalidOperationException(), 32 | v => AsyncEnumerable.Range(v, 5), v => { }) 33 | .AssertFailure(typeof(InvalidOperationException)); 34 | } 35 | 36 | [Fact] 37 | public async Task SourceSupplier_Crash() 38 | { 39 | await AsyncEnumerable.Using(() => 1, 40 | v => throw new InvalidOperationException(), v => { }) 41 | .AssertFailure(typeof(InvalidOperationException)); 42 | } 43 | 44 | [Fact] 45 | public async Task SourceSupplier_And_Cleanup_Crash() 46 | { 47 | await AsyncEnumerable.Using(() => 1, 48 | v => throw new InvalidOperationException(), v => throw new IndexOutOfRangeException()) 49 | .AssertFailure(typeof(AggregateException)); 50 | } 51 | 52 | [Fact] 53 | public async Task Cleanup_Crash() 54 | { 55 | try 56 | { 57 | await AsyncEnumerable.Using(() => 1, 58 | v => AsyncEnumerable.Range(v, 5), v => throw new IndexOutOfRangeException()) 59 | .AssertResult(1, 2, 3, 4, 5); 60 | Assert.False(true, "Should have thrown"); 61 | } 62 | catch (IndexOutOfRangeException) 63 | { 64 | // expected 65 | } 66 | } 67 | 68 | [Fact] 69 | public async Task Upstream_Dispose_Crash() 70 | { 71 | try 72 | { 73 | await AsyncEnumerable.Using(() => 1, 74 | v => AsyncEnumerable.Range(v, 5).DoOnDispose(() => throw new InvalidOperationException()), 75 | v => { }) 76 | .AssertResult(1, 2, 3, 4, 5); 77 | Assert.False(true, "Should have thrown!"); 78 | } 79 | catch (InvalidOperationException) 80 | { 81 | // expected 82 | } 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/WithLatestFromTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using Xunit; 6 | using async_enumerable_dotnet; 7 | using System.Threading.Tasks; 8 | using System; 9 | 10 | namespace async_enumerable_dotnet_test 11 | { 12 | public class WithLatestFromTest 13 | { 14 | [Fact] 15 | public async Task Simple() 16 | { 17 | var t = 200; 18 | if (Environment.GetEnvironmentVariable("CI") != null) 19 | { 20 | t = 1000; 21 | } 22 | 23 | await AsyncEnumerable.Range(1, 5) 24 | .DoOnNext(async v => 25 | { 26 | if (v == 1) 27 | { 28 | // otherwise the first value may come too early. 29 | await Task.Delay(t); 30 | } 31 | }) 32 | .WithLatestFrom(AsyncEnumerable.Just(10), (a, b) => a + b) 33 | .AssertResult(11, 12, 13, 14, 15); 34 | } 35 | 36 | [Fact] 37 | public async Task Empty_Main() 38 | { 39 | await AsyncEnumerable.Empty() 40 | .WithLatestFrom(AsyncEnumerable.Just(10), (a, b) => a + b) 41 | .AssertResult(); 42 | } 43 | 44 | 45 | [Fact] 46 | public async Task Error_Main() 47 | { 48 | await AsyncEnumerable.Error(new InvalidOperationException()) 49 | .WithLatestFrom(AsyncEnumerable.Just(10), (a, b) => a + b) 50 | .AssertFailure(typeof(InvalidOperationException)); 51 | } 52 | 53 | [Fact] 54 | public async Task Empty_Other() 55 | { 56 | await AsyncEnumerable.Range(1, 5) 57 | .WithLatestFrom(AsyncEnumerable.Empty(), (a, b) => a + b) 58 | .AssertResult(); 59 | } 60 | 61 | [Fact] 62 | public async Task Error_Other() 63 | { 64 | var t = 200; 65 | if (Environment.GetEnvironmentVariable("CI") != null) 66 | { 67 | t = 1000; 68 | } 69 | 70 | await AsyncEnumerable.Just(1) 71 | .DoOnNext(async v => 72 | { 73 | if (v == 1) 74 | { 75 | // otherwise the first value may come too early. 76 | await Task.Delay(t); 77 | } 78 | }) 79 | .WithLatestFrom(AsyncEnumerable.Error(new InvalidOperationException()), (a, b) => a + b) 80 | .AssertFailure(typeof(InvalidOperationException)); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/ZipTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Linq; 7 | using Xunit; 8 | using async_enumerable_dotnet; 9 | using System.Threading.Tasks; 10 | 11 | namespace async_enumerable_dotnet_test 12 | { 13 | public class ZipTest 14 | { 15 | [Fact] 16 | public async Task SameLength() 17 | { 18 | var source = AsyncEnumerable.FromArray(1, 2, 3, 4); 19 | 20 | var result = AsyncEnumerable.Zip(v => v.Sum(), source, source, source); 21 | 22 | await result.AssertResult(3, 6, 9, 12); 23 | } 24 | 25 | [Fact] 26 | public async Task DifferentLengths() 27 | { 28 | var source1 = AsyncEnumerable.FromArray(1, 2, 3, 4); 29 | var source2 = AsyncEnumerable.FromArray(1, 2, 3); 30 | var source3 = AsyncEnumerable.FromArray(1, 2, 3, 4, 5); 31 | 32 | var result = AsyncEnumerable.Zip(v => v.Sum(), source1, source2, source3); 33 | 34 | await result.AssertResult(3, 6, 9); 35 | } 36 | 37 | [Fact] 38 | public async Task Error_One() 39 | { 40 | await AsyncEnumerable.Zip(v => v.Sum(), 41 | AsyncEnumerable.Error(new InvalidOperationException()), 42 | AsyncEnumerable.Range(1, 5) 43 | ) 44 | .AssertFailure(typeof(InvalidOperationException)); 45 | } 46 | 47 | [Fact] 48 | public async Task Error_Both() 49 | { 50 | await AsyncEnumerable.Zip(v => v.Sum(), 51 | AsyncEnumerable.Error(new InvalidOperationException()), 52 | AsyncEnumerable.Error(new InvalidOperationException()) 53 | ) 54 | .AssertFailure(typeof(AggregateException)); 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /async-enumerable-dotnet-test/async-enumerable-dotnet-test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | async_enumerable_dotnet_test 6 | 7 | false 8 | 9 | latest 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /async-enumerable-dotnet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2046 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "async-enumerable-dotnet", "async-enumerable-dotnet\async-enumerable-dotnet.csproj", "{12363CF4-3B69-4096-8807-E140D6C5AF72}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "async-enumerable-dotnet-test", "async-enumerable-dotnet-test\async-enumerable-dotnet-test.csproj", "{833B1173-D251-4FA9-A18F-D01F99DF8E2F}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "async-enumerable-dotnet-benchmark", "async-enumerable-dotnet-benchmark\async-enumerable-dotnet-benchmark.csproj", "{81929167-8990-418D-9838-8AFE3F7810D2}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {12363CF4-3B69-4096-8807-E140D6C5AF72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {12363CF4-3B69-4096-8807-E140D6C5AF72}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {12363CF4-3B69-4096-8807-E140D6C5AF72}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {12363CF4-3B69-4096-8807-E140D6C5AF72}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {833B1173-D251-4FA9-A18F-D01F99DF8E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {833B1173-D251-4FA9-A18F-D01F99DF8E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {833B1173-D251-4FA9-A18F-D01F99DF8E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {833B1173-D251-4FA9-A18F-D01F99DF8E2F}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {81929167-8990-418D-9838-8AFE3F7810D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {81929167-8990-418D-9838-8AFE3F7810D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {81929167-8990-418D-9838-8AFE3F7810D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {81929167-8990-418D-9838-8AFE3F7810D2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {65318B90-70B4-4DF6-9338-95014428192F} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/IAsyncEmitter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet 10 | { 11 | /// 12 | /// Provides API for generating items. 13 | /// 14 | /// The element type. 15 | public interface IAsyncEmitter 16 | { 17 | /// 18 | /// Push an item and wait until the generator can proceed. 19 | /// 20 | /// The element to produce. 21 | /// The task that should be awaited before calling the method again. 22 | ValueTask Next(T value); 23 | 24 | /// 25 | /// Returns true if the consumer requested stopping a sequence. 26 | /// 27 | bool DisposeAsyncRequested { get; } 28 | 29 | /// 30 | /// Returns the CancellationToken instance supplied by the downstream. 31 | /// 32 | CancellationToken Token { get; } 33 | } 34 | 35 | /// 36 | /// The "push-side" of an IAsyncEnumerator. 37 | /// Each method should be awaited and called in a non-overlapping fashion. 38 | /// 39 | /// The element type. 40 | /// 41 | /// The protocol is Next* (Error | Complete)? 42 | /// 43 | public interface IAsyncConsumer 44 | { 45 | /// 46 | /// Push a value. Can be called multiple times. 47 | /// 48 | /// The value to push. 49 | /// The task to await before calling any of the methods again. 50 | ValueTask Next(T value); 51 | 52 | /// 53 | /// Push a final exception. Can be called at most once. 54 | /// 55 | /// The exception to push. 56 | /// The task to await before calling any of the methods again. 57 | ValueTask Error(Exception ex); 58 | 59 | /// 60 | /// Indicate no more items will be pushed. Can be called at most once. 61 | /// 62 | /// The task to await before calling any of the methods again. 63 | ValueTask Complete(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/IAsyncGroupedEnumerable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace async_enumerable_dotnet 8 | { 9 | /// 10 | /// An IAsyncEnumerable with a key property to support grouping operations. 11 | /// 12 | /// The key type. 13 | /// The element type of the async sequence. 14 | public interface IAsyncGroupedEnumerable : IAsyncEnumerable 15 | { 16 | /// 17 | /// Returns the group key. 18 | /// 19 | // ReSharper disable once UnusedMemberInSuper.Global 20 | TKey Key 21 | { 22 | // ReSharper disable once UnusedMemberInSuper.Global 23 | get; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/async-enumerable-dotnet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0;netstandard2.1 5 | async_enumerable_dotnet 6 | true 7 | latest 8 | akarnokd.async-enumerable-dotnet 9 | 0.0.4.0 10 | David Karnok 11 | 12 | 0.0.4.0 13 | 0.0.4.0 14 | async, concurrency, async-enumerable, operators, async-sequence 15 | https://github.com/akarnokd/async-enumerable-dotnet 16 | https://github.com/akarnokd/async-enumerable-dotnet#getting-started 17 | Experimental operators for the C# 8 IAsyncEnumerables. 18 | (C) David Karnok 19 | 20 | Fix cancellation propagation with many operators 21 | Github 22 | Async Enumerable operators for .NET 23 | Apache-2.0 24 | 25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/ArrayQueue.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | 8 | [assembly: InternalsVisibleTo("async-enumerable-dotnet-test")] 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal struct ArrayQueue 13 | { 14 | private int _producerIndex; 15 | private int _consumerIndex; 16 | 17 | private T[] _array; 18 | 19 | internal ArrayQueue(int capacity) 20 | { 21 | _producerIndex = 0; 22 | _consumerIndex = 0; 23 | _array = new T[capacity]; 24 | } 25 | 26 | internal void Enqueue(T item) 27 | { 28 | var pi = _producerIndex; 29 | var ci = _consumerIndex; 30 | var a = _array; 31 | var len = a.Length; 32 | 33 | a[pi] = item; 34 | 35 | pi = (pi + 1) & (len - 1); 36 | if (pi == ci) 37 | { 38 | var b = new T[len * 2]; 39 | Array.Copy(a, ci, b, 0, len - ci); 40 | Array.Copy(a, 0, b, len - ci, ci); 41 | _array = b; 42 | _consumerIndex = 0; 43 | _producerIndex = len; 44 | } 45 | else 46 | { 47 | _producerIndex = pi; 48 | } 49 | } 50 | 51 | internal bool Dequeue(out T item) 52 | { 53 | var pi = _producerIndex; 54 | var ci = _consumerIndex; 55 | var a = _array; 56 | var len = a.Length; 57 | 58 | if (pi == ci) 59 | { 60 | item = default; 61 | return false; 62 | } 63 | item = a[ci]; 64 | a[ci] = default; 65 | _consumerIndex = (ci + 1) & (len - 1); 66 | return true; 67 | } 68 | 69 | internal void Release() 70 | { 71 | _array = null; 72 | } 73 | 74 | internal void ForEach(TState state, Action onEach) 75 | { 76 | var ci = _consumerIndex; 77 | var pi = _producerIndex; 78 | var a = _array; 79 | var len = a.Length; 80 | 81 | while (ci != pi) 82 | { 83 | onEach(a[ci], state); 84 | ci = (ci + 1) & (len - 1); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/CancellationHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading; 6 | 7 | namespace async_enumerable_dotnet.impl 8 | { 9 | /// 10 | /// Utility class to work with CancellationTokenSources atomically. 11 | /// 12 | internal static class CancellationHelper 13 | { 14 | /// 15 | /// The cancelled indicator, do not leak! 16 | /// 17 | internal static readonly CancellationTokenSource Cancelled = new CancellationTokenSource(); 18 | 19 | /// 20 | /// Atomically replace an old CancellationTokenSource within the field 21 | /// or cancel the new token source if the field contains the Cancelled indicator. 22 | /// 23 | /// The target field 24 | /// The new cancellation token source to swap in. 25 | /// True if successful, false if the field contains the Cancelled indicator 26 | internal static bool Replace(ref CancellationTokenSource field, CancellationTokenSource cts) 27 | { 28 | for (; ; ) 29 | { 30 | var a = Volatile.Read(ref field); 31 | if (a == Cancelled) 32 | { 33 | cts?.Cancel(); 34 | return false; 35 | } 36 | if (Interlocked.CompareExchange(ref field, cts, a) == a) 37 | { 38 | return true; 39 | } 40 | } 41 | } 42 | 43 | /// 44 | /// Atomically swap in the Cancelled indicator and cancel the 45 | /// current token source if any. 46 | /// 47 | /// The target field. 48 | /// True if the cancel happened 49 | internal static bool Cancel(ref CancellationTokenSource field) 50 | { 51 | if (Volatile.Read(ref field) != Cancelled) 52 | { 53 | var old = Interlocked.Exchange(ref field, Cancelled); 54 | if (old != Cancelled) 55 | { 56 | old?.Cancel(); 57 | return true; 58 | } 59 | } 60 | return false; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Collect.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class Collect : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Func _collectionSupplier; 17 | 18 | private readonly Action _collector; 19 | 20 | public Collect(IAsyncEnumerable source, Func collectionSupplier, Action collector) 21 | { 22 | _source = source; 23 | _collectionSupplier = collectionSupplier; 24 | _collector = collector; 25 | } 26 | 27 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 28 | { 29 | TCollection initial; 30 | try 31 | { 32 | initial = _collectionSupplier(); 33 | } 34 | catch (Exception ex) 35 | { 36 | return new Error.ErrorEnumerator(ex); 37 | } 38 | return new CollectEnumerator(_source.GetAsyncEnumerator(cancellationToken), initial, _collector); 39 | } 40 | 41 | private sealed class CollectEnumerator : IAsyncEnumerator 42 | { 43 | private readonly IAsyncEnumerator _source; 44 | 45 | private readonly Action _collector; 46 | 47 | private bool _once; 48 | 49 | public TCollection Current { get; private set; } 50 | 51 | public CollectEnumerator(IAsyncEnumerator source, TCollection collection, Action collector) 52 | { 53 | _source = source; 54 | Current = collection; 55 | _collector = collector; 56 | } 57 | 58 | public ValueTask DisposeAsync() 59 | { 60 | return _source.DisposeAsync(); 61 | } 62 | 63 | public async ValueTask MoveNextAsync() 64 | { 65 | if (_once) 66 | { 67 | Current = default; 68 | return false; 69 | } 70 | _once = true; 71 | 72 | while (await _source.MoveNextAsync()) 73 | { 74 | _collector(Current, _source.Current); 75 | } 76 | 77 | return true; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Count.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Count : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | public Count(IAsyncEnumerable source) 16 | { 17 | _source = source; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | return new CountEnumerator(_source.GetAsyncEnumerator(cancellationToken)); 23 | } 24 | 25 | private sealed class CountEnumerator : IAsyncEnumerator 26 | { 27 | private readonly IAsyncEnumerator _source; 28 | 29 | public long Current { get; private set; } 30 | 31 | private bool _once; 32 | 33 | public CountEnumerator(IAsyncEnumerator source) 34 | { 35 | _source = source; 36 | } 37 | 38 | public ValueTask DisposeAsync() 39 | { 40 | return _source.DisposeAsync(); 41 | } 42 | 43 | public async ValueTask MoveNextAsync() 44 | { 45 | if (_once) 46 | { 47 | return false; 48 | } 49 | 50 | var n = 0L; 51 | 52 | while (await _source.MoveNextAsync()) 53 | { 54 | n++; 55 | } 56 | 57 | Current = n; 58 | _once = true; 59 | return true; 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/DefaultIfEmpty.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class DefaultIfEmpty : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly T _defaultItem; 16 | 17 | public DefaultIfEmpty(IAsyncEnumerable source, T defaultItem) 18 | { 19 | _source = source; 20 | _defaultItem = defaultItem; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new DefaultIfEmptyEnumerator(_source.GetAsyncEnumerator(cancellationToken), _defaultItem); 26 | } 27 | 28 | private sealed class DefaultIfEmptyEnumerator : IAsyncEnumerator 29 | { 30 | private IAsyncEnumerator _source; 31 | 32 | private readonly T _defaultItem; 33 | 34 | private bool _hasItems; 35 | 36 | public T Current 37 | { 38 | get 39 | { 40 | if (_source != null) 41 | { 42 | return _source.Current; 43 | } 44 | return _defaultItem; 45 | } 46 | } 47 | 48 | public DefaultIfEmptyEnumerator(IAsyncEnumerator source, T defaultItem) 49 | { 50 | _source = source; 51 | _defaultItem = defaultItem; 52 | } 53 | 54 | public ValueTask DisposeAsync() 55 | { 56 | if (_source != null) 57 | { 58 | return _source.DisposeAsync(); 59 | } 60 | return new ValueTask(); 61 | } 62 | 63 | public async ValueTask MoveNextAsync() 64 | { 65 | if (_source == null) 66 | { 67 | return false; 68 | } 69 | if (await _source.MoveNextAsync()) 70 | { 71 | _hasItems = true; 72 | return true; 73 | } 74 | 75 | if (!_hasItems) 76 | { 77 | await _source.DisposeAsync(); 78 | _source = null; 79 | _hasItems = true; 80 | return true; 81 | } 82 | return false; 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Defer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Defer : IAsyncEnumerable 12 | { 13 | private readonly Func> _func; 14 | 15 | public Defer(Func> func) 16 | { 17 | _func = func; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | IAsyncEnumerator en; 23 | try 24 | { 25 | en = _func().GetAsyncEnumerator(cancellationToken); 26 | } 27 | catch (Exception ex) 28 | { 29 | en = new Error.ErrorEnumerator(ex); 30 | } 31 | return en; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Distinct.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class Distinct : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Func _keySelector; 17 | 18 | private readonly Func> _collectionSupplier; 19 | 20 | public Distinct(IAsyncEnumerable source, Func keySelector, Func> collectionSupplier) 21 | { 22 | _source = source; 23 | _keySelector = keySelector; 24 | _collectionSupplier = collectionSupplier; 25 | } 26 | 27 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 28 | { 29 | ISet collection; 30 | 31 | try 32 | { 33 | collection = _collectionSupplier(); 34 | } 35 | catch (Exception ex) 36 | { 37 | return new Error.ErrorEnumerator(ex); 38 | } 39 | return new DistinctEnumerator(_source.GetAsyncEnumerator(cancellationToken), _keySelector, collection); 40 | } 41 | 42 | private sealed class DistinctEnumerator : IAsyncEnumerator 43 | { 44 | private readonly IAsyncEnumerator _source; 45 | 46 | private readonly Func _keySelector; 47 | 48 | private ISet _collection; 49 | 50 | public TSource Current => _source.Current; 51 | 52 | public DistinctEnumerator(IAsyncEnumerator source, Func keySelector, ISet collection) 53 | { 54 | _source = source; 55 | _keySelector = keySelector; 56 | _collection = collection; 57 | } 58 | 59 | public ValueTask DisposeAsync() 60 | { 61 | _collection = default; 62 | return _source.DisposeAsync(); 63 | } 64 | 65 | public async ValueTask MoveNextAsync() 66 | { 67 | for (; ; ) 68 | { 69 | if (await _source.MoveNextAsync()) 70 | { 71 | if (_collection.Add(_keySelector(_source.Current))) 72 | { 73 | return true; 74 | } 75 | } 76 | else 77 | { 78 | return false; 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/DistinctUntilChanged.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class DistinctUntilChanged : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Func _keySelector; 17 | 18 | private readonly IEqualityComparer _keyComparer; 19 | 20 | public DistinctUntilChanged(IAsyncEnumerable source, Func keySelector, IEqualityComparer keyComparer) 21 | { 22 | _source = source; 23 | _keySelector = keySelector; 24 | _keyComparer = keyComparer; 25 | } 26 | 27 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 28 | { 29 | return new DistinctUntilChangedEnumerator(_source.GetAsyncEnumerator(cancellationToken), _keySelector, _keyComparer); 30 | } 31 | 32 | private sealed class DistinctUntilChangedEnumerator : IAsyncEnumerator 33 | { 34 | private readonly IAsyncEnumerator _source; 35 | 36 | private readonly Func _keySelector; 37 | 38 | private readonly IEqualityComparer _keyComparer; 39 | 40 | private TKey _prevKey; 41 | 42 | public TSource Current => _source.Current; 43 | 44 | private bool _once; 45 | 46 | public DistinctUntilChangedEnumerator(IAsyncEnumerator source, Func keySelector, IEqualityComparer keyComparer) 47 | { 48 | _source = source; 49 | _keySelector = keySelector; 50 | _keyComparer = keyComparer; 51 | } 52 | 53 | public ValueTask DisposeAsync() 54 | { 55 | _prevKey = default; 56 | return _source.DisposeAsync(); 57 | } 58 | 59 | public async ValueTask MoveNextAsync() 60 | { 61 | for (; ;) 62 | { 63 | if (await _source.MoveNextAsync()) 64 | { 65 | var key = _keySelector(_source.Current); 66 | if (!_once) 67 | { 68 | _once = true; 69 | _prevKey = key; 70 | return true; 71 | } 72 | if (!_keyComparer.Equals(_prevKey, key)) 73 | { 74 | _prevKey = key; 75 | return true; 76 | } 77 | 78 | _prevKey = key; 79 | } 80 | else 81 | { 82 | return false; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/DoOnNext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class DoOnNext : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Action _handler; 17 | 18 | public DoOnNext(IAsyncEnumerable source, Action handler) 19 | { 20 | _source = source; 21 | _handler = handler; 22 | } 23 | 24 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 25 | { 26 | return new DoOnNextEnumerator(_source.GetAsyncEnumerator(cancellationToken), _handler); 27 | } 28 | 29 | private sealed class DoOnNextEnumerator : IAsyncEnumerator 30 | { 31 | private readonly IAsyncEnumerator _source; 32 | 33 | private readonly Action _handler; 34 | 35 | public DoOnNextEnumerator(IAsyncEnumerator source, Action handler) 36 | { 37 | _source = source; 38 | _handler = handler; 39 | } 40 | 41 | public T Current => _source.Current; 42 | 43 | public ValueTask DisposeAsync() 44 | { 45 | return _source.DisposeAsync(); 46 | } 47 | 48 | public async ValueTask MoveNextAsync() 49 | { 50 | if (await _source.MoveNextAsync()) 51 | { 52 | _handler(_source.Current); 53 | return true; 54 | } 55 | return false; 56 | } 57 | } 58 | } 59 | 60 | internal sealed class DoOnNextAsync : IAsyncEnumerable 61 | { 62 | private readonly IAsyncEnumerable _source; 63 | 64 | private readonly Func _handler; 65 | 66 | public DoOnNextAsync(IAsyncEnumerable source, Func handler) 67 | { 68 | _source = source; 69 | _handler = handler; 70 | } 71 | 72 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 73 | { 74 | return new DoOnNextAsyncEnumerator(_source.GetAsyncEnumerator(cancellationToken), _handler); 75 | } 76 | 77 | private sealed class DoOnNextAsyncEnumerator : IAsyncEnumerator 78 | { 79 | private readonly IAsyncEnumerator _source; 80 | 81 | private readonly Func _handler; 82 | 83 | public DoOnNextAsyncEnumerator(IAsyncEnumerator source, Func handler) 84 | { 85 | _source = source; 86 | _handler = handler; 87 | } 88 | 89 | public T Current => _source.Current; 90 | 91 | public ValueTask DisposeAsync() 92 | { 93 | return _source.DisposeAsync(); 94 | } 95 | 96 | public async ValueTask MoveNextAsync() 97 | { 98 | if (await _source.MoveNextAsync()) 99 | { 100 | await _handler(_source.Current); 101 | return true; 102 | } 103 | return false; 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/ElementAt.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class ElementAt : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly long _index; 17 | 18 | private readonly TSource _defaultItem; 19 | 20 | private readonly bool _hasDefault; 21 | 22 | public ElementAt(IAsyncEnumerable source, long index, TSource defaultItem, bool hasDefault) 23 | { 24 | _source = source; 25 | _index = index; 26 | _defaultItem = defaultItem; 27 | _hasDefault = hasDefault; 28 | } 29 | 30 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 31 | { 32 | return new ElementAtEnumerator(_source.GetAsyncEnumerator(cancellationToken), _index, _defaultItem, _hasDefault); 33 | } 34 | 35 | private sealed class ElementAtEnumerator : IAsyncEnumerator 36 | { 37 | private readonly IAsyncEnumerator _source; 38 | 39 | private readonly long _index; 40 | 41 | private readonly TSource _defaultItem; 42 | 43 | private readonly bool _hasDefault; 44 | 45 | public TSource Current { get; private set; } 46 | 47 | private bool _done; 48 | 49 | public ElementAtEnumerator(IAsyncEnumerator source, long index, TSource defaultItem, bool hasDefault) 50 | { 51 | _source = source; 52 | _index = index; 53 | _defaultItem = defaultItem; 54 | _hasDefault = hasDefault; 55 | } 56 | 57 | public ValueTask DisposeAsync() 58 | { 59 | return _source.DisposeAsync(); 60 | } 61 | 62 | public async ValueTask MoveNextAsync() 63 | { 64 | if (_done) 65 | { 66 | return false; 67 | } 68 | var idx = 0L; 69 | for (;;) 70 | { 71 | if (await _source.MoveNextAsync()) 72 | { 73 | if (idx == _index) 74 | { 75 | Current = _source.Current; 76 | _done = true; 77 | return true; 78 | } 79 | 80 | idx++; 81 | } 82 | else 83 | { 84 | if (_hasDefault) 85 | { 86 | Current = _defaultItem; 87 | _done = true; 88 | return true; 89 | } 90 | 91 | throw new IndexOutOfRangeException(); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Empty.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Empty : IAsyncEnumerable, IAsyncEnumerator 12 | { 13 | internal static readonly Empty Instance = new Empty(); 14 | 15 | public T Current => default; 16 | 17 | public ValueTask DisposeAsync() 18 | { 19 | return new ValueTask(); 20 | } 21 | 22 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 23 | { 24 | return this; 25 | } 26 | 27 | public ValueTask MoveNextAsync() 28 | { 29 | return new ValueTask(false); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/EmptyHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | 6 | namespace async_enumerable_dotnet.impl 7 | { 8 | /// 9 | /// Hosts the EmptyIndicator singleton. 10 | /// 11 | internal static class EmptyHelper 12 | { 13 | internal static readonly object EmptyIndicator = new object(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Error.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class Error : IAsyncEnumerable 13 | { 14 | private readonly Exception _error; 15 | 16 | public Error(Exception error) 17 | { 18 | _error = error; 19 | } 20 | 21 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 22 | { 23 | return new ErrorEnumerator(_error); 24 | } 25 | 26 | internal sealed class ErrorEnumerator : IAsyncEnumerator 27 | { 28 | private readonly Exception _error; 29 | 30 | private bool _once; 31 | 32 | public ErrorEnumerator(Exception error) 33 | { 34 | _error = error; 35 | } 36 | 37 | public T Current => default; 38 | 39 | public ValueTask DisposeAsync() 40 | { 41 | return new ValueTask(); 42 | } 43 | 44 | public ValueTask MoveNextAsync() 45 | { 46 | if (_once) 47 | { 48 | return new ValueTask(false); 49 | } 50 | _once = true; 51 | return new ValueTask(Task.FromException(_error)); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/FirstLastSingleAsync.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading.Tasks; 8 | using System.Collections.Generic; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal static class FirstLastSingleAsync 13 | { 14 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 15 | internal static async ValueTask First(IAsyncEnumerable source, T defaultItem, bool hasDefault) 16 | { 17 | var enumerator = source.GetAsyncEnumerator(default); 18 | try 19 | { 20 | if (await enumerator.MoveNextAsync()) 21 | { 22 | return enumerator.Current; 23 | } 24 | else 25 | { 26 | if (hasDefault) 27 | { 28 | return defaultItem; 29 | } 30 | throw new IndexOutOfRangeException("The source async sequence is empty"); 31 | } 32 | } 33 | finally 34 | { 35 | await enumerator.DisposeAsync(); 36 | } 37 | } 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | internal static async ValueTask Last(IAsyncEnumerable source, T defaultItem, bool hasDefault) 41 | { 42 | var hasLast = false; 43 | var last = default(T); 44 | 45 | var enumerator = source.GetAsyncEnumerator(default); 46 | try 47 | { 48 | 49 | while (await enumerator.MoveNextAsync()) 50 | { 51 | last = enumerator.Current; 52 | hasLast = true; 53 | } 54 | } 55 | finally 56 | { 57 | await enumerator.DisposeAsync(); 58 | } 59 | 60 | if (hasLast) 61 | { 62 | return last; 63 | } 64 | if (hasDefault) 65 | { 66 | return defaultItem; 67 | } 68 | throw new IndexOutOfRangeException("The source async sequence is empty"); 69 | } 70 | 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | internal static async ValueTask Single(IAsyncEnumerable source, T defaultItem, bool hasDefault) 73 | { 74 | var enumerator = source.GetAsyncEnumerator(default); 75 | try 76 | { 77 | if (!await enumerator.MoveNextAsync()) 78 | { 79 | if (hasDefault) 80 | { 81 | return defaultItem; 82 | } 83 | 84 | throw new IndexOutOfRangeException("The source async sequence is empty"); 85 | } 86 | 87 | var value = enumerator.Current; 88 | 89 | if (await enumerator.MoveNextAsync()) 90 | { 91 | throw new IndexOutOfRangeException("The source async sequence has more than one value"); 92 | } 93 | 94 | return value; 95 | } 96 | finally 97 | { 98 | await enumerator.DisposeAsync(); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/ForEach.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Collections.Generic; 10 | 11 | namespace async_enumerable_dotnet.impl 12 | { 13 | internal static class ForEach 14 | { 15 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 16 | internal static async ValueTask ForEachAction(IAsyncEnumerable source, Action onNext, Action onError, Action onComplete) 17 | { 18 | var enumerator = source.GetAsyncEnumerator(default); 19 | try 20 | { 21 | for (; ; ) 22 | { 23 | bool b; 24 | try 25 | { 26 | b = await enumerator.MoveNextAsync(); 27 | if (b) 28 | { 29 | onNext?.Invoke(enumerator.Current); 30 | } 31 | } 32 | catch (Exception ex) 33 | { 34 | onError?.Invoke(ex); 35 | break; 36 | } 37 | 38 | if (!b) 39 | { 40 | onComplete?.Invoke(); 41 | break; 42 | } 43 | 44 | } 45 | } 46 | finally 47 | { 48 | await enumerator.DisposeAsync(); 49 | } 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static async ValueTask Consume(this IAsyncEnumerable source, IAsyncConsumer consumer, CancellationToken ct) 54 | { 55 | var en = source.GetAsyncEnumerator(default); 56 | try 57 | { 58 | if (!ct.IsCancellationRequested) 59 | { 60 | try 61 | { 62 | while (await en.MoveNextAsync()) 63 | { 64 | if (ct.IsCancellationRequested) 65 | { 66 | return; 67 | } 68 | await consumer.Next(en.Current); 69 | } 70 | 71 | await consumer.Complete(); 72 | } 73 | catch (Exception ex) 74 | { 75 | await consumer.Error(ex); 76 | } 77 | } 78 | } 79 | finally 80 | { 81 | await en.DisposeAsync(); 82 | } 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/FromArray.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class FromArray : IAsyncEnumerable 12 | { 13 | private readonly T[] _values; 14 | 15 | public FromArray(T[] values) 16 | { 17 | _values = values; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | return new FromArrayEnumerator(_values); 23 | } 24 | 25 | private sealed class FromArrayEnumerator : IAsyncEnumerator 26 | { 27 | private readonly T[] _values; 28 | 29 | private int _index; 30 | 31 | public T Current => _values[_index]; 32 | 33 | public FromArrayEnumerator(T[] values) 34 | { 35 | _values = values; 36 | _index = -1; 37 | } 38 | 39 | public ValueTask DisposeAsync() 40 | { 41 | return new ValueTask(); 42 | } 43 | 44 | public ValueTask MoveNextAsync() 45 | { 46 | var idx = _index + 1; 47 | if (idx < _values.Length) 48 | { 49 | _index = idx; 50 | return new ValueTask(true); 51 | } 52 | return new ValueTask(false); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/FromEnumerable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Collections.Generic; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class FromEnumerable : IAsyncEnumerable 12 | { 13 | private readonly IEnumerable _source; 14 | 15 | public FromEnumerable(IEnumerable source) 16 | { 17 | _source = source; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | return new FromEnumerableEnumerator(_source.GetEnumerator(), cancellationToken); 23 | } 24 | 25 | private sealed class FromEnumerableEnumerator : IAsyncEnumerator 26 | { 27 | private readonly IEnumerator _source; 28 | 29 | private readonly CancellationToken _ct; 30 | 31 | public T Current => _source.Current; 32 | 33 | public FromEnumerableEnumerator(IEnumerator source, CancellationToken ct) 34 | { 35 | _source = source; 36 | _ct = ct; 37 | } 38 | 39 | public ValueTask DisposeAsync() 40 | { 41 | _source.Dispose(); 42 | return new ValueTask(); 43 | } 44 | 45 | public ValueTask MoveNextAsync() 46 | { 47 | if (!_ct.IsCancellationRequested && _source.MoveNext()) 48 | { 49 | return new ValueTask(true); 50 | } 51 | return new ValueTask(false); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/FromTaskFunc.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class FromTaskFunc : IAsyncEnumerable 13 | { 14 | private readonly Func> _func; 15 | 16 | public FromTaskFunc(Func> func) 17 | { 18 | _func = func; 19 | } 20 | 21 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 22 | { 23 | return new FromTaskFuncEnumerator(_func); 24 | } 25 | 26 | private sealed class FromTaskFuncEnumerator : IAsyncEnumerator 27 | { 28 | private readonly Func> _func; 29 | 30 | public T Current { get; private set; } 31 | 32 | private bool _once; 33 | 34 | public FromTaskFuncEnumerator(Func> func) 35 | { 36 | _func = func; 37 | } 38 | 39 | public ValueTask DisposeAsync() 40 | { 41 | return new ValueTask(); 42 | } 43 | 44 | public async ValueTask MoveNextAsync() 45 | { 46 | if (_once) 47 | { 48 | Current = default; 49 | return false; 50 | } 51 | 52 | _once = true; 53 | 54 | Current = await _func(); 55 | return true; 56 | } 57 | } 58 | } 59 | 60 | internal sealed class FromTask : IAsyncEnumerable 61 | { 62 | private readonly Task _task; 63 | 64 | public FromTask(Task task) 65 | { 66 | _task = task; 67 | } 68 | 69 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 70 | { 71 | return new FromTaskEnumerator(_task); 72 | } 73 | 74 | private sealed class FromTaskEnumerator : IAsyncEnumerator 75 | { 76 | private readonly Task _task; 77 | 78 | public T Current => _task.Result; 79 | 80 | private bool _once; 81 | 82 | public FromTaskEnumerator(Task task) 83 | { 84 | _task = task; 85 | } 86 | 87 | public ValueTask DisposeAsync() 88 | { 89 | return new ValueTask(); 90 | } 91 | 92 | public async ValueTask MoveNextAsync() 93 | { 94 | if (_once) 95 | { 96 | return false; 97 | } 98 | 99 | _once = true; 100 | 101 | await _task; 102 | return true; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/IgnoreElements.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class IgnoreElements : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | public IgnoreElements(IAsyncEnumerable source) 16 | { 17 | _source = source; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | return new IgnoreElementsEnumerator(_source.GetAsyncEnumerator(cancellationToken)); 23 | } 24 | 25 | private sealed class IgnoreElementsEnumerator : IAsyncEnumerator 26 | { 27 | private readonly IAsyncEnumerator _source; 28 | 29 | public IgnoreElementsEnumerator(IAsyncEnumerator source) 30 | { 31 | _source = source; 32 | } 33 | 34 | public T Current => default; 35 | 36 | public ValueTask DisposeAsync() 37 | { 38 | return _source.DisposeAsync(); 39 | } 40 | 41 | public async ValueTask MoveNextAsync() 42 | { 43 | while (await _source.MoveNextAsync()) 44 | { 45 | // deliberately ignoring items 46 | } 47 | 48 | return false; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/IsEmpty.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class IsEmpty : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | public IsEmpty(IAsyncEnumerable source) 16 | { 17 | _source = source; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | return new IsEmptyEnumerator(_source.GetAsyncEnumerator(cancellationToken)); 23 | } 24 | 25 | private sealed class IsEmptyEnumerator : IAsyncEnumerator 26 | { 27 | private readonly IAsyncEnumerator _source; 28 | 29 | public bool Current { get; private set; } 30 | 31 | private bool _once; 32 | 33 | public IsEmptyEnumerator(IAsyncEnumerator source) 34 | { 35 | _source = source; 36 | } 37 | 38 | public ValueTask DisposeAsync() 39 | { 40 | return _source.DisposeAsync(); 41 | } 42 | 43 | public async ValueTask MoveNextAsync() 44 | { 45 | if (_once) 46 | { 47 | return false; 48 | } 49 | 50 | _once = true; 51 | 52 | if (await _source.MoveNextAsync()) 53 | { 54 | Current = false; 55 | return true; 56 | } 57 | 58 | Current = true; 59 | return true; 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Just.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Just : IAsyncEnumerable 12 | { 13 | private readonly T _value; 14 | 15 | public Just(T value) 16 | { 17 | _value = value; 18 | } 19 | 20 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 21 | { 22 | return new JustEnumerator(_value); 23 | } 24 | 25 | private sealed class JustEnumerator : IAsyncEnumerator 26 | { 27 | private bool _once; 28 | 29 | public JustEnumerator(T value) 30 | { 31 | Current = value; 32 | } 33 | 34 | public T Current { get; private set; } 35 | 36 | public ValueTask DisposeAsync() 37 | { 38 | Current = default; 39 | return new ValueTask(); 40 | } 41 | 42 | public ValueTask MoveNextAsync() 43 | { 44 | if (_once) 45 | { 46 | return new ValueTask(false); 47 | } 48 | _once = true; 49 | return new ValueTask(true); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Never.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Never : IAsyncEnumerable 12 | { 13 | internal static readonly Never Instance = new Never(); 14 | 15 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 16 | { 17 | return new NeverEnumerator(cancellationToken); 18 | } 19 | 20 | private sealed class NeverEnumerator : IAsyncEnumerator 21 | { 22 | private readonly CancellationToken _ct; 23 | 24 | public T Current => default; 25 | 26 | internal NeverEnumerator(CancellationToken ct) 27 | { 28 | _ct = ct; 29 | } 30 | 31 | public ValueTask DisposeAsync() 32 | { 33 | return new ValueTask(); 34 | } 35 | 36 | public async ValueTask MoveNextAsync() 37 | { 38 | var tcs = new TaskCompletionSource(); 39 | using var reg = _ct.Register(t => (t as TaskCompletionSource).TrySetCanceled(), tcs); 40 | 41 | await tcs.Task; 42 | 43 | return false; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/OnErrorResumeNext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class OnErrorResumeNext : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Func> _handler; 17 | 18 | public OnErrorResumeNext(IAsyncEnumerable source, Func> handler) 19 | { 20 | _source = source; 21 | _handler = handler; 22 | } 23 | 24 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 25 | { 26 | return new OnErrorResumeNextEnumerator(_source.GetAsyncEnumerator(cancellationToken), _handler, cancellationToken); 27 | } 28 | 29 | private sealed class OnErrorResumeNextEnumerator : IAsyncEnumerator 30 | { 31 | private readonly CancellationToken _ct; 32 | 33 | private IAsyncEnumerator _source; 34 | 35 | private Func> _handler; 36 | 37 | public OnErrorResumeNextEnumerator(IAsyncEnumerator source, Func> handler, 38 | CancellationToken ct) 39 | { 40 | _source = source; 41 | _handler = handler; 42 | _ct = ct; 43 | } 44 | 45 | public T Current => _source.Current; 46 | 47 | public ValueTask DisposeAsync() 48 | { 49 | var src = _source; 50 | if (src != null) 51 | { 52 | return src.DisposeAsync(); 53 | } 54 | return new ValueTask(); 55 | } 56 | 57 | public async ValueTask MoveNextAsync() 58 | { 59 | if (_handler == null) 60 | { 61 | return await _source.MoveNextAsync(); 62 | } 63 | 64 | try 65 | { 66 | return await _source.MoveNextAsync(); 67 | } 68 | catch (Exception ex) 69 | { 70 | var old = _source; 71 | _source = null; 72 | if (old != null) 73 | { 74 | await old.DisposeAsync(); 75 | } 76 | 77 | IAsyncEnumerator en; 78 | 79 | try 80 | { 81 | 82 | en = _handler(ex).GetAsyncEnumerator(_ct); 83 | } 84 | catch (Exception exc) 85 | { 86 | throw new AggregateException(ex, exc); 87 | } 88 | 89 | _handler = null; 90 | _source = en; 91 | 92 | return await en.MoveNextAsync(); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Publish.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Publish : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly Func, IAsyncEnumerable> _func; 16 | 17 | public Publish(IAsyncEnumerable source, Func, IAsyncEnumerable> func) 18 | { 19 | _source = source; 20 | _func = func; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | var subject = new MulticastAsyncEnumerable(); 26 | IAsyncEnumerable result; 27 | try 28 | { 29 | result = _func(subject); 30 | } 31 | catch (Exception ex) 32 | { 33 | return new Error.ErrorEnumerator(ex); 34 | } 35 | var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); 36 | var en = new MulticastEnumerator(_source.GetAsyncEnumerator(cts.Token), subject, result.GetAsyncEnumerator(cancellationToken), cts); 37 | return en; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/QueueDrainHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Collections.Generic; 10 | 11 | namespace async_enumerable_dotnet.impl 12 | { 13 | /// 14 | /// Utility methods for common queue-drain operations. 15 | /// 16 | internal static class QueueDrainHelper 17 | { 18 | 19 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 20 | internal static void MoveNext(IAsyncEnumerator source, ref int sourceWip, ref int disposeWip, Action, object> nextHandlerAction, object sender) 21 | { 22 | if (Interlocked.Increment(ref sourceWip) == 1) 23 | { 24 | do 25 | { 26 | if (Interlocked.Increment(ref disposeWip) == 1) 27 | { 28 | source.MoveNextAsync() 29 | .AsTask() 30 | .ContinueWith(nextHandlerAction, sender); 31 | } 32 | else 33 | { 34 | break; 35 | } 36 | } 37 | while (Interlocked.Decrement(ref sourceWip) != 0); 38 | } 39 | } 40 | 41 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 42 | internal static void DisposeHandler(Task t, ref int allDisposeWip, ref Exception allDisposeError, TaskCompletionSource allDisposeTask) 43 | { 44 | if (t.IsCanceled) 45 | { 46 | ExceptionHelper.AddException(ref allDisposeError, new OperationCanceledException()); 47 | } 48 | else if (t.IsFaulted) 49 | { 50 | ExceptionHelper.AddException(ref allDisposeError, ExceptionHelper.Extract(t.Exception)); 51 | } 52 | DisposeOne(ref allDisposeWip, ref allDisposeError, allDisposeTask); 53 | } 54 | 55 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 56 | internal static void DisposeOne(ref int allDisposeWip, ref Exception allDisposeError, TaskCompletionSource allDisposeTask) 57 | { 58 | if (Interlocked.Decrement(ref allDisposeWip) == 0) 59 | { 60 | var ex = allDisposeError; 61 | if (ex != null) 62 | { 63 | allDisposeError = null; 64 | allDisposeTask.TrySetException(ex); 65 | } 66 | else 67 | { 68 | allDisposeTask.TrySetResult(true); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Range.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Range : IAsyncEnumerable 12 | { 13 | private readonly int _start; 14 | 15 | private readonly int _end; 16 | 17 | public Range(int start, int end) 18 | { 19 | _start = start; 20 | _end = end; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new RangeEnumerator(_start, _end); 26 | } 27 | 28 | private sealed class RangeEnumerator : IAsyncEnumerator 29 | { 30 | private readonly int _end; 31 | 32 | private int _index; 33 | 34 | public RangeEnumerator(int index, int end) 35 | { 36 | _index = index; 37 | _end = end; 38 | } 39 | 40 | public int Current { get; private set; } 41 | 42 | public ValueTask DisposeAsync() 43 | { 44 | return new ValueTask(); 45 | } 46 | 47 | public ValueTask MoveNextAsync() 48 | { 49 | var idx = _index; 50 | if (idx == _end) 51 | { 52 | return new ValueTask(false); 53 | } 54 | _index = idx + 1; 55 | Current = idx; 56 | return new ValueTask(true); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Replay.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Replay : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly Func, IAsyncEnumerable> _func; 16 | 17 | public Replay(IAsyncEnumerable source, Func, IAsyncEnumerable> func) 18 | { 19 | _source = source; 20 | _func = func; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | var subject = new ReplayAsyncEnumerable(); 26 | IAsyncEnumerable result; 27 | try 28 | { 29 | result = _func(subject); 30 | } 31 | catch (Exception ex) 32 | { 33 | return new Error.ErrorEnumerator(ex); 34 | } 35 | var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); 36 | var en = new MulticastEnumerator(_source.GetAsyncEnumerator(cts.Token), subject, result.GetAsyncEnumerator(cancellationToken), cts); 37 | return en; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Skip.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Skip : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly long _n; 16 | 17 | public Skip(IAsyncEnumerable source, long n) 18 | { 19 | _source = source; 20 | _n = n; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new SkipEnumerator(_source.GetAsyncEnumerator(cancellationToken), _n); 26 | } 27 | 28 | private sealed class SkipEnumerator : IAsyncEnumerator 29 | { 30 | private readonly IAsyncEnumerator _source; 31 | 32 | private long _remaining; 33 | 34 | public T Current => _source.Current; 35 | 36 | public SkipEnumerator(IAsyncEnumerator source, long remaining) 37 | { 38 | _source = source; 39 | _remaining = remaining; 40 | } 41 | 42 | public ValueTask DisposeAsync() 43 | { 44 | return _source.DisposeAsync(); 45 | } 46 | 47 | public async ValueTask MoveNextAsync() 48 | { 49 | var n = _remaining; 50 | if (n != 0) 51 | { 52 | while (n != 0) 53 | { 54 | if (await _source.MoveNextAsync()) 55 | { 56 | n--; 57 | } 58 | else 59 | { 60 | _remaining = 0; 61 | return false; 62 | } 63 | } 64 | _remaining = 0; 65 | } 66 | 67 | return await _source.MoveNextAsync(); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/SkipLast.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class SkipLast : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly int _n; 16 | 17 | public SkipLast(IAsyncEnumerable source, int n) 18 | { 19 | _source = source; 20 | _n = n; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new SkipLastEnumerable(_source.GetAsyncEnumerator(cancellationToken), _n); 26 | } 27 | 28 | private sealed class SkipLastEnumerable : IAsyncEnumerator 29 | { 30 | private readonly IAsyncEnumerator _source; 31 | 32 | private int _size; 33 | 34 | private ArrayQueue _queue; 35 | 36 | private T _current; 37 | 38 | public T Current => _current; 39 | 40 | public SkipLastEnumerable(IAsyncEnumerator source, int n) 41 | { 42 | _source = source; 43 | _size = n; 44 | _queue = new ArrayQueue(16); 45 | } 46 | 47 | public ValueTask DisposeAsync() 48 | { 49 | return _source.DisposeAsync(); 50 | } 51 | 52 | public async ValueTask MoveNextAsync() 53 | { 54 | while (_size != 0) 55 | { 56 | if (await _source.MoveNextAsync()) 57 | { 58 | _queue.Enqueue(_source.Current); 59 | _size--; 60 | } 61 | else 62 | { 63 | _size = 0; 64 | return false; 65 | } 66 | } 67 | 68 | if (await _source.MoveNextAsync()) 69 | { 70 | _queue.Dequeue(out _current); 71 | _queue.Enqueue(_source.Current); 72 | return true; 73 | } 74 | return false; 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/SkipWhile.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class SkipWhile : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Func _predicate; 17 | 18 | public SkipWhile(IAsyncEnumerable source, Func predicate) 19 | { 20 | _source = source; 21 | _predicate = predicate; 22 | } 23 | 24 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 25 | { 26 | return new SkipWhileEnumerator(_source.GetAsyncEnumerator(cancellationToken), _predicate); 27 | } 28 | 29 | private sealed class SkipWhileEnumerator : IAsyncEnumerator 30 | { 31 | private readonly IAsyncEnumerator _source; 32 | 33 | private readonly Func _predicate; 34 | 35 | public T Current => _source.Current; 36 | 37 | private bool _once; 38 | 39 | public SkipWhileEnumerator(IAsyncEnumerator source, Func predicate) 40 | { 41 | _source = source; 42 | _predicate = predicate; 43 | } 44 | 45 | 46 | public ValueTask DisposeAsync() 47 | { 48 | return _source.DisposeAsync(); 49 | } 50 | 51 | public async ValueTask MoveNextAsync() 52 | { 53 | if (_once) 54 | { 55 | return await _source.MoveNextAsync(); 56 | } 57 | 58 | for (; ;) 59 | { 60 | if (await _source.MoveNextAsync()) 61 | { 62 | if (!_predicate(_source.Current)) 63 | { 64 | _once = true; 65 | return true; 66 | } 67 | } 68 | else 69 | { 70 | return false; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/SlimResume.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | /// 12 | /// A minimal awaitable construct that supports only one awaiter 13 | /// and does not convey any value or exception. 14 | /// 15 | internal sealed class SlimResume : INotifyCompletion 16 | { 17 | private Action _continuation; 18 | 19 | private static readonly Action CompletedAction = () => { }; 20 | 21 | /// 22 | /// The singleton instance of a completed SlimResume. 23 | /// 24 | internal static readonly SlimResume Completed; 25 | 26 | static SlimResume() 27 | { 28 | Completed = new SlimResume(); 29 | Volatile.Write(ref Completed._continuation, CompletedAction); 30 | } 31 | 32 | // ReSharper disable once UnusedMethodReturnValue.Global 33 | public SlimResume GetAwaiter() 34 | { 35 | return this; 36 | } 37 | 38 | public bool IsCompleted => Volatile.Read(ref _continuation) == CompletedAction; 39 | 40 | public void OnCompleted(Action continuation) 41 | { 42 | var old = Interlocked.CompareExchange(ref _continuation, continuation, null); 43 | if (old == CompletedAction) 44 | { 45 | continuation.Invoke(); 46 | } 47 | else 48 | if (old != null) 49 | { 50 | throw new InvalidOperationException("Only one continuation allowed"); 51 | } 52 | } 53 | 54 | // ReSharper disable once MemberCanBeMadeStatic.Global 55 | public void GetResult() 56 | { 57 | // no actual outcome, only resumption 58 | } 59 | 60 | internal void Signal() 61 | { 62 | var prev = Interlocked.Exchange(ref _continuation, CompletedAction); 63 | prev?.Invoke(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/SwitchIfEmpty.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class SwitchIfEmpty : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly IAsyncEnumerable _other; 16 | 17 | public SwitchIfEmpty(IAsyncEnumerable source, IAsyncEnumerable other) 18 | { 19 | _source = source; 20 | _other = other; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new SwitchIfEmptyEnumerator(_source.GetAsyncEnumerator(cancellationToken), _other, cancellationToken); 26 | } 27 | 28 | private sealed class SwitchIfEmptyEnumerator : IAsyncEnumerator 29 | { 30 | private readonly IAsyncEnumerable _other; 31 | 32 | private readonly CancellationToken _ct; 33 | 34 | private IAsyncEnumerator _source; 35 | 36 | private bool _once; 37 | 38 | private bool _hasItems; 39 | 40 | public T Current => _source.Current; 41 | 42 | public SwitchIfEmptyEnumerator(IAsyncEnumerator source, IAsyncEnumerable other, CancellationToken ct) 43 | { 44 | _source = source; 45 | _other = other; 46 | _ct = ct; 47 | } 48 | 49 | public ValueTask DisposeAsync() 50 | { 51 | return _source.DisposeAsync(); 52 | } 53 | 54 | public async ValueTask MoveNextAsync() 55 | { 56 | if (_once) 57 | { 58 | return await _source.MoveNextAsync(); 59 | } 60 | 61 | if (await _source.MoveNextAsync()) 62 | { 63 | _hasItems = true; 64 | return true; 65 | } 66 | 67 | if (_hasItems) 68 | { 69 | return false; 70 | } 71 | 72 | await _source.DisposeAsync(); 73 | 74 | _source = _other.GetAsyncEnumerator(_ct); 75 | _once = true; 76 | 77 | return await _source.MoveNextAsync(); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Take.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class Take : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly long _n; 16 | 17 | public Take(IAsyncEnumerable source, long n) 18 | { 19 | _source = source; 20 | _n = n; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new TakeEnumerator(_source.GetAsyncEnumerator(cancellationToken), _n); 26 | } 27 | 28 | private sealed class TakeEnumerator : IAsyncEnumerator 29 | { 30 | private readonly IAsyncEnumerator _source; 31 | 32 | private long _remaining; 33 | 34 | private bool _once; 35 | 36 | public TakeEnumerator(IAsyncEnumerator source, long remaining) 37 | { 38 | _source = source; 39 | _remaining = remaining; 40 | } 41 | 42 | public T Current => _source.Current; 43 | 44 | public ValueTask DisposeAsync() 45 | { 46 | if (!_once) 47 | { 48 | _once = true; 49 | return _source.DisposeAsync(); 50 | } 51 | return new ValueTask(); 52 | } 53 | 54 | public async ValueTask MoveNextAsync() 55 | { 56 | var n = _remaining; 57 | if (n == 0) 58 | { 59 | // eagerly dispose as who knows when the 60 | // consumer will call DisposeAsync? 61 | await DisposeAsync(); 62 | return false; 63 | } 64 | _remaining = n - 1; 65 | 66 | return await _source.MoveNextAsync(); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/TakeLast.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Threading; 8 | 9 | namespace async_enumerable_dotnet.impl 10 | { 11 | internal sealed class TakeLast : IAsyncEnumerable 12 | { 13 | private readonly IAsyncEnumerable _source; 14 | 15 | private readonly int _n; 16 | 17 | public TakeLast(IAsyncEnumerable source, int n) 18 | { 19 | _source = source; 20 | _n = n; 21 | } 22 | 23 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 24 | { 25 | return new TakeLastEnumerator(_source.GetAsyncEnumerator(cancellationToken), _n); 26 | } 27 | 28 | private sealed class TakeLastEnumerator : IAsyncEnumerator 29 | { 30 | private readonly IAsyncEnumerator _source; 31 | 32 | private int _size; 33 | 34 | public T Current => _current; 35 | 36 | private ArrayQueue _queue; 37 | 38 | private bool _once; 39 | 40 | private T _current; 41 | 42 | public TakeLastEnumerator(IAsyncEnumerator source, int size) 43 | { 44 | _source = source; 45 | _size = size; 46 | _queue = new ArrayQueue(16); 47 | } 48 | 49 | public ValueTask DisposeAsync() 50 | { 51 | if (_size == 0) 52 | { 53 | _queue.Release(); 54 | return new ValueTask(); 55 | } 56 | 57 | return _source.DisposeAsync(); 58 | } 59 | 60 | public async ValueTask MoveNextAsync() 61 | { 62 | for (; ; ) 63 | { 64 | if (_once) 65 | { 66 | if (_queue.Dequeue(out _current)) 67 | { 68 | return true; 69 | } 70 | return false; 71 | } 72 | while (await _source.MoveNextAsync()) 73 | { 74 | if (_size != 0) 75 | { 76 | _size--; 77 | } 78 | else 79 | { 80 | _queue.Dequeue(out _); 81 | } 82 | _queue.Enqueue(_source.Current); 83 | } 84 | _once = true; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/TakeWhile.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class TakeWhile : IAsyncEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | private readonly Func _predicate; 17 | 18 | public TakeWhile(IAsyncEnumerable source, Func predicate) 19 | { 20 | _source = source; 21 | _predicate = predicate; 22 | } 23 | 24 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 25 | { 26 | return new TakeWhileEnumerator(_source.GetAsyncEnumerator(cancellationToken), _predicate); 27 | } 28 | 29 | private sealed class TakeWhileEnumerator : IAsyncEnumerator 30 | { 31 | private readonly IAsyncEnumerator _source; 32 | 33 | private readonly Func _predicate; 34 | 35 | public T Current => _source.Current; 36 | 37 | public TakeWhileEnumerator(IAsyncEnumerator source, Func predicate) 38 | { 39 | _source = source; 40 | _predicate = predicate; 41 | } 42 | 43 | public ValueTask DisposeAsync() 44 | { 45 | return _source.DisposeAsync(); 46 | } 47 | 48 | public async ValueTask MoveNextAsync() 49 | { 50 | if (await _source.MoveNextAsync()) 51 | { 52 | return _predicate(_source.Current); 53 | } 54 | return false; 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/Timer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using System.Collections.Generic; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class Timer : IAsyncEnumerable 13 | { 14 | private readonly TimeSpan _delay; 15 | 16 | public Timer(TimeSpan delay) 17 | { 18 | _delay = delay; 19 | } 20 | 21 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 22 | { 23 | return new TimerEnumerator(_delay, cancellationToken); 24 | } 25 | 26 | internal sealed class TimerEnumerator : IAsyncEnumerator 27 | { 28 | private readonly TimeSpan _delay; 29 | 30 | private readonly CancellationToken _ct; 31 | 32 | private bool _once; 33 | 34 | public TimerEnumerator(TimeSpan delay, CancellationToken ct) 35 | { 36 | _delay = delay; 37 | _ct = ct; 38 | } 39 | 40 | public long Current => 0; 41 | 42 | public ValueTask DisposeAsync() 43 | { 44 | return new ValueTask(); 45 | } 46 | 47 | public async ValueTask MoveNextAsync() 48 | { 49 | if (_once) 50 | { 51 | return false; 52 | } 53 | _once = true; 54 | 55 | await Task.Delay(_delay, _ct).ConfigureAwait(false); 56 | return true; 57 | } 58 | } 59 | } 60 | 61 | internal sealed class TimerCancellable : IAsyncEnumerable 62 | { 63 | private readonly TimeSpan _delay; 64 | 65 | private readonly CancellationToken _token; 66 | 67 | public TimerCancellable(TimeSpan delay, CancellationToken token) 68 | { 69 | _delay = delay; 70 | _token = token; 71 | } 72 | 73 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken) 74 | { 75 | return new Timer.TimerEnumerator(_delay, CancellationTokenSource.CreateLinkedTokenSource(_token, cancellationToken).Token); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/ToCollection.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Runtime.CompilerServices; 8 | using System.Threading.Tasks; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal static class ToCollection 13 | { 14 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 15 | internal static async ValueTask> ToList(IAsyncEnumerable source) 16 | { 17 | var result = new List(); 18 | var en = source.GetAsyncEnumerator(default); 19 | try 20 | { 21 | while (await en.MoveNextAsync()) 22 | { 23 | result.Add(en.Current); 24 | } 25 | } 26 | finally 27 | { 28 | await en.DisposeAsync(); 29 | } 30 | 31 | return result; 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | internal static async ValueTask ToArray(IAsyncEnumerable source) 36 | { 37 | var en = source.GetAsyncEnumerator(default); 38 | try 39 | { 40 | if (await en.MoveNextAsync()) 41 | { 42 | var result = new List(); 43 | do 44 | { 45 | result.Add(en.Current); 46 | } while (await en.MoveNextAsync()); 47 | 48 | return result.ToArray(); 49 | } 50 | else 51 | { 52 | return Array.Empty(); 53 | } 54 | } 55 | finally 56 | { 57 | await en.DisposeAsync(); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /async-enumerable-dotnet/impl/ToEnumerable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) David Karnok & Contributors. 2 | // Licensed under the Apache 2.0 License. 3 | // See LICENSE file in the project root for full license information. 4 | 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | 10 | namespace async_enumerable_dotnet.impl 11 | { 12 | internal sealed class ToEnumerable : IEnumerable 13 | { 14 | private readonly IAsyncEnumerable _source; 15 | 16 | public ToEnumerable(IAsyncEnumerable source) 17 | { 18 | _source = source; 19 | } 20 | 21 | public IEnumerator GetEnumerator() 22 | { 23 | var cts = new CancellationTokenSource(); 24 | return new ToEnumerator(_source.GetAsyncEnumerator(cts.Token), cts); 25 | } 26 | 27 | IEnumerator IEnumerable.GetEnumerator() 28 | { 29 | return GetEnumerator(); 30 | } 31 | 32 | private sealed class ToEnumerator : IEnumerator 33 | { 34 | private readonly IAsyncEnumerator _source; 35 | 36 | private readonly CancellationTokenSource _cts; 37 | 38 | public ToEnumerator(IAsyncEnumerator source, CancellationTokenSource cts) 39 | { 40 | _source = source; 41 | _cts = cts; 42 | } 43 | 44 | public T Current => _source.Current; 45 | 46 | object IEnumerator.Current => Current; 47 | 48 | public void Dispose() 49 | { 50 | _cts.Cancel(); 51 | _source.DisposeAsync().AsTask().Wait(); 52 | } 53 | 54 | public bool MoveNext() 55 | { 56 | return _source.MoveNextAsync().AsTask().Result; 57 | } 58 | 59 | public void Reset() 60 | { 61 | throw new InvalidOperationException(); 62 | } 63 | } 64 | } 65 | } 66 | --------------------------------------------------------------------------------