├── .github
├── dependabot.yml
└── workflows
│ └── dotnet.yml
├── .travis.yml
├── async-enumerable-dotnet
├── impl
│ ├── EmptyHelper.cs
│ ├── Empty.cs
│ ├── Defer.cs
│ ├── Never.cs
│ ├── Just.cs
│ ├── Replay.cs
│ ├── Publish.cs
│ ├── Error.cs
│ ├── IgnoreElements.cs
│ ├── FromArray.cs
│ ├── Range.cs
│ ├── FromEnumerable.cs
│ ├── Count.cs
│ ├── IsEmpty.cs
│ ├── TakeWhile.cs
│ ├── ToCollection.cs
│ ├── ToEnumerable.cs
│ ├── SlimResume.cs
│ ├── Take.cs
│ ├── Skip.cs
│ ├── Timer.cs
│ ├── SkipWhile.cs
│ ├── SkipLast.cs
│ ├── CancellationHelper.cs
│ ├── ArrayQueue.cs
│ ├── SwitchIfEmpty.cs
│ ├── DefaultIfEmpty.cs
│ ├── Collect.cs
│ ├── TakeLast.cs
│ ├── QueueDrainHelper.cs
│ ├── ForEach.cs
│ ├── Distinct.cs
│ ├── FromTaskFunc.cs
│ ├── DistinctUntilChanged.cs
│ ├── OnErrorResumeNext.cs
│ ├── ElementAt.cs
│ ├── FirstLastSingleAsync.cs
│ └── DoOnNext.cs
├── IAsyncGroupedEnumerable.cs
├── async-enumerable-dotnet.csproj
└── IAsyncEmitter.cs
├── async-enumerable-dotnet-benchmark
├── async-enumerable-dotnet-benchmark.csproj
└── Program.cs
├── async-enumerable-dotnet-test
├── JustTest.cs
├── UnitTest1.cs
├── MergeWithTest.cs
├── FromEnumerableTest.cs
├── EmptyTest.cs
├── async-enumerable-dotnet-test.csproj
├── RangeTest.cs
├── DefaultIfEmptyTest.cs
├── SwitchIfEmptyTest.cs
├── TakeTest.cs
├── ListComparer.cs
├── FromTaskFuncTest.cs
├── MapTest.cs
├── FilterTest.cs
├── ErrorTest.cs
├── IgnoreElementsTest.cs
├── SkipWhileTest.cs
├── TakeWhileTest.cs
├── ForEachTest.cs
├── IsEmptyTest.cs
├── CancellationHelperTest.cs
├── NeverTest.cs
├── ToListTest.cs
├── IntervalTest.cs
├── DeferTest.cs
├── QueueDrainHelperTest.cs
├── DoOnNextTest.cs
├── TakeLastTest.cs
├── SkipLastTest.cs
├── SkipTest.cs
├── CollectTest.cs
├── TestHelperTest.cs
├── CountTest.cs
├── ElementAtTest.cs
├── ToEnumerableTest.cs
├── RepeatTest.cs
├── TimeoutTest.cs
├── TimerTest.cs
├── OnErrorResumeNextTest.cs
├── ReduceTest.cs
├── ArrayQueueTest.cs
├── ConsumeTest.cs
├── ReplayTest.cs
├── ZipTest.cs
├── ScanTest.cs
├── DistinctUntilChangedTest.cs
├── SkipUntilTest.cs
├── FirstLastSingleAsyncTest.cs
├── DistinctTest.cs
├── TakeUntilTest.cs
├── ToCollectionTest.cs
├── LatestTest.cs
├── PrefetchTest.cs
├── RetryTest.cs
├── WithLatestFromTest.cs
├── ConcatMapTest.cs
├── SlimResumeTest.cs
├── UsingTest.cs
├── ConcatTest.cs
├── SampleTest.cs
├── CreateTest.cs
├── ExceptionHelperTest.cs
├── ToObservableTest.cs
├── CombineLatestTest.cs
├── PublishTest.cs
├── ResumeHelperTest.cs
└── LicenseHeader.cs
├── async-enumerable-dotnet.sln
└── .gitattributes
/.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 |
--------------------------------------------------------------------------------
/.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/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-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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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/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/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-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/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/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/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/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/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/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/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-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/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/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-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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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-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/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/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-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/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/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-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/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/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/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/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/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-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/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 |
--------------------------------------------------------------------------------
/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/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/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-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/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-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.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-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-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/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/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