exception testing

master
neuecc 2020-05-10 22:44:40 +09:00
parent af6dbd8868
commit 93dd82e3d4
17 changed files with 926 additions and 3963 deletions

View File

@ -110,9 +110,12 @@ namespace Cysharp.Threading.Tasks.Linq
{
var self = (Enumerator)state;
if (self.awaiter.GetResult())
if (self.TryGetResult(self.awaiter, out var result))
{
if (result)
{
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
}
else
{
@ -120,16 +123,15 @@ namespace Cysharp.Threading.Tasks.Linq
{
self.state = State.Completed;
self.Current = self.element;
self.completionSource.TrySetResult(true);
}
else
{
self.state = State.Completed;
self.completionSource.TrySetResult(false);
return;
}
}
self.completionSource.TrySetResult(true);
}
}
public UniTask DisposeAsync()
@ -144,770 +146,3 @@ namespace Cysharp.Threading.Tasks.Linq
}
}

View File

@ -31,6 +31,21 @@ namespace Cysharp.Threading.Tasks.Linq
{
completionSource.GetResult(token);
}
protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result)
{
try
{
result = awaiter.GetResult();
return true;
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
result = default;
return false;
}
}
}

View File

@ -1,775 +1,53 @@
namespace Cysharp.Threading.Tasks.Linq
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
internal sealed class Cast
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TResult> Cast<TResult>(this IUniTaskAsyncEnumerable<Object> source)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new Cast<TResult>(source);
}
}
internal sealed class Cast<TResult> : IUniTaskAsyncEnumerable<TResult>
{
readonly IUniTaskAsyncEnumerable<object> source;
public Cast(IUniTaskAsyncEnumerable<object> source)
{
this.source = source;
}
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, cancellationToken);
}
class Enumerator : AsyncEnumeratorBase<object, TResult>
{
public Enumerator(IUniTaskAsyncEnumerable<object> source, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
}
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
{
if (sourceHasCurrent)
{
Current = (TResult)SourceCurrent;
result = true;
return true;
}
result = false;
return true;
}
}
}
}

View File

@ -86,7 +86,15 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
try
{
awaiter = enumerator.MoveNextAsync().GetAwaiter();
}
catch (Exception ex)
{
completionSource.TrySetException(ex);
return;
}
if (awaiter.IsCompleted)
{
@ -102,7 +110,9 @@ namespace Cysharp.Threading.Tasks.Linq
{
var self = (Enumerator)state;
if (self.awaiter.GetResult())
if (self.TryGetResult(self.awaiter, out var result))
{
if (result)
{
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
@ -119,6 +129,7 @@ namespace Cysharp.Threading.Tasks.Linq
self.completionSource.TrySetResult(false);
}
}
}
async UniTaskVoid RunSecondAfterDisposeAsync()
{

View File

@ -1,775 +0,0 @@
namespace Cysharp.Threading.Tasks.Linq
{
internal sealed class Create
{
}
}

View File

@ -1,775 +1,135 @@
namespace Cysharp.Threading.Tasks.Linq
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
internal sealed class DefaultIfEmpty
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TSource> DefaultIfEmpty<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new DefaultIfEmpty<TSource>(source, default);
}
public static IUniTaskAsyncEnumerable<TSource> DefaultIfEmpty<TSource>(this IUniTaskAsyncEnumerable<TSource> source, TSource defaultValue)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new DefaultIfEmpty<TSource>(source, defaultValue);
}
}
internal sealed class DefaultIfEmpty<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly TSource defaultValue;
public DefaultIfEmpty(IUniTaskAsyncEnumerable<TSource> source, TSource defaultValue)
{
this.source = source;
this.defaultValue = defaultValue;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, defaultValue, cancellationToken);
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<TSource>
{
enum IteratingState : byte
{
Empty,
Iterating,
Completed
}
static readonly Action<object> MoveNextCoreDelegate = MoveNextCore;
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly TSource defaultValue;
CancellationToken cancellationToken;
IteratingState iteratingState;
IUniTaskAsyncEnumerator<TSource> enumerator;
UniTask<bool>.Awaiter awaiter;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, TSource defaultValue, CancellationToken cancellationToken)
{
this.source = source;
this.defaultValue = defaultValue;
this.cancellationToken = cancellationToken;
this.iteratingState = IteratingState.Empty;
}
public TSource Current { get; private set; }
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
completionSource.Reset();
if (iteratingState == IteratingState.Empty)
{
return CompletedTasks.False;
}
awaiter = enumerator.MoveNextAsync().GetAwaiter();
if (awaiter.IsCompleted)
{
MoveNextCore(this);
}
else
{
awaiter.SourceOnCompleted(MoveNextCoreDelegate, this);
}
return new UniTask<bool>(this, completionSource.Version);
}
static void MoveNextCore(object state)
{
var self = (Enumerator)state;
if (self.TryGetResult(self.awaiter, out var result))
{
if (result)
{
self.iteratingState = IteratingState.Iterating;
self.Current = self.enumerator.Current;
self.completionSource.TrySetResult(true);
}
else
{
if (self.iteratingState == IteratingState.Empty)
{
self.iteratingState = IteratingState.Completed;
self.Current = self.defaultValue;
self.completionSource.TrySetResult(true);
}
else
{
self.completionSource.TrySetResult(false);
}
}
}
}
public UniTask DisposeAsync()
{
if (enumerator != null)
{
return enumerator.DisposeAsync();
}
return default;
}
}
}
}

View File

@ -1,775 +1,61 @@
namespace Cysharp.Threading.Tasks.Linq
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
internal sealed class OfType
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TResult> OfType<TResult>(this IUniTaskAsyncEnumerable<Object> source)
{
Error.ThrowArgumentNullException(source, nameof(source));
return new OfType<TResult>(source);
}
}
internal sealed class OfType<TResult> : IUniTaskAsyncEnumerable<TResult>
{
readonly IUniTaskAsyncEnumerable<object> source;
public OfType(IUniTaskAsyncEnumerable<object> source)
{
this.source = source;
}
public IUniTaskAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, cancellationToken);
}
class Enumerator : AsyncEnumeratorBase<object, TResult>
{
public Enumerator(IUniTaskAsyncEnumerable<object> source, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
}
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
{
if (sourceHasCurrent)
{
if (SourceCurrent is TResult castCurent)
{
Current = castCurent;
result = true;
return true;
}
else
{
result = default;
return false;
}
}
result = false;
return true;
}
}
}
}

View File

@ -0,0 +1,54 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TValue> Throw<TValue>(Exception exception)
{
return new Throw<TValue>(exception);
}
}
internal class Throw<TValue> : IUniTaskAsyncEnumerable<TValue>
{
readonly Exception exception;
public Throw(Exception exception)
{
this.exception = exception;
}
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(exception, cancellationToken);
}
class Enumerator : IUniTaskAsyncEnumerator<TValue>
{
readonly Exception exception;
CancellationToken cancellationToken;
public Enumerator(Exception exception, CancellationToken cancellationToken)
{
this.exception = exception;
this.cancellationToken = cancellationToken;
}
public TValue Current => default;
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
return UniTask.FromException<bool>(exception);
}
public UniTask DisposeAsync()
{
return default;
}
}
}
}

View File

@ -243,20 +243,22 @@ namespace Cysharp.Threading.Tasks.Linq
{
get
{
if (error != null)
{
ExceptionDispatchInfo.Capture(error).Throw();
}
if (useCachedCurrent)
{
return current;
}
if (queuedResult.Count != 0)
{
current = queuedResult.Dequeue();
useCachedCurrent = true;
return current;
}
else
{
return default; // undefined.
}
}
}
public UniTask<bool> MoveNextAsync()

View File

@ -1,18 +1,57 @@
using System;
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks.Linq
{
public static partial class UniTaskAsyncEnumerable
{
public static IUniTaskAsyncEnumerable<TSource> Where<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, bool> predicate)
public static IUniTaskAsyncEnumerable<TSource> Where<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate)
{
return new Cysharp.Threading.Tasks.Linq.Where<TSource>(source, predicate);
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return new Where<TSource>(source, predicate);
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<bool>> predicate)
public static IUniTaskAsyncEnumerable<TSource> Where<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32, Boolean> predicate)
{
return new Cysharp.Threading.Tasks.Linq.WhereAwait<TSource>(source, predicate);
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return new WhereInt<TSource>(source, predicate);
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return new WhereAwait<TSource>(source, predicate);
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32, UniTask<Boolean>> predicate)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return new WhereAwaitInt<TSource>(source, predicate);
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwaitWithCancellation<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return new WhereAwaitCancellation<TSource>(source, predicate);
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwaitWithCancellation<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32, CancellationToken, UniTask<Boolean>> predicate)
{
Error.ThrowArgumentNullException(source, nameof(source));
Error.ThrowArgumentNullException(predicate, nameof(predicate));
return new WhereAwaitIntCancellation<TSource>(source, predicate);
}
}
@ -66,6 +105,57 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal sealed class WhereInt<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, int, bool> predicate;
public WhereInt(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate)
{
this.source = source;
this.predicate = predicate;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, predicate, cancellationToken);
}
class Enumerator : AsyncEnumeratorBase<TSource, TSource>
{
readonly Func<TSource, int, bool> predicate;
int index;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.predicate = predicate;
}
protected override bool TryMoveNextCore(bool sourceHasCurrent, out bool result)
{
if (sourceHasCurrent)
{
if (predicate(SourceCurrent, checked(index++)))
{
Current = SourceCurrent;
result = true;
return true;
}
else
{
result = default;
return false;
}
}
result = false;
return true;
}
}
}
internal sealed class WhereAwait<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
@ -113,4 +203,149 @@ namespace Cysharp.Threading.Tasks.Linq
}
}
internal sealed class WhereAwaitInt<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, int, UniTask<bool>> predicate;
public WhereAwaitInt(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<bool>> predicate)
{
this.source = source;
this.predicate = predicate;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, predicate, cancellationToken);
}
class Enumerator : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
{
readonly Func<TSource, int, UniTask<bool>> predicate;
int index;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, UniTask<bool>> predicate, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.predicate = predicate;
}
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
{
return predicate(sourceCurrent, checked(index++));
}
protected override bool TrySetCurrentCore(bool awaitResult)
{
if (awaitResult)
{
Current = SourceCurrent;
return true;
}
else
{
return false;
}
}
}
}
internal sealed class WhereAwaitCancellation<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, CancellationToken, UniTask<bool>> predicate;
public WhereAwaitCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<bool>> predicate)
{
this.source = source;
this.predicate = predicate;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, predicate, cancellationToken);
}
class Enumerator : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
{
readonly Func<TSource, CancellationToken, UniTask<bool>> predicate;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.predicate = predicate;
}
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
{
return predicate(sourceCurrent, cancellationToken);
}
protected override bool TrySetCurrentCore(bool awaitResult)
{
if (awaitResult)
{
Current = SourceCurrent;
return true;
}
else
{
return false;
}
}
}
}
internal sealed class WhereAwaitIntCancellation<TSource> : IUniTaskAsyncEnumerable<TSource>
{
readonly IUniTaskAsyncEnumerable<TSource> source;
readonly Func<TSource, int, CancellationToken, UniTask<bool>> predicate;
public WhereAwaitIntCancellation(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<bool>> predicate)
{
this.source = source;
this.predicate = predicate;
}
public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(source, predicate, cancellationToken);
}
class Enumerator : AsyncEnumeratorAwaitSelectorBase<TSource, TSource, bool>
{
readonly Func<TSource, int, CancellationToken, UniTask<bool>> predicate;
int index;
public Enumerator(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, UniTask<bool>> predicate, CancellationToken cancellationToken)
: base(source, cancellationToken)
{
this.predicate = predicate;
}
protected override UniTask<bool> TransformAsync(TSource sourceCurrent)
{
return predicate(sourceCurrent, checked(index++), cancellationToken);
}
protected override bool TrySetCurrentCore(bool awaitResult)
{
if (awaitResult)
{
Current = SourceCurrent;
return true;
}
else
{
return false;
}
}
}
}
}

View File

@ -90,9 +90,20 @@ namespace Cysharp.Threading.Tasks.Linq
{
var self = (Enumerator)state;
if (self.firstAwaiter.GetResult())
if (self.TryGetResult(self.firstAwaiter, out var result))
{
if (result)
{
try
{
self.secondAwaiter = self.secondEnumerator.MoveNextAsync().GetAwaiter();
}
catch (Exception ex)
{
self.completionSource.TrySetException(ex);
return;
}
if (self.secondAwaiter.IsCompleted)
{
SecondMoveNextCore(self);
@ -107,6 +118,7 @@ namespace Cysharp.Threading.Tasks.Linq
self.completionSource.TrySetResult(false);
}
}
}
static void SecondMoveNextCore(object state)
{

View File

@ -24,24 +24,8 @@ namespace ___Dummy
public static IUniTaskAsyncEnumerable<TResult> Cast<TResult>(this IUniTaskAsyncEnumerable<Object> source)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> DefaultIfEmpty<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> DefaultIfEmpty<TSource>(this IUniTaskAsyncEnumerable<TSource> source, TSource defaultValue)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> Distinct<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
{
throw new NotImplementedException();
@ -259,11 +243,6 @@ namespace ___Dummy
public static IUniTaskAsyncEnumerable<TResult> OfType<TResult>(this IUniTaskAsyncEnumerable<Object> source)
{
throw new NotImplementedException();
}
public static IOrderedAsyncEnumerable<TSource> OrderBy<TSource, TKey>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
throw new NotImplementedException();
@ -550,35 +529,7 @@ namespace ___Dummy
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> Where<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Boolean> predicate)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> Where<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32, Boolean> predicate)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, UniTask<Boolean>> predicate)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwait<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32, UniTask<Boolean>> predicate)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwaitWithCancellation<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, UniTask<Boolean>> predicate)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<TSource> WhereAwaitWithCancellation<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, Int32, CancellationToken, UniTask<Boolean>> predicate)
{
throw new NotImplementedException();
}
public static IUniTaskAsyncEnumerable<ValueTuple<TFirst, TSecond>> Zip<TFirst, TSecond>(this IUniTaskAsyncEnumerable<TFirst> first, IUniTaskAsyncEnumerable<TSecond> second)
{

View File

@ -37,24 +37,28 @@ namespace NetCoreSandbox
await Task.Delay(10, cancellationToken);
}
static void Main(string[] args)
static async Task Main(string[] args)
{
// Create Canceled token.
var cts = new CancellationTokenSource();
cts.Cancel();
// OK, don't throw.
var e1 = FooAsync(cts.Token).GetAsyncEnumerator(cts.Token);
Console.WriteLine("OK:FooAsyunc().GetAsyncEnumerator()");
// Ix.Async LINQ Operator throws OperationCanceledException
var e2 = FooAsync(cts.Token).Select(x => x).GetAsyncEnumerator(cts.Token);
Console.WriteLine("YEAH");
try
{
var xs = await UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskAsyncEnumerable.Throw<int>(new InvalidOperationException("something")))
.SumAsync();
}
catch (Exception ex)
{
Console.WriteLine("EX");
Console.WriteLine(ex);
}
}
void Foo()
{
// AsyncEnumerable.t
var sb = new StringBuilder();

View File

@ -27,6 +27,19 @@ namespace NetCoreTests.Linq
xs.Should().BeEquivalentTo(ys);
}
[Fact]
public async Task AppendThrow()
{
var xs = UniTaskTestException.ThrowImmediate().Append(99).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
var ys = UniTaskTestException.ThrowAfter().Append(99).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
var zs = UniTaskTestException.ThrowInMoveNext().Append(99).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
}
[Theory]
[InlineData(0, 0)]
[InlineData(0, 1)]
@ -40,6 +53,19 @@ namespace NetCoreTests.Linq
xs.Should().BeEquivalentTo(ys);
}
[Fact]
public async Task PrependThrow()
{
var xs = UniTaskTestException.ThrowImmediate().Prepend(99).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
var ys = UniTaskTestException.ThrowAfter().Prepend(99).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
var zs = UniTaskTestException.ThrowInMoveNext().Prepend(99).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
}
public static IEnumerable<object[]> array1 = new object[][]
{
new object[] { (0, 0), (0, 0) }, // empty + empty
@ -61,5 +87,30 @@ namespace NetCoreTests.Linq
var ys = l.Concat(r).ToArray();
xs.Should().BeEquivalentTo(ys);
}
[Fact]
public async Task ConcatThrow()
{
{
var xs = UniTaskTestException.ThrowImmediate().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
var ys = UniTaskTestException.ThrowAfter().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
var zs = UniTaskTestException.ThrowInMoveNext().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
}
{
var xs = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowImmediate()).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
var ys = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowAfter()).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
var zs = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowInMoveNext()).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
}
}
}
}

View File

@ -0,0 +1,118 @@
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Xunit;
namespace NetCoreTests.Linq
{
public class Filtering
{
[Fact]
public async Task Where()
{
var range = Enumerable.Range(1, 10);
var src = range.ToUniTaskAsyncEnumerable();
{
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
var expected = range.Where(x => x % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected);
}
{
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected);
}
{
var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync();
var b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
var expected = range.Where(x => x % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected);
b.Should().BeEquivalentTo(expected);
}
{
var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync();
var b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync();
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
a.Should().BeEquivalentTo(expected);
b.Should().BeEquivalentTo(expected);
}
}
[Fact]
public async Task WhereException()
{
foreach (var item in UniTaskTestException.Throws())
{
{
var xs = item.Where(x => x % 2 == 0).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
{
var xs = item.Where((x, i) => x % 2 == 0).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
{
var xs = item.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
{
var xs = item.WhereAwait((x, i) => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
}
}
[Fact]
public async Task OfType()
{
var data = new object[] { 0, null, 10, 30, null, "foo", 99 };
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
var b = data.OfType<int>().ToArray();
a.Should().BeEquivalentTo(b);
}
[Fact]
public async Task OfTypeException()
{
foreach (var item in UniTaskTestException.Throws())
{
var xs = item.Select(x => (object)x).OfType<int>().ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
}
[Fact]
public async Task Cast()
{
var data = new object[] { 0, 10, 30, 99 };
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
var b = data.Cast<int>().ToArray();
a.Should().BeEquivalentTo(b);
}
[Fact]
public async Task CastException()
{
foreach (var item in UniTaskTestException.Throws())
{
var xs = item.Select(x => (object)x).Cast<int>().ToArrayAsync();
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
}
}
}
}

View File

@ -0,0 +1,126 @@
using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Linq;
using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace NetCoreTests.Linq
{
public class UniTaskTestException : Exception
{
public static IUniTaskAsyncEnumerable<int> ThrowImmediate()
{
return UniTaskAsyncEnumerable.Throw<int>(new UniTaskTestException());
}
public static IUniTaskAsyncEnumerable<int> ThrowAfter()
{
return new ThrowAfter<int>(new UniTaskTestException());
}
public static IUniTaskAsyncEnumerable<int> ThrowInMoveNext()
{
return new ThrowIn<int>(new UniTaskTestException());
}
public static IEnumerable<IUniTaskAsyncEnumerable<int>> Throws()
{
yield return ThrowImmediate();
yield return ThrowAfter();
yield return ThrowInMoveNext();
yield return UniTaskAsyncEnumerable.Range(1, 3).Concat(ThrowImmediate());
yield return UniTaskAsyncEnumerable.Range(1, 3).Concat(ThrowAfter());
yield return UniTaskAsyncEnumerable.Range(1, 3).Concat(ThrowInMoveNext());
}
}
internal class ThrowIn<TValue> : IUniTaskAsyncEnumerable<TValue>
{
readonly Exception exception;
public ThrowIn(Exception exception)
{
this.exception = exception;
}
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(exception, cancellationToken);
}
class Enumerator : IUniTaskAsyncEnumerator<TValue>
{
readonly Exception exception;
CancellationToken cancellationToken;
public Enumerator(Exception exception, CancellationToken cancellationToken)
{
this.exception = exception;
this.cancellationToken = cancellationToken;
}
public TValue Current => default;
public UniTask<bool> MoveNextAsync()
{
ExceptionDispatchInfo.Capture(exception).Throw();
return new UniTask<bool>(false);
}
public UniTask DisposeAsync()
{
return default;
}
}
}
internal class ThrowAfter<TValue> : IUniTaskAsyncEnumerable<TValue>
{
readonly Exception exception;
public ThrowAfter(Exception exception)
{
this.exception = exception;
}
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new Enumerator(exception, cancellationToken);
}
class Enumerator : IUniTaskAsyncEnumerator<TValue>
{
readonly Exception exception;
CancellationToken cancellationToken;
public Enumerator(Exception exception, CancellationToken cancellationToken)
{
this.exception = exception;
this.cancellationToken = cancellationToken;
}
public TValue Current => default;
public UniTask<bool> MoveNextAsync()
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new UniTaskCompletionSource<bool>();
var awaiter = UniTask.Yield().GetAwaiter();
awaiter.UnsafeOnCompleted(() =>
{
Thread.Sleep(1);
tcs.TrySetException(exception);
});
return tcs.Task;
}
public UniTask DisposeAsync()
{
return default;
}
}
}
}

View File

@ -93,7 +93,7 @@ namespace Cysharp.Threading.Tasks
break;
}
#else
Console.WriteLine(ex.ToString());
Console.WriteLine("UnobservedTaskException: " + ex.ToString());
#endif
}
}