Skip to content

Commit 1a6f08f

Browse files
committed
CSHARP-1645: fixed issue with empty first batches.
1 parent 8094ff5 commit 1a6f08f

File tree

3 files changed

+301
-29
lines changed

3 files changed

+301
-29
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/* Copyright 2016 MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.Threading;
19+
using System.Threading.Tasks;
20+
using FluentAssertions;
21+
using NSubstitute;
22+
using NUnit.Framework;
23+
24+
namespace MongoDB.Driver.Tests
25+
{
26+
[TestFixture]
27+
public class AsyncCursorHelperTests
28+
{
29+
[Test]
30+
public void AnyAsync_should_return_true_when_a_result_exists()
31+
{
32+
var task = SetupResultInFirstBatch();
33+
34+
var result = AsyncCursorHelper.AnyAsync(task, CancellationToken.None).Result;
35+
36+
result.Should().Be(true);
37+
}
38+
39+
[Test]
40+
public void AnyAsync_should_return_false_when_no_result_exists()
41+
{
42+
var task = SetupNoResultIn1Batch();
43+
44+
var result = AsyncCursorHelper.AnyAsync(task, CancellationToken.None).Result;
45+
46+
result.Should().Be(false);
47+
}
48+
49+
[Test]
50+
public void AnyAsync_should_return_true_when_result_exists_but_is_in_the_second_batch()
51+
{
52+
var task = SetupResultInSecondBatch();
53+
54+
var result = AsyncCursorHelper.AnyAsync(task, CancellationToken.None).Result;
55+
56+
result.Should().Be(true);
57+
}
58+
59+
[Test]
60+
public void AnyAsync_should_return_false_when_no_result_exists_delayed_to_the_second_batch()
61+
{
62+
var task = SetupNoResultInTwoBatches();
63+
64+
var result = AsyncCursorHelper.AnyAsync(task, CancellationToken.None).Result;
65+
66+
result.Should().Be(false);
67+
}
68+
69+
[Test]
70+
public void FirstAsync_should_return_first_result_when_one_exists()
71+
{
72+
var task = SetupResultInFirstBatch();
73+
74+
var result = AsyncCursorHelper.FirstAsync(task, CancellationToken.None).Result;
75+
76+
result.Should().Be(1);
77+
}
78+
79+
[Test]
80+
public void FirstAsync_should_throw_an_exception_when_no_results_exist()
81+
{
82+
var task = SetupNoResultIn1Batch();
83+
84+
Action act = () => AsyncCursorHelper.FirstAsync(task, CancellationToken.None).GetAwaiter().GetResult();
85+
86+
act.ShouldThrow<Exception>();
87+
}
88+
89+
[Test]
90+
public void FirstAsync_should_return_first_result_when_one_exists_but_is_in_the_second_batch()
91+
{
92+
var task = SetupResultInSecondBatch();
93+
94+
var result = AsyncCursorHelper.FirstAsync(task, CancellationToken.None).Result;
95+
96+
result.Should().Be(1);
97+
}
98+
99+
[Test]
100+
public void FirstAsync_should_throw_an_exception_when_no_result_exists_delayed_to_the_second_batch()
101+
{
102+
var task = SetupNoResultInTwoBatches();
103+
104+
Action act = () => AsyncCursorHelper.FirstAsync(task, CancellationToken.None).GetAwaiter().GetResult();
105+
106+
act.ShouldThrow<Exception>();
107+
}
108+
109+
[Test]
110+
public void FirstOrDefaultAsync_should_return_first_result_when_one_exists()
111+
{
112+
var task = SetupResultInFirstBatch();
113+
114+
var result = AsyncCursorHelper.FirstOrDefaultAsync(task, CancellationToken.None).Result;
115+
116+
result.Should().Be(1);
117+
}
118+
119+
[Test]
120+
public void FirstOrDefaultAsync_should_return_default_when_no_results_exist()
121+
{
122+
var task = SetupNoResultIn1Batch();
123+
124+
var result = AsyncCursorHelper.FirstOrDefaultAsync(task, CancellationToken.None).Result;
125+
126+
result.Should().Be(0);
127+
}
128+
129+
[Test]
130+
public void FirstOrDefaultAsync_should_return_first_result_when_one_exists_but_is_in_the_second_batch()
131+
{
132+
var task = SetupResultInSecondBatch();
133+
134+
var result = AsyncCursorHelper.FirstOrDefaultAsync(task, CancellationToken.None).Result;
135+
136+
result.Should().Be(1);
137+
}
138+
139+
[Test]
140+
public void FirstOrDefaultAsync_should_return_default_value_when_no_result_exists_delayed_to_the_second_batch()
141+
{
142+
var task = SetupNoResultInTwoBatches();
143+
144+
var result = AsyncCursorHelper.FirstOrDefaultAsync(task, CancellationToken.None).Result;
145+
146+
result.Should().Be(0);
147+
}
148+
149+
[Test]
150+
public void SingleAsync_should_return_first_result_when_one_exists()
151+
{
152+
var task = SetupResultInFirstBatch();
153+
154+
var result = AsyncCursorHelper.SingleAsync(task, CancellationToken.None).Result;
155+
156+
result.Should().Be(1);
157+
}
158+
159+
[Test]
160+
public void SingleAsync_should_throw_an_exception_when_no_results_exist()
161+
{
162+
var task = SetupNoResultIn1Batch();
163+
164+
Action act = () => AsyncCursorHelper.SingleAsync(task, CancellationToken.None).GetAwaiter().GetResult();
165+
166+
act.ShouldThrow<Exception>();
167+
}
168+
169+
[Test]
170+
public void SingleAsync_should_return_first_result_when_one_exists_but_is_in_the_second_batch()
171+
{
172+
var task = SetupResultInSecondBatch();
173+
174+
var result = AsyncCursorHelper.SingleAsync(task, CancellationToken.None).Result;
175+
176+
result.Should().Be(1);
177+
}
178+
179+
[Test]
180+
public void SingleAsync_should_throw_an_exception_when_no_result_exists_delayed_to_the_second_batch()
181+
{
182+
var task = SetupNoResultInTwoBatches();
183+
184+
Action act = () => AsyncCursorHelper.SingleAsync(task, CancellationToken.None).GetAwaiter().GetResult();
185+
186+
act.ShouldThrow<Exception>();
187+
}
188+
189+
[Test]
190+
public void SingleOrDefaultAsync_should_return_first_result_when_one_exists()
191+
{
192+
var task = SetupResultInFirstBatch();
193+
194+
var result = AsyncCursorHelper.SingleOrDefaultAsync(task, CancellationToken.None).Result;
195+
196+
result.Should().Be(1);
197+
}
198+
199+
[Test]
200+
public void SingleOrDefaultAsync_should_return_default_when_no_results_exist()
201+
{
202+
var task = SetupNoResultIn1Batch();
203+
204+
var result = AsyncCursorHelper.SingleOrDefaultAsync(task, CancellationToken.None).Result;
205+
206+
result.Should().Be(0);
207+
}
208+
209+
[Test]
210+
public void SingleOrDefaultAsync_should_return_first_result_when_one_exists_but_is_in_the_second_batch()
211+
{
212+
var task = SetupResultInSecondBatch();
213+
214+
var result = AsyncCursorHelper.SingleOrDefaultAsync(task, CancellationToken.None).Result;
215+
216+
result.Should().Be(1);
217+
}
218+
219+
[Test]
220+
public void SingleOrDefaultAsync_should_return_default_value_when_no_result_exists_delayed_to_the_second_batch()
221+
{
222+
var task = SetupNoResultInTwoBatches();
223+
224+
var result = AsyncCursorHelper.SingleOrDefaultAsync(task, CancellationToken.None).Result;
225+
226+
result.Should().Be(0);
227+
}
228+
229+
private Task<IAsyncCursor<int>> SetupResultInFirstBatch()
230+
{
231+
var cursor = Substitute.For<IAsyncCursor<int>>();
232+
cursor.MoveNextAsync().ReturnsForAnyArgs(Task.FromResult(true), Task.FromResult(false));
233+
cursor.Current.Returns(new List<int> { 1 }, null);
234+
return Task.FromResult(cursor);
235+
}
236+
237+
private Task<IAsyncCursor<int>> SetupResultInSecondBatch()
238+
{
239+
var cursor = Substitute.For<IAsyncCursor<int>>();
240+
cursor.MoveNextAsync().ReturnsForAnyArgs(Task.FromResult(true), Task.FromResult(true), Task.FromResult(false));
241+
cursor.Current.Returns(new List<int>(), new List<int> { 1 }, null);
242+
return Task.FromResult(cursor);
243+
}
244+
245+
private Task<IAsyncCursor<int>> SetupNoResultIn1Batch()
246+
{
247+
var cursor = Substitute.For<IAsyncCursor<int>>();
248+
cursor.MoveNextAsync().ReturnsForAnyArgs(Task.FromResult(false));
249+
cursor.Current.Returns((IEnumerable<int>)null);
250+
return Task.FromResult(cursor);
251+
}
252+
253+
private Task<IAsyncCursor<int>> SetupNoResultInTwoBatches()
254+
{
255+
var cursor = Substitute.For<IAsyncCursor<int>>();
256+
cursor.MoveNextAsync().ReturnsForAnyArgs(Task.FromResult(true), Task.FromResult(false));
257+
cursor.Current.Returns(new List<int>(), null);
258+
return Task.FromResult(cursor);
259+
}
260+
}
261+
}

src/MongoDB.Driver.Tests/MongoDB.Driver.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<Link>Properties\GlobalAssemblyInfo.cs</Link>
9090
</Compile>
9191
<Compile Include="AggregateFluentTests.cs" />
92+
<Compile Include="AsyncCursorHelperTests.cs" />
9293
<Compile Include="BulkWriteErrorTests.cs" />
9394
<Compile Include="FieldDefinitionTests.cs" />
9495
<Compile Include="FindFluentTests.cs" />

src/MongoDB.Driver/AsyncCursorHelper.cs

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2015 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -14,9 +14,7 @@
1414
*/
1515

1616
using System;
17-
using System.Collections.Generic;
1817
using System.Linq;
19-
using System.Text;
2018
using System.Threading;
2119
using System.Threading.Tasks;
2220

@@ -28,9 +26,13 @@ public async static Task<bool> AnyAsync<T>(Task<IAsyncCursor<T>> cursorTask, Can
2826
{
2927
using (var cursor = await cursorTask.ConfigureAwait(false))
3028
{
31-
if (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
29+
while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
3230
{
33-
return cursor.Current.Any();
31+
var current = cursor.Current;
32+
if (current.Any())
33+
{
34+
return true;
35+
}
3436
}
3537

3638
return false;
@@ -41,59 +43,67 @@ public async static Task<T> FirstAsync<T>(Task<IAsyncCursor<T>> cursorTask, Canc
4143
{
4244
using (var cursor = await cursorTask.ConfigureAwait(false))
4345
{
44-
if (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
46+
while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
4547
{
46-
return cursor.Current.First();
47-
}
48-
else
49-
{
50-
throw new InvalidOperationException("The source sequence is empty.");
48+
var current = cursor.Current;
49+
if (current.Any())
50+
{
51+
return current.First();
52+
}
5153
}
54+
55+
throw new InvalidOperationException("The source sequence is empty.");
5256
}
5357
}
5458

5559
public async static Task<T> FirstOrDefaultAsync<T>(Task<IAsyncCursor<T>> cursorTask, CancellationToken cancellationToken)
5660
{
5761
using (var cursor = await cursorTask.ConfigureAwait(false))
5862
{
59-
if (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
63+
while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
6064
{
61-
return cursor.Current.FirstOrDefault();
62-
}
63-
else
64-
{
65-
return default(T);
65+
var current = cursor.Current;
66+
if (current.Any())
67+
{
68+
return current.FirstOrDefault();
69+
}
6670
}
71+
72+
return default(T);
6773
}
6874
}
6975

7076
public async static Task<T> SingleAsync<T>(Task<IAsyncCursor<T>> cursorTask, CancellationToken cancellationToken)
7177
{
7278
using (var cursor = await cursorTask.ConfigureAwait(false))
7379
{
74-
if (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
80+
while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
7581
{
76-
return cursor.Current.Single();
77-
}
78-
else
79-
{
80-
throw new InvalidOperationException("The source sequence is empty.");
82+
var current = cursor.Current;
83+
if (current.Any())
84+
{
85+
return current.Single();
86+
}
8187
}
88+
89+
throw new InvalidOperationException("The source sequence is empty.");
8290
}
8391
}
8492

8593
public async static Task<T> SingleOrDefaultAsync<T>(Task<IAsyncCursor<T>> cursorTask, CancellationToken cancellationToken)
8694
{
8795
using (var cursor = await cursorTask.ConfigureAwait(false))
8896
{
89-
if (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
97+
while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false))
9098
{
91-
return cursor.Current.SingleOrDefault();
92-
}
93-
else
94-
{
95-
return default(T);
99+
var current = cursor.Current;
100+
if (current.Any())
101+
{
102+
return current.SingleOrDefault();
103+
}
96104
}
105+
106+
return default(T);
97107
}
98108
}
99109
}

0 commit comments

Comments
 (0)