1717
1818import static org .junit .Assert .assertEquals ;
1919import static org .junit .Assert .assertFalse ;
20- import static org .junit .Assert .assertNull ;
2120import static org .junit .Assert .assertTrue ;
2221import static org .junit .Assert .fail ;
2322
2423import java .util .Iterator ;
24+ import java .util .NoSuchElementException ;
2525import java .util .concurrent .ArrayBlockingQueue ;
2626import java .util .concurrent .BlockingQueue ;
2727import java .util .concurrent .CountDownLatch ;
@@ -70,6 +70,7 @@ private static class NextIterator<T> implements Iterator<T> {
7070 private final NextObserver <? extends T > observer ;
7171 private T next ;
7272 private boolean hasNext = true ;
73+ private boolean isNextConsumed = true ;
7374
7475 private NextIterator (NextObserver <? extends T > observer ) {
7576 this .observer = observer ;
@@ -80,23 +81,34 @@ public boolean hasNext() {
8081 // Since an iterator should not be used in different thread,
8182 // so we do not need any synchronization.
8283 if (hasNext == false ) {
84+ // the iterator has reached the end.
8385 return false ;
8486 }
87+ if (isNextConsumed == false ) {
88+ // next has not been used yet.
89+ return true ;
90+ }
91+ return moveToNext ();
92+ }
93+
94+ private boolean moveToNext () {
8595 try {
8696 Notification <? extends T > nextNotification = observer .takeNext ();
8797 if (nextNotification .isOnNext ()) {
98+ isNextConsumed = false ;
8899 next = nextNotification .getValue ();
89100 return true ;
90101 }
91102 // If an observable is completed or fails,
92- // next always return null and hasNext always return false.
93- next = null ;
103+ // hasNext() always return false.
94104 hasNext = false ;
95105 if (nextNotification .isOnCompleted ()) {
96106 return false ;
97107 }
98- // onError
99- throw Exceptions .propagate (nextNotification .getThrowable ());
108+ if (nextNotification .isOnError ()) {
109+ throw Exceptions .propagate (nextNotification .getThrowable ());
110+ }
111+ throw new IllegalStateException ("Should not reach here" );
100112 } catch (InterruptedException e ) {
101113 Thread .currentThread ().interrupt ();
102114 throw Exceptions .propagate (e );
@@ -105,7 +117,13 @@ public boolean hasNext() {
105117
106118 @ Override
107119 public T next () {
108- return next ;
120+ if (hasNext ()) {
121+ isNextConsumed = true ;
122+ return next ;
123+ }
124+ else {
125+ throw new NoSuchElementException ("No more elements" );
126+ }
109127 }
110128
111129 @ Override
@@ -197,11 +215,21 @@ public void testNext() {
197215
198216 obs .onCompleted ();
199217 assertFalse (it .hasNext ());
200- assertNull (it .next ());
218+ try {
219+ it .next ();
220+ fail ("At the end of an iterator should throw a NoSuchElementException" );
221+ }
222+ catch (NoSuchElementException e ){
223+ }
201224
202- // If the observable is completed, hasNext always returns false and next always returns null .
225+ // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException .
203226 assertFalse (it .hasNext ());
204- assertNull (it .next ());
227+ try {
228+ it .next ();
229+ fail ("At the end of an iterator should throw a NoSuchElementException" );
230+ }
231+ catch (NoSuchElementException e ){
232+ }
205233 }
206234
207235 @ Test
@@ -221,9 +249,14 @@ public void testNextWithError() {
221249 // successful
222250 }
223251
224- // After the observable fails, hasNext always returns false and next always returns null .
252+ // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException .
225253 assertFalse (it .hasNext ());
226- assertNull (it .next ());
254+ try {
255+ it .next ();
256+ fail ("At the end of an iterator should throw a NoSuchElementException" );
257+ }
258+ catch (NoSuchElementException e ){
259+ }
227260 }
228261
229262 @ Test
@@ -232,11 +265,21 @@ public void testNextWithEmpty() {
232265 Iterator <String > it = next (obs ).iterator ();
233266
234267 assertFalse (it .hasNext ());
235- assertNull (it .next ());
236-
237- // If the observable is completed, hasNext always returns false and next always returns null.
268+ try {
269+ it .next ();
270+ fail ("At the end of an iterator should throw a NoSuchElementException" );
271+ }
272+ catch (NoSuchElementException e ){
273+ }
274+
275+ // If the observable is completed, hasNext always returns false and next always throw a NoSuchElementException.
238276 assertFalse (it .hasNext ());
239- assertNull (it .next ());
277+ try {
278+ it .next ();
279+ fail ("At the end of an iterator should throw a NoSuchElementException" );
280+ }
281+ catch (NoSuchElementException e ){
282+ }
240283 }
241284
242285 @ Test
@@ -253,9 +296,14 @@ public void testOnError() throws Throwable {
253296 // successful
254297 }
255298
256- // After the observable fails, hasNext always returns false and next always returns null .
299+ // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException .
257300 assertFalse (it .hasNext ());
258- assertNull (it .next ());
301+ try {
302+ it .next ();
303+ fail ("At the end of an iterator should throw a NoSuchElementException" );
304+ }
305+ catch (NoSuchElementException e ){
306+ }
259307 }
260308
261309 @ Test
@@ -273,9 +321,53 @@ public void testOnErrorInNewThread() {
273321 // successful
274322 }
275323
276- // After the observable fails, hasNext always returns false and next always returns null .
324+ // After the observable fails, hasNext always returns false and next always throw a NoSuchElementException .
277325 assertFalse (it .hasNext ());
278- assertNull (it .next ());
326+ try {
327+ it .next ();
328+ fail ("At the end of an iterator should throw a NoSuchElementException" );
329+ }
330+ catch (NoSuchElementException e ){
331+ }
332+ }
333+
334+ @ Test
335+ public void testNextWithOnlyUsingNextMethod () {
336+ Subject <String , String > obs = PublishSubject .create ();
337+ Iterator <String > it = next (obs ).iterator ();
338+ fireOnNextInNewThread (obs , "one" );
339+ assertEquals ("one" , it .next ());
340+
341+ fireOnNextInNewThread (obs , "two" );
342+ assertEquals ("two" , it .next ());
343+
344+ obs .onCompleted ();
345+ try {
346+ it .next ();
347+ fail ("At the end of an iterator should throw a NoSuchElementException" );
348+ }
349+ catch (NoSuchElementException e ){
350+ }
351+ }
352+
353+ @ Test
354+ public void testNextWithCallingHasNextMultipleTimes () {
355+ Subject <String , String > obs = PublishSubject .create ();
356+ Iterator <String > it = next (obs ).iterator ();
357+ fireOnNextInNewThread (obs , "one" );
358+ assertTrue (it .hasNext ());
359+ assertTrue (it .hasNext ());
360+ assertTrue (it .hasNext ());
361+ assertTrue (it .hasNext ());
362+ assertEquals ("one" , it .next ());
363+
364+ obs .onCompleted ();
365+ try {
366+ it .next ();
367+ fail ("At the end of an iterator should throw a NoSuchElementException" );
368+ }
369+ catch (NoSuchElementException e ){
370+ }
279371 }
280372
281373 @ SuppressWarnings ("serial" )
@@ -342,6 +434,8 @@ public void run() {
342434 assertTrue ("expected that c [" + c + "] is higher than or equal to " + COUNT , c >= COUNT );
343435
344436 assertTrue (it .hasNext ());
437+ int d = it .next ();
438+ assertTrue (d > c );
345439
346440 // shut down the thread
347441 running .set (false );
0 commit comments