1
0
Эх сурвалжийг харах

More tests for First and Last.

Bart De Smet 6 жил өмнө
parent
commit
bf9361a43e

+ 258 - 17
Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/First.cs

@@ -13,80 +13,321 @@ namespace Tests
     public class First : AsyncEnumerableTests
     {
         [Fact]
-        public async Task First_Null()
+        public async Task FirstAsync_Null()
         {
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, CancellationToken.None));
+
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, x => true));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync(Return42, default(Func<int, bool>)));
 
-            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, x => true, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync(Return42, default(Func<int, bool>), CancellationToken.None));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, x => new ValueTask<bool>(true)));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync(Return42, default(Func<int, ValueTask<bool>>)));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, x => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync(Return42, default(Func<int, ValueTask<bool>>), CancellationToken.None));
+
+#if !NO_DEEP_CANCELLATION
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync<int>(default, (x, ct) => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstAsync(Return42, default(Func<int, CancellationToken, ValueTask<bool>>), CancellationToken.None));
+#endif
         }
 
         [Fact]
-        public async Task First1Async()
+        public async Task FirstAsync_NoParam_Empty()
         {
             var res = AsyncEnumerable.Empty<int>().FirstAsync();
             await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task First2Async()
+        public async Task FirstAsync_NoParam_Empty_Enumerable()
         {
-            var res = AsyncEnumerable.Empty<int>().FirstAsync(x => true);
+            var res = new int[0].Select(x => x).ToAsyncEnumerable().FirstAsync();
             await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task First3Async()
+        public async Task FirstAsync_NoParam_Empty_IList()
         {
-            var res = Return42.FirstAsync(x => x % 2 != 0);
+            var res = new int[0].ToAsyncEnumerable().FirstAsync();
             await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task First4Async()
+        public async Task FirstAsync_NoParam_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).FirstAsync();
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task FirstAsync_NoParam_Single_IList()
         {
             var res = Return42.FirstAsync();
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task First5Async()
+        public async Task FirstAsync_NoParam_Single()
+        {
+            var res = new[] { 42 }.Select(x => x).ToAsyncEnumerable().FirstAsync();
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_NoParam_Many_IList()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstAsync();
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_NoParam_Many()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().FirstAsync();
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().FirstAsync(x => true);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).FirstAsync(x => true);
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Single_None()
+        {
+            var res = Return42.FirstAsync(x => x % 2 != 0);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Single_Pass()
         {
             var res = Return42.FirstAsync(x => x % 2 == 0);
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task First6Async()
+        public async Task FirstAsync_Predicate_Many_IList_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Many_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_Predicate_PredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().FirstAsync(x => 1 / x > 0);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().FirstAsync(x => new ValueTask<bool>(true));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Throw()
         {
             var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).FirstAsync();
+            var res = Throw<int>(ex).FirstAsync(x => new ValueTask<bool>(true));
             await AssertThrowsAsync(res, ex);
         }
 
         [Fact]
-        public async Task First7Async()
+        public async Task FirstAsync_AsyncPredicate_Single_None()
+        {
+            var res = Return42.FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().Select(x => x).FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Single_Pass()
+        {
+            var res = Return42.FirstAsync(x => new ValueTask<bool>(x % 2 == 0));
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Many_IList_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Many_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().FirstAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicate_AsyncPredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().FirstAsync(x => new ValueTask<bool>(1 / x > 0));
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+#if !NO_DEEP_CANCELLATION
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().FirstAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Throw()
         {
             var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).FirstAsync(x => true);
+            var res = Throw<int>(ex).FirstAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
             await AssertThrowsAsync(res, ex);
         }
 
         [Fact]
-        public async Task First8Async()
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Single_None()
+        {
+            var res = Return42.FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().Select((x, ct) => x).FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Single_Pass()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstAsync();
+            var res = Return42.FirstAsync((x, ct) => new ValueTask<bool>(x % 2 == 0), CancellationToken.None);
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task First9Async()
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Many_IList_Pass1()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstAsync(x => x % 2 != 0);
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
             Assert.Equal(45, await res);
         }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Many_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.Select((x, ct) => x).ToAsyncEnumerable().FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select((x, ct) => x).ToAsyncEnumerable().FirstAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstAsync_AsyncPredicateWithCancellation_AsyncPredicateWithCancellationThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().FirstAsync((x, ct) => new ValueTask<bool>(1 / x > 0), CancellationToken.None);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+#endif
     }
 }

+ 254 - 19
Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/FirstOrDefault.cs

@@ -16,59 +16,94 @@ namespace Tests
         public async Task FirstOrDefault_Null()
         {
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, CancellationToken.None));
+
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, x => true));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync(Return42, default(Func<int, bool>)));
 
-            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, x => true, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync(Return42, default(Func<int, bool>), CancellationToken.None));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, x => new ValueTask<bool>(true)));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync(Return42, default(Func<int, ValueTask<bool>>)));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, x => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync(Return42, default(Func<int, ValueTask<bool>>), CancellationToken.None));
+
+#if !NO_DEEP_CANCELLATION
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync<int>(default, (x, ct) => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.FirstOrDefaultAsync(Return42, default(Func<int, CancellationToken, ValueTask<bool>>), CancellationToken.None));
+#endif
         }
 
         [Fact]
-        public async Task FirstOrDefault1Async()
+        public async Task FirstOrDefaultAsync_NoParam_Empty()
         {
             var res = AsyncEnumerable.Empty<int>().FirstOrDefaultAsync();
             Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault2Async()
+        public async Task FirstOrDefaultAsync_NoParam_Empty_Enumerable()
         {
-            var res = AsyncEnumerable.Empty<int>().FirstOrDefaultAsync(x => true);
+            var res = new int[0].Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync();
             Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault3Async()
+        public async Task FirstOrDefaultAsync_NoParam_Empty_IList()
         {
-            var res = Return42.FirstOrDefaultAsync(x => x % 2 != 0);
+            var res = new int[0].ToAsyncEnumerable().FirstOrDefaultAsync();
             Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault4Async()
+        public async Task FirstOrDefaultAsync_NoParam_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).FirstOrDefaultAsync();
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_NoParam_Single_IList()
         {
             var res = Return42.FirstOrDefaultAsync();
             Assert.Equal(42, await res);
         }
 
+
         [Fact]
-        public async Task FirstOrDefault5Async()
+        public async Task FirstOrDefaultAsync_NoParam_Single()
         {
-            var res = Return42.FirstOrDefaultAsync(x => x % 2 == 0);
+            var res = new[] { 42 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync();
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault6Async()
+        public async Task FirstOrDefaultAsync_NoParam_Many_IList()
         {
-            var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).FirstOrDefaultAsync();
-            await AssertThrowsAsync(res, ex);
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync();
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_NoParam_Many()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync();
+            Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault7Async()
+        public async Task FirstOrDefaultAsync_Predicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().FirstOrDefaultAsync(x => true);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_Throw()
         {
             var ex = new Exception("Bang!");
             var res = Throw<int>(ex).FirstOrDefaultAsync(x => true);
@@ -76,24 +111,224 @@ namespace Tests
         }
 
         [Fact]
-        public async Task FirstOrDefault8Async()
+        public async Task FirstOrDefaultAsync_Predicate_Single_None()
+        {
+            var res = Return42.FirstOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_Many_None()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstOrDefaultAsync();
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_Single_Pass()
+        {
+            var res = Return42.FirstOrDefaultAsync(x => x % 2 == 0);
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault9Async()
+        public async Task FirstOrDefaultAsync_Predicate_Many_IList_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_Many_IList_Pass2()
         {
             var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => x % 2 != 0);
             Assert.Equal(45, await res);
         }
 
         [Fact]
-        public async Task FirstOrDefault10Async()
+        public async Task FirstOrDefaultAsync_Predicate_Many_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_Predicate_PredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => 1 / x > 0);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().FirstOrDefaultAsync(x => new ValueTask<bool>(true));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).FirstOrDefaultAsync(x => new ValueTask<bool>(true));
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Single_None()
+        {
+            var res = Return42.FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Single_Pass()
+        {
+            var res = Return42.FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 == 0));
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Many_IList_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Many_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicate_AsyncPredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => new ValueTask<bool>(1 / x > 0));
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+#if !NO_DEEP_CANCELLATION
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Single_None()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstOrDefaultAsync(x => x < 10);
+            var res = Return42.FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
             Assert.Equal(0, await res);
         }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select((x, ct) => x).ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Single_Pass()
+        {
+            var res = Return42.FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 == 0), CancellationToken.None);
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Many_IList_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Many_Pass1()
+        {
+            var res = new[] { 42, 43, 44 }.Select((x, ct) => x).ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(43, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select((x, ct) => x).ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task FirstOrDefaultAsync_AsyncPredicateWithCancellation_AsyncPredicateWithCancellationThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().FirstOrDefaultAsync((x, ct) => new ValueTask<bool>(1 / x > 0), CancellationToken.None);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+#endif
     }
 }

+ 258 - 17
Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/Last.cs

@@ -16,77 +16,318 @@ namespace Tests
         public async Task Last_Null()
         {
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, CancellationToken.None));
+
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, x => true));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync(Return42, default(Func<int, bool>)));
 
-            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, x => true, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync(Return42, default(Func<int, bool>), CancellationToken.None));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, x => new ValueTask<bool>(true)));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync(Return42, default(Func<int, ValueTask<bool>>)));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, x => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync(Return42, default(Func<int, ValueTask<bool>>), CancellationToken.None));
+
+#if !NO_DEEP_CANCELLATION
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync<int>(default, (x, ct) => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastAsync(Return42, default(Func<int, CancellationToken, ValueTask<bool>>), CancellationToken.None));
+#endif
         }
 
         [Fact]
-        public async Task Last1Async()
+        public async Task LastAsync_NoParam_Empty()
         {
             var res = AsyncEnumerable.Empty<int>().LastAsync();
             await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task Last2Async()
+        public async Task LastAsync_NoParam_Empty_Enumerable()
         {
-            var res = AsyncEnumerable.Empty<int>().LastAsync(x => true);
+            var res = new int[0].Select(x => x).ToAsyncEnumerable().LastAsync();
             await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task Last3Async()
+        public async Task LastAsync_NoParam_Empty_IList()
         {
-            var res = Return42.LastAsync(x => x % 2 != 0);
+            var res = new int[0].ToAsyncEnumerable().LastAsync();
             await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task Last4Async()
+        public async Task LastAsync_NoParam_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).LastAsync();
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task LastAsync_NoParam_Single_IList()
         {
             var res = Return42.LastAsync();
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task Last5Async()
+        public async Task LastAsync_NoParam_Single()
+        {
+            var res = new[] { 42 }.Select(x => x).ToAsyncEnumerable().LastAsync();
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_NoParam_Many_IList()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().LastAsync();
+            Assert.Equal(44, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_NoParam_Many()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().LastAsync();
+            Assert.Equal(44, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().LastAsync(x => true);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).LastAsync(x => true);
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Single_None()
+        {
+            var res = Return42.LastAsync(x => x % 2 != 0);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Single_Pass()
         {
             var res = Return42.LastAsync(x => x % 2 == 0);
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task Last6Async()
+        public async Task LastAsync_Predicate_Many_IList_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Many_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.Select(x => x).ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_Predicate_PredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().LastAsync(x => 1 / x > 0);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().LastAsync(x => new ValueTask<bool>(true));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Throw()
         {
             var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).LastAsync();
+            var res = Throw<int>(ex).LastAsync(x => new ValueTask<bool>(true));
             await AssertThrowsAsync(res, ex);
         }
 
         [Fact]
-        public async Task Last7Async()
+        public async Task LastAsync_AsyncPredicate_Single_None()
+        {
+            var res = Return42.LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Single_Pass()
+        {
+            var res = Return42.LastAsync(x => new ValueTask<bool>(x % 2 == 0));
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Many_IList_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Many_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.Select(x => x).ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicate_AsyncPredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().LastAsync(x => new ValueTask<bool>(1 / x > 0));
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+#if !NO_DEEP_CANCELLATION
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().LastAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Throw()
         {
             var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).LastAsync(x => true);
+            var res = Throw<int>(ex).LastAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
             await AssertThrowsAsync(res, ex);
         }
 
         [Fact]
-        public async Task Last8Async()
+        public async Task LastAsync_AsyncPredicateWithCancellation_Single_None()
+        {
+            var res = Return42.LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Many_IList_None()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastAsync();
-            Assert.Equal(90, await res);
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
         }
 
         [Fact]
-        public async Task Last9Async()
+        public async Task LastAsync_AsyncPredicateWithCancellation_Many_None()
         {
-            var res = new[] { 42, 23, 45, 90 }.ToAsyncEnumerable().LastAsync(x => x % 2 != 0);
+            var res = new[] { 40, 42, 44 }.Select((x, ct) => x).ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            await AssertThrowsAsync<InvalidOperationException>(res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Single_Pass()
+        {
+            var res = Return42.LastAsync((x, ct) => new ValueTask<bool>(x % 2 == 0), CancellationToken.None);
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Many_IList_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
             Assert.Equal(45, await res);
         }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Many_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.Select((x, ct) => x).ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select((x, ct) => x).ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastAsync_AsyncPredicateWithCancellation_AsyncPredicateWithCancellationThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().LastAsync((x, ct) => new ValueTask<bool>(1 / x > 0), CancellationToken.None);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+#endif
     }
 }

+ 254 - 20
Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/LastOrDefault.cs

@@ -16,84 +16,318 @@ namespace Tests
         public async Task LastOrDefault_Null()
         {
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, CancellationToken.None));
+
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, x => true));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync(Return42, default(Func<int, bool>)));
 
-            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, x => true, CancellationToken.None));
             await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync(Return42, default(Func<int, bool>), CancellationToken.None));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, x => new ValueTask<bool>(true)));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync(Return42, default(Func<int, ValueTask<bool>>)));
+
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, x => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync(Return42, default(Func<int, ValueTask<bool>>), CancellationToken.None));
+
+#if !NO_DEEP_CANCELLATION
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync<int>(default, (x, ct) => new ValueTask<bool>(true), CancellationToken.None));
+            await Assert.ThrowsAsync<ArgumentNullException>(() => AsyncEnumerable.LastOrDefaultAsync(Return42, default(Func<int, CancellationToken, ValueTask<bool>>), CancellationToken.None));
+#endif
         }
 
         [Fact]
-        public async Task LastOrDefault1Async()
+        public async Task LastOrDefaultAsync_NoParam_Empty()
         {
             var res = AsyncEnumerable.Empty<int>().LastOrDefaultAsync();
             Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault2Async()
+        public async Task LasyOrDefaultAsync_NoParam_Empty_Enumerable()
         {
-            var res = AsyncEnumerable.Empty<int>().LastOrDefaultAsync(x => true);
+            var res = new int[0].Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync();
             Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault3Async()
+        public async Task LastOrDefaultAsync_NoParam_Empty_IList()
         {
-            var res = Return42.LastOrDefaultAsync(x => x % 2 != 0);
+            var res = new int[0].ToAsyncEnumerable().LastOrDefaultAsync();
             Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault4Async()
+        public async Task LastOrDefaultAsync_NoParam_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).LastOrDefaultAsync();
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_NoParam_Single_IList()
         {
             var res = Return42.LastOrDefaultAsync();
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault5Async()
+        public async Task LastOrDefaultAsync_NoParam_Single()
         {
-            var res = Return42.LastOrDefaultAsync(x => x % 2 == 0);
+            var res = new[] { 42 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync();
             Assert.Equal(42, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault6Async()
+        public async Task LastOrDefaultAsync_NoParam_Many_IList()
+        {
+            var res = new[] { 42, 43, 44 }.ToAsyncEnumerable().LastOrDefaultAsync();
+            Assert.Equal(44, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_NoParam_Many()
+        {
+            var res = new[] { 42, 43, 44 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync();
+            Assert.Equal(44, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().LastOrDefaultAsync(x => true);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Throw()
         {
             var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).LastOrDefaultAsync();
+            var res = Throw<int>(ex).LastOrDefaultAsync(x => true);
             await AssertThrowsAsync(res, ex);
         }
 
         [Fact]
-        public async Task LastOrDefault7Async()
+        public async Task LastOrDefaultAsync_Predicate_Single_None()
+        {
+            var res = Return42.LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Single_Pass()
+        {
+            var res = Return42.LastOrDefaultAsync(x => x % 2 == 0);
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Many_IList_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Many_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_Predicate_PredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().LastOrDefaultAsync(x => 1 / x > 0);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().LastOrDefaultAsync(x => new ValueTask<bool>(true));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Throw()
         {
             var ex = new Exception("Bang!");
-            var res = Throw<int>(ex).LastOrDefaultAsync(x => true);
+            var res = Throw<int>(ex).LastOrDefaultAsync(x => new ValueTask<bool>(true));
             await AssertThrowsAsync(res, ex);
         }
 
         [Fact]
-        public async Task LastOrDefault8Async()
+        public async Task LastOrDefaultAsync_AsyncPredicate_Single_None()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastOrDefaultAsync();
-            Assert.Equal(90, await res);
+            var res = Return42.LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(0, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault9Async()
+        public async Task LastOrDefaultAsync_AsyncPredicate_Single_Pass()
         {
-            var res = new[] { 42, 23, 45, 90 }.ToAsyncEnumerable().LastOrDefaultAsync(x => x % 2 != 0);
+            var res = Return42.LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 == 0));
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Many_IList_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
             Assert.Equal(45, await res);
         }
 
         [Fact]
-        public async Task LastOrDefault10Async()
+        public async Task LastOrDefaultAsync_AsyncPredicate_Many_IList_Pass2()
         {
-            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastOrDefaultAsync(x => x < 10);
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Many_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select(x => x).ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(x % 2 != 0));
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicate_AsyncPredicateThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().LastOrDefaultAsync(x => new ValueTask<bool>(1 / x > 0));
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+
+#if !NO_DEEP_CANCELLATION
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Empty()
+        {
+            var res = AsyncEnumerable.Empty<int>().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
             Assert.Equal(0, await res);
         }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Throw()
+        {
+            var ex = new Exception("Bang!");
+            var res = Throw<int>(ex).LastOrDefaultAsync((x, ct) => new ValueTask<bool>(true), CancellationToken.None);
+            await AssertThrowsAsync(res, ex);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Single_None()
+        {
+            var res = Return42.LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Many_IList_None()
+        {
+            var res = new[] { 40, 42, 44 }.ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Many_None()
+        {
+            var res = new[] { 40, 42, 44 }.Select((x, ct) => x).ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(0, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Single_Pass()
+        {
+            var res = Return42.LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 == 0), CancellationToken.None);
+            Assert.Equal(42, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Many_IList_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Many_IList_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Many_Pass1()
+        {
+            var res = new[] { 43, 44, 45 }.Select((x, ct) => x).ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_Many_Pass2()
+        {
+            var res = new[] { 42, 45, 90 }.Select((x, ct) => x).ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(x % 2 != 0), CancellationToken.None);
+            Assert.Equal(45, await res);
+        }
+
+        [Fact]
+        public async Task LastOrDefaultAsync_AsyncPredicateWithCancellation_AsyncPredicateWithCancellationThrows()
+        {
+            var res = new[] { 0, 1, 2 }.ToAsyncEnumerable().LastOrDefaultAsync((x, ct) => new ValueTask<bool>(1 / x > 0), CancellationToken.None);
+            await AssertThrowsAsync<DivideByZeroException>(res);
+        }
+#endif
     }
 }