Skip to content

Commit 32a9d8e

Browse files
committed
Copy all Async classes and fix the types due to the change to System.Linq.Async
1 parent 974a9f9 commit 32a9d8e

73 files changed

Lines changed: 4793 additions & 55 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#if INTEGRATED_ASYNC
2+
using System.Collections.Immutable;
3+
using FsCheck;
4+
using FsCheck.Fluent;
5+
using FsCheck.Xunit;
6+
using Funcky.Test.TestUtilities;
7+
8+
namespace Funcky.Test.AsyncSequence;
9+
10+
public sealed class ConcatTest
11+
{
12+
[Fact]
13+
public async Task ConcatenatedSequenceIsEmptyWhenNoSourcesAreProvidedAsync()
14+
{
15+
await AsyncAssert.Empty(Funcky.AsyncSequence.Concat<object>());
16+
}
17+
18+
[Fact]
19+
public async Task ConcatenatedSequenceIsEmptyWhenAllSourcesAreEmptyAsync()
20+
{
21+
await AsyncAssert.Empty(Funcky.AsyncSequence.Concat(Enumerable.Empty<object>().ToAsyncEnumerable(), Enumerable.Empty<object>().ToAsyncEnumerable(), Enumerable.Empty<object>().ToAsyncEnumerable()));
22+
}
23+
24+
[Property]
25+
public Property ConcatenatedSequenceContainsElementsFromAllSourcesInOrder(int[][] sources)
26+
{
27+
var expected = sources.Aggregate(ImmutableArray<int>.Empty, (l, s) => l.AddRange(s)).ToAsyncEnumerable();
28+
29+
var innerOuterAsync = sources.Select(source => source.ToAsyncEnumerable()).ToAsyncEnumerable();
30+
var innerAsync = sources.Select(source => source.ToAsyncEnumerable());
31+
IAsyncEnumerable<IEnumerable<int>> outerAsync = sources.ToAsyncEnumerable();
32+
33+
var result = expected.SequenceEqualAsync(Funcky.AsyncSequence.Concat(innerOuterAsync)).Result
34+
&& expected.SequenceEqualAsync(Funcky.AsyncSequence.Concat(innerAsync)).Result
35+
&& expected.SequenceEqualAsync(Funcky.AsyncSequence.Concat(outerAsync)).Result;
36+
37+
return result.ToProperty();
38+
}
39+
}
40+
#endif
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#if INTEGRATED_ASYNC
2+
using FsCheck;
3+
using FsCheck.Fluent;
4+
using FsCheck.Xunit;
5+
using Funcky.Test.TestUtilities;
6+
7+
namespace Funcky.Test.AsyncSequence;
8+
9+
public sealed class CycleRangeTest
10+
{
11+
[Fact]
12+
public async Task CycleRangeIsEnumeratedLazilyAsync()
13+
{
14+
var doNotEnumerate = new FailOnEnumerateAsyncSequence<object>();
15+
16+
await using var cycleRange = Funcky.AsyncSequence.CycleRange(doNotEnumerate);
17+
}
18+
19+
[Fact]
20+
public async Task CyclingAnEmptySetThrowsAnArgumentException()
21+
=> await Assert.ThrowsAsync<InvalidOperationException>(CycleEmptySequenceAsync);
22+
23+
[Property]
24+
public Property CycleRangeCanProduceArbitraryManyItemsAsync(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
25+
=> (GetArbitraryManyItemsAsync(sequence.Get, arbitraryElements.Get).Result == arbitraryElements.Get)
26+
.ToProperty();
27+
28+
[Property]
29+
public Property CycleRangeRepeatsTheElementsArbitraryManyTimes(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
30+
=> CycleRangeRepeatsTheElementsArbitraryManyTimesAsync(sequence.Get.ToAsyncEnumerable(), arbitraryElements.Get)
31+
.Result.ToProperty();
32+
33+
[Fact]
34+
public async Task CycleRangeEnumeratesUnderlyingEnumerableOnlyOnceAsync()
35+
{
36+
var sequence = Sequence.Return("Test", "Hello", "Do", "Wait");
37+
var enumerateOnce = AsyncEnumerateOnce.Create(sequence);
38+
39+
await using var cycleRange = Funcky.AsyncSequence.CycleRange(enumerateOnce);
40+
41+
await cycleRange
42+
.Take(sequence.Count * 3)
43+
.ForEachAsync(NoOperation<string>);
44+
}
45+
46+
private static async Task<int> GetArbitraryManyItemsAsync(IEnumerable<int> sequence, int arbitraryElements)
47+
{
48+
await using var cycleRange = Funcky.AsyncSequence.CycleRange(sequence.ToAsyncEnumerable());
49+
50+
return await cycleRange.Take(arbitraryElements).CountAsync();
51+
}
52+
53+
private static async Task CycleEmptySequenceAsync()
54+
{
55+
await using var cycledRange = Funcky.AsyncSequence.CycleRange(Funcky.AsyncSequence.Return<string>());
56+
await using var enumerator = cycledRange.GetAsyncEnumerator();
57+
58+
await enumerator.MoveNextAsync();
59+
}
60+
61+
private async Task<bool> CycleRangeRepeatsTheElementsArbitraryManyTimesAsync(IAsyncEnumerable<int> asyncEnumerable, int arbitraryElements)
62+
{
63+
await using var cycleRange = Funcky.AsyncSequence.CycleRange(asyncEnumerable);
64+
65+
return await cycleRange
66+
.IsSequenceRepeating(asyncEnumerable)
67+
.NTimes(arbitraryElements);
68+
}
69+
}
70+
#endif
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#if INTEGRATED_ASYNC
2+
using FsCheck;
3+
using FsCheck.Fluent;
4+
using FsCheck.Xunit;
5+
using Funcky.Test.TestUtilities;
6+
7+
namespace Funcky.Test.AsyncSequence;
8+
9+
public sealed class CycleTest
10+
{
11+
[Property]
12+
public Property CycleCanProduceArbitraryManyItems(int value, PositiveInt arbitraryElements)
13+
=> (Funcky.AsyncSequence.Cycle(value).Take(arbitraryElements.Get).CountAsync().Result == arbitraryElements.Get)
14+
.ToProperty();
15+
16+
[Property]
17+
public Property CycleRepeatsTheElementArbitraryManyTimes(int value, PositiveInt arbitraryElements)
18+
=> Funcky.AsyncSequence
19+
.Cycle(value)
20+
.IsSequenceRepeating(Funcky.AsyncSequence.Return(value))
21+
.NTimes(arbitraryElements.Get)
22+
.Result
23+
.ToProperty();
24+
}
25+
#endif
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#if INTEGRATED_ASYNC
2+
using FsCheck;
3+
using FsCheck.Fluent;
4+
using FsCheck.Xunit;
5+
using Funcky.Test.TestUtilities;
6+
7+
namespace Funcky.Test.AsyncSequence;
8+
9+
public sealed class RepeatRangeTest
10+
{
11+
[Fact]
12+
public async Task RepeatRangeIsEnumeratedLazily()
13+
{
14+
var doNotEnumerate = new FailOnEnumerateAsyncSequence<object>();
15+
16+
await using var repeatRange = Funcky.AsyncSequence.RepeatRange(doNotEnumerate, 2);
17+
}
18+
19+
[Fact]
20+
public async Task RepeatRangeThrowsWhenAlreadyDisposedAsync()
21+
{
22+
var repeatRange = Funcky.AsyncSequence.RepeatRange(Funcky.AsyncSequence.Return(1337), 5);
23+
24+
#pragma warning disable IDISP016 // we test behaviour after Dispose
25+
#pragma warning disable IDISP017 // we test behaviour after Dispose
26+
await repeatRange.DisposeAsync();
27+
#pragma warning restore IDISP016
28+
#pragma warning restore IDISP017
29+
30+
await Assert.ThrowsAsync<ObjectDisposedException>(() => repeatRange.ForEachAsync(NoOperation<int>));
31+
}
32+
33+
[Fact]
34+
public async Task RepeatRangeThrowsWhenAlreadyDisposedEvenIfYouDisposeBetweenMoveNextAsync()
35+
{
36+
var list = Funcky.AsyncSequence.Return(1337, 2, 5);
37+
38+
const int repeats = 5;
39+
40+
foreach (var i in Enumerable.Range(0, await list.CountAsync() * repeats))
41+
{
42+
var repeatRange = Funcky.AsyncSequence.RepeatRange(list, repeats);
43+
await using var enumerator = repeatRange.GetAsyncEnumerator();
44+
45+
Assert.True(await AsyncEnumerable.Range(0, i).AllAwaitAsync(async _ => await enumerator.MoveNextAsync()));
46+
47+
#pragma warning disable IDISP016 // we test behaviour after Dispose
48+
#pragma warning disable IDISP017 // we test behaviour after Dispose
49+
await repeatRange.DisposeAsync();
50+
#pragma warning restore IDISP016
51+
#pragma warning restore IDISP017
52+
53+
await Assert.ThrowsAnyAsync<ObjectDisposedException>(async () => await enumerator.MoveNextAsync());
54+
}
55+
}
56+
57+
[Property]
58+
public Property TheLengthOfTheGeneratedRepeatRangeIsCorrect(List<int> list, NonNegativeInt count)
59+
=> TheLengthOfTheGeneratedRepeatRangeIsCorrectAsync(list, count.Get)
60+
.Result
61+
.ToProperty();
62+
63+
[Property]
64+
public Property TheSequenceRepeatsTheGivenNumberOfTimes(List<int> list, NonNegativeInt count)
65+
=> TheSequenceRepeatsTheGivenNumberOfTimesAsync(list.ToAsyncEnumerable(), count.Get)
66+
.Result
67+
.ToProperty();
68+
69+
[Fact]
70+
public async Task RepeatRangeEnumeratesUnderlyingEnumerableOnlyOnceAsync()
71+
{
72+
var sequence = Sequence.Return("Test", "Hello", "Do", "Wait");
73+
var enumerateOnce = AsyncEnumerateOnce.Create(sequence);
74+
75+
await using var repeatRange = Funcky.AsyncSequence.RepeatRange(enumerateOnce, 3);
76+
77+
await repeatRange.ForEachAsync(NoOperation<string>);
78+
}
79+
80+
private static async Task<bool> TheLengthOfTheGeneratedRepeatRangeIsCorrectAsync(List<int> list, int count)
81+
{
82+
await using var repeatRange = Funcky.AsyncSequence.RepeatRange(list.ToAsyncEnumerable(), count);
83+
84+
var materialized = await repeatRange.ToListAsync();
85+
86+
return materialized.Count == list.Count * count;
87+
}
88+
89+
private static async Task<bool> TheSequenceRepeatsTheGivenNumberOfTimesAsync(IAsyncEnumerable<int> asyncEnumerable, int count)
90+
{
91+
await using var repeatRange = Funcky.AsyncSequence.RepeatRange(asyncEnumerable, count);
92+
93+
return await repeatRange
94+
.IsSequenceRepeating(asyncEnumerable)
95+
.NTimes(count);
96+
}
97+
}
98+
#endif
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#if INTEGRATED_ASYNC
2+
using FsCheck;
3+
using FsCheck.Fluent;
4+
using FsCheck.Xunit;
5+
using Funcky.Test.TestUtilities;
6+
7+
namespace Funcky.Test.AsyncSequence;
8+
9+
public sealed class ReturnTest
10+
{
11+
[Property]
12+
public Property ReturnOfASingleItemElevatesThatItemIntoASingleItemedEnumerable(int item)
13+
{
14+
var sequence = Funcky.AsyncSequence.Return(item);
15+
16+
return (sequence.SingleOrNoneAsync().Result == item).ToProperty();
17+
}
18+
19+
[Fact]
20+
public async Task SequenceReturnCreatesAnEnumerableFromAnArbitraryNumberOfParameters()
21+
{
22+
const string one = "Alpha";
23+
const string two = "Beta";
24+
const string three = "Gamma";
25+
26+
var sequence = Funcky.AsyncSequence.Return(one, two, three);
27+
28+
await AsyncAssert.Collection(
29+
sequence,
30+
element1 => Assert.Equal(one, element1),
31+
element2 => Assert.Equal(two, element2),
32+
element3 => Assert.Equal(three, element3));
33+
}
34+
}
35+
#endif
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#if INTEGRATED_ASYNC
2+
using Funcky.Test.TestUtilities;
3+
4+
namespace Funcky.Test.AsyncSequence;
5+
6+
public sealed class SuccessorsTest
7+
{
8+
[Fact]
9+
public async Task ReturnsEmptySequenceWhenFirstItemIsNoneAsync()
10+
{
11+
await AsyncAssert.Empty(Funcky.AsyncSequence.Successors(Option<int>.None, ValueTask.FromResult));
12+
}
13+
14+
[Fact]
15+
public async Task ReturnsOnlyTheFirstItemWhenSuccessorFunctionImmediatelyReturnsNoneAsync()
16+
{
17+
var first = await AsyncAssert.Single(Funcky.AsyncSequence.Successors(10, _ => ValueTask.FromResult(Option<int>.None)));
18+
Assert.Equal(10, first);
19+
}
20+
21+
[Fact]
22+
public async Task SuccessorsWithNonOptionFunctionReturnsEndlessEnumerableAsync()
23+
{
24+
const int count = 40;
25+
Assert.Equal(count, await Funcky.AsyncSequence.Successors(0, ValueTask.FromResult).Take(count).CountAsync());
26+
}
27+
28+
[Fact]
29+
public async Task SuccessorsReturnsEnumerableThatReturnsValuesBasedOnSeedAsync()
30+
{
31+
await AsyncAssert.Equal(
32+
AsyncEnumerable.Range(0, 10),
33+
Funcky.AsyncSequence.Successors(0, i => ValueTask.FromResult(i + 1)).Take(10));
34+
}
35+
36+
[Fact]
37+
public async Task SuccessorsReturnsEnumerableThatReturnsItemUntilNoneIsReturnedFromFuncAsync()
38+
{
39+
await AsyncAssert.Equal(
40+
AsyncEnumerable.Range(0, 11),
41+
Funcky.AsyncSequence.Successors(0, i => ValueTask.FromResult(Option.FromBoolean(i < 10, i + 1))));
42+
}
43+
}
44+
#endif
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#if INTEGRATED_ASYNC
2+
namespace Funcky;
3+
4+
public static partial class AsyncSequence
5+
{
6+
/// <summary>
7+
/// Concatenates multiple sequences together.
8+
/// </summary>
9+
[Pure]
10+
public static IAsyncEnumerable<TSource> Concat<TSource>(params IAsyncEnumerable<TSource>[] sources)
11+
=> Concat(sources.AsEnumerable());
12+
13+
/// <summary>
14+
/// Concatenates multiple sequences together.
15+
/// </summary>
16+
[Pure]
17+
public static IAsyncEnumerable<TSource> Concat<TSource>(IAsyncEnumerable<IAsyncEnumerable<TSource>> sources)
18+
=> sources
19+
.SelectMany(Identity);
20+
21+
/// <summary>
22+
/// Concatenates multiple sequences together.
23+
/// </summary>
24+
[Pure]
25+
public static IAsyncEnumerable<TSource> Concat<TSource>(IEnumerable<IAsyncEnumerable<TSource>> sources)
26+
=> sources.ToAsyncEnumerable()
27+
.SelectMany(Identity);
28+
29+
/// <summary>
30+
/// Concatenates multiple sequences together.
31+
/// </summary>
32+
[Pure]
33+
public static IAsyncEnumerable<TSource> Concat<TSource>(IAsyncEnumerable<IEnumerable<TSource>> sources)
34+
=> sources
35+
.SelectMany(AsyncEnumerable.ToAsyncEnumerable);
36+
}
37+
#endif
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#if INTEGRATED_ASYNC
2+
using static Funcky.ValueTaskFactory;
3+
4+
namespace Funcky;
5+
6+
public static partial class AsyncSequence
7+
{
8+
/// <summary>
9+
/// Cycles the same element over and over again as an endless generator.
10+
/// </summary>
11+
/// <typeparam name="TResult">Type of the element to be cycled.</typeparam>
12+
/// <param name="element">The element to be cycled.</param>
13+
/// <returns>Returns an infinite IEnumerable cycling through the same elements.</returns>
14+
[Pure]
15+
public static IAsyncEnumerable<TResult> Cycle<TResult>(TResult element)
16+
=> Successors(element, ValueTaskFromResult);
17+
}
18+
#endif

0 commit comments

Comments
 (0)