View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.mavibot.btree;
21  
22  
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNotNull;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.io.File;
30  import java.io.IOException;
31  import java.util.NoSuchElementException;
32  import java.util.UUID;
33  
34  import org.apache.commons.io.FileUtils;
35  import org.apache.directory.mavibot.btree.BTree;
36  import org.apache.directory.mavibot.btree.RecordManager;
37  import org.apache.directory.mavibot.btree.Tuple;
38  import org.apache.directory.mavibot.btree.TupleCursor;
39  import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
40  import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
41  import org.apache.directory.mavibot.btree.serializer.LongSerializer;
42  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
43  import org.junit.After;
44  import org.junit.Before;
45  import org.junit.Rule;
46  import org.junit.Test;
47  import org.junit.rules.TemporaryFolder;
48  
49  
50  /**
51   * Tests the browse methods on a managed BTree
52   * 
53   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
54   */
55  public class PersistedBTreeBrowseTest
56  {
57      private BTree<Long, String> btree = null;
58  
59      private RecordManager recordManager1 = null;
60  
61      @Rule
62      public TemporaryFolder tempFolder = new TemporaryFolder();
63  
64      private File dataDir = null;
65  
66  
67      /**
68       * Create a BTree for this test
69       */
70      @Before
71      public void createBTree()
72      {
73          dataDir = tempFolder.newFolder( UUID.randomUUID().toString() );
74  
75          openRecordManagerAndBtree();
76  
77          try
78          {
79              // Create a new BTree which allows duplicate values
80              btree = recordManager1.addBTree( "test", new LongSerializer(), new StringSerializer(), true );
81          }
82          catch ( Exception e )
83          {
84              throw new RuntimeException( e );
85          }
86      }
87  
88  
89      @After
90      public void cleanup() throws IOException
91      {
92          dataDir = new File( System.getProperty( "java.io.tmpdir" ) + "/recordman" );
93  
94          btree.close();
95  
96          if ( dataDir.exists() )
97          {
98              FileUtils.deleteDirectory( dataDir );
99          }
100     }
101 
102 
103     /**
104      * Reload the BTree into a new record manager
105      */
106     private void openRecordManagerAndBtree()
107     {
108         try
109         {
110             if ( recordManager1 != null )
111             {
112                 recordManager1.close();
113             }
114 
115             // Now, try to reload the file back
116             recordManager1 = new RecordManager( dataDir.getAbsolutePath() );
117 
118             // load the last created btree
119             if ( btree != null )
120             {
121                 btree = recordManager1.getManagedTree( btree.getName() );
122             }
123         }
124         catch ( Exception e )
125         {
126             throw new RuntimeException( e );
127         }
128     }
129 
130 
131     /**
132      * Check a tuple
133      */
134     private void checkTuple( Tuple<Long, String> tuple, long key, String value ) throws EndOfFileExceededException,
135         IOException
136     {
137         assertNotNull( tuple );
138         assertEquals( key, ( long ) tuple.getKey() );
139         assertEquals( value, tuple.getValue() );
140     }
141 
142 
143     /**
144      * Check a next() call
145      */
146     private void checkNext( TupleCursor<Long, String> cursor, long key, String value, boolean next, boolean prev )
147         throws EndOfFileExceededException, IOException
148     {
149         Tuple<Long, String> tuple = cursor.next();
150 
151         checkTuple( tuple, key, value );
152         assertEquals( next, cursor.hasNext() );
153         assertEquals( prev, cursor.hasPrev() );
154     }
155 
156 
157     /**
158      * Check a prev() call
159      */
160     private void checkPrev( TupleCursor<Long, String> cursor, long key, String value, boolean next, boolean prev )
161         throws EndOfFileExceededException, IOException
162     {
163         Tuple<Long, String> tuple = cursor.prev();
164         assertNotNull( tuple );
165         assertEquals( key, ( long ) tuple.getKey() );
166         assertEquals( value, tuple.getValue() );
167         assertEquals( next, cursor.hasNext() );
168         assertEquals( prev, cursor.hasPrev() );
169     }
170 
171 
172     /**
173      * Construct a String representation of a number padded with 0 on the left
174      */
175     private String toString( long value, int size )
176     {
177         String valueStr = Long.toString( value );
178 
179         StringBuilder sb = new StringBuilder();
180 
181         if ( size > valueStr.length() )
182         {
183             for ( int i = valueStr.length(); i < size; i++ )
184             {
185                 sb.append( "0" );
186             }
187         }
188 
189         sb.append( valueStr );
190 
191         return sb.toString();
192     }
193 
194 
195     //----------------------------------------------------------------------------------------
196     // The Browse tests
197     //----------------------------------------------------------------------------------------
198     /**
199      * Test the browse methods on an empty btree  
200      */
201     @Test
202     public void testBrowseEmptyBTree() throws IOException, BTreeAlreadyManagedException
203     {
204         TupleCursor<Long, String> cursor = btree.browse();
205 
206         assertFalse( cursor.hasNext() );
207         assertFalse( cursor.hasPrev() );
208 
209         try
210         {
211             cursor.next();
212             fail();
213         }
214         catch ( NoSuchElementException nsee )
215         {
216             // Expected
217         }
218 
219         try
220         {
221             cursor.prev();
222             fail();
223         }
224         catch ( NoSuchElementException nsee )
225         {
226             // Expected
227         }
228 
229         assertEquals( -1L, cursor.getRevision() );
230     }
231 
232 
233     /**
234      * Test the browse methods on a btree containing just a leaf
235      */
236     @Test
237     public void testBrowseBTreeLeafNext() throws IOException, BTreeAlreadyManagedException
238     {
239         // Inject some data
240         btree.insert( 1L, "1" );
241         btree.insert( 4L, "4" );
242         btree.insert( 2L, "2" );
243         btree.insert( 3L, "3" );
244         btree.insert( 5L, "5" );
245 
246         // Create the cursor
247         TupleCursor<Long, String> cursor = btree.browse();
248 
249         // Move forward
250         cursor.beforeFirst();
251 
252         assertFalse( cursor.hasPrev() );
253         assertTrue( cursor.hasNext() );
254 
255         checkNext( cursor, 1L, "1", true, false );
256         checkNext( cursor, 2L, "2", true, true );
257         checkNext( cursor, 3L, "3", true, true );
258         checkNext( cursor, 4L, "4", true, true );
259         checkNext( cursor, 5L, "5", false, true );
260     }
261 
262 
263     /**
264      * Test the browse methods on a btree containing just a leaf
265      */
266     @Test
267     public void testBrowseBTreeLeafPrev() throws IOException, BTreeAlreadyManagedException
268     {
269         // Inject some data
270         btree.insert( 1L, "1" );
271         btree.insert( 4L, "4" );
272         btree.insert( 2L, "2" );
273         btree.insert( 3L, "3" );
274         btree.insert( 5L, "5" );
275 
276         // Create the cursor
277         TupleCursor<Long, String> cursor = btree.browse();
278 
279         // Move backward
280         cursor.afterLast();
281 
282         checkPrev( cursor, 5L, "5", false, true );
283         checkPrev( cursor, 4L, "4", true, true );
284         checkPrev( cursor, 3L, "3", true, true );
285         checkPrev( cursor, 2L, "2", true, true );
286         checkPrev( cursor, 1L, "1", true, false );
287     }
288 
289 
290     /**
291      * Test the browse methods on a btree containing just a leaf and see if we can
292      * move at the end or at the beginning
293      */
294     @Test
295     public void testBrowseBTreeLeafFirstLast() throws IOException, BTreeAlreadyManagedException
296     {
297         // Inject some data
298         btree.insert( 1L, "1" );
299         btree.insert( 4L, "4" );
300         btree.insert( 2L, "2" );
301         btree.insert( 3L, "3" );
302         btree.insert( 5L, "5" );
303 
304         // Create the cursor
305         TupleCursor<Long, String> cursor = btree.browse();
306 
307         // We should not be able to move backward
308         try
309         {
310             cursor.prev();
311             fail();
312         }
313         catch ( NoSuchElementException nsee )
314         {
315             // Expected
316         }
317 
318         // Start browsing three elements
319         assertFalse( cursor.hasPrev() );
320         assertTrue( cursor.hasNext() );
321         Tuple<Long, String> tuple = cursor.next();
322         tuple = cursor.next();
323         tuple = cursor.next();
324 
325         // We should be at 3 now
326         assertTrue( cursor.hasPrev() );
327         assertTrue( cursor.hasNext() );
328         assertEquals( 3L, ( long ) tuple.getKey() );
329         assertEquals( "3", tuple.getValue() );
330 
331         // Move to the end
332         cursor.afterLast();
333 
334         assertTrue( cursor.hasPrev() );
335         assertFalse( cursor.hasNext() );
336 
337         // We should not be able to move forward
338         try
339         {
340             cursor.next();
341             fail();
342         }
343         catch ( NoSuchElementException nsee )
344         {
345             // Expected
346         }
347 
348         // We should be at 5
349         tuple = cursor.prev();
350         assertEquals( 5L, ( long ) tuple.getKey() );
351         assertEquals( "5", tuple.getValue() );
352 
353         assertTrue( cursor.hasPrev() );
354         assertFalse( cursor.hasNext() );
355 
356         // Move back to the origin
357         cursor.beforeFirst();
358 
359         assertFalse( cursor.hasPrev() );
360         assertTrue( cursor.hasNext() );
361 
362         // We should be at 1
363         tuple = cursor.next();
364         assertEquals( 1L, ( long ) tuple.getKey() );
365         assertEquals( "1", tuple.getValue() );
366 
367         assertFalse( cursor.hasPrev() );
368         assertTrue( cursor.hasNext() );
369     }
370 
371 
372     /**
373      * Test the browse methods on a btree containing just a leaf and see if we can
374      * move back and forth
375      */
376     @Test
377     public void testBrowseBTreeLeafNextPrev() throws IOException, BTreeAlreadyManagedException
378     {
379         // Inject some data
380         btree.insert( 1L, "1" );
381         btree.insert( 4L, "4" );
382         btree.insert( 2L, "2" );
383         btree.insert( 3L, "3" );
384         btree.insert( 5L, "5" );
385 
386         // Create the cursor
387         TupleCursor<Long, String> cursor = btree.browse();
388 
389         // We should not be able to move backward
390         try
391         {
392             cursor.prev();
393             fail();
394         }
395         catch ( NoSuchElementException nsee )
396         {
397             // Expected
398         }
399 
400         // Start browsing three elements
401         assertFalse( cursor.hasPrev() );
402         assertTrue( cursor.hasNext() );
403         Tuple<Long, String> tuple = cursor.next();
404         tuple = cursor.next();
405         tuple = cursor.next();
406 
407         // We should be at 3 now
408         assertTrue( cursor.hasPrev() );
409         assertTrue( cursor.hasNext() );
410         assertEquals( 3L, ( long ) tuple.getKey() );
411         assertEquals( "3", tuple.getValue() );
412 
413         // Now, move to the prev value
414         cursor.prev();
415         assertEquals( 2L, ( long ) tuple.getKey() );
416         assertEquals( "2", tuple.getValue() );
417 
418         // And to the next value
419         cursor.next();
420         assertEquals( 3L, ( long ) tuple.getKey() );
421         assertEquals( "3", tuple.getValue() );
422     }
423 
424 
425     /**
426      * Test the browse methods on a btree containing many nodes
427      */
428     @Test
429     public void testBrowseBTreeNodesNext() throws IOException, BTreeAlreadyManagedException
430     {
431         // Inject some data
432         for ( long i = 1; i < 1000L; i++ )
433         {
434             btree.insert( i, Long.toString( i ) );
435         }
436 
437         // Create the cursor
438         TupleCursor<Long, String> cursor = btree.browse();
439 
440         // Move forward
441         cursor.beforeFirst();
442 
443         assertFalse( cursor.hasPrev() );
444         assertTrue( cursor.hasNext() );
445 
446         checkNext( cursor, 1L, "1", true, false );
447 
448         for ( long i = 2L; i < 999L; i++ )
449         {
450             checkNext( cursor, i, Long.toString( i ), true, true );
451         }
452 
453         checkNext( cursor, 999L, "999", false, true );
454     }
455 
456 
457     /**
458      * Test the browse methods on a btree containing many nodes
459      */
460     @Test
461     public void testBrowseBTreeNodesPrev() throws IOException, BTreeAlreadyManagedException
462     {
463         // Inject some data
464         for ( long i = 1; i < 1000L; i++ )
465         {
466             btree.insert( i, Long.toString( i ) );
467         }
468 
469         // Create the cursor
470         TupleCursor<Long, String> cursor = btree.browse();
471 
472         // Move backward
473         cursor.afterLast();
474 
475         assertTrue( cursor.hasPrev() );
476         assertFalse( cursor.hasNext() );
477 
478         checkPrev( cursor, 999L, "999", false, true );
479 
480         for ( long i = 998L; i > 1L; i-- )
481         {
482             checkPrev( cursor, i, Long.toString( i ), true, true );
483         }
484 
485         checkPrev( cursor, 1L, "1", true, false );
486     }
487 
488 
489     /**
490      * Test the browse methods on a btree containing just a leaf with duplicate values
491      */
492     @Test
493     public void testBrowseBTreeLeafNextDups1() throws IOException, BTreeAlreadyManagedException
494     {
495         // Inject some duplicate data
496         btree.insert( 1L, "1" );
497         btree.insert( 1L, "4" );
498         btree.insert( 1L, "2" );
499         btree.insert( 1L, "3" );
500         btree.insert( 1L, "5" );
501 
502         // Create the cursor
503         TupleCursor<Long, String> cursor = btree.browse();
504 
505         // Move forward
506         cursor.beforeFirst();
507 
508         assertFalse( cursor.hasPrev() );
509         assertTrue( cursor.hasNext() );
510 
511         checkNext( cursor, 1L, "1", true, false );
512         checkNext( cursor, 1L, "2", true, true );
513         checkNext( cursor, 1L, "3", true, true );
514         checkNext( cursor, 1L, "4", true, true );
515         checkNext( cursor, 1L, "5", false, true );
516     }
517 
518 
519     /**
520      * Test the browse methods on a btree containing just a leaf with duplicate values
521      */
522     @Test
523     public void testBrowseBTreeLeafNextDupsN() throws IOException, BTreeAlreadyManagedException
524     {
525         // Inject some duplicate data
526         btree.insert( 1L, "1" );
527         btree.insert( 1L, "4" );
528         btree.insert( 1L, "2" );
529         btree.insert( 2L, "3" );
530         btree.insert( 3L, "5" );
531         btree.insert( 3L, "7" );
532         btree.insert( 3L, "6" );
533 
534         // Create the cursor
535         TupleCursor<Long, String> cursor = btree.browse();
536 
537         // Move forward
538         cursor.beforeFirst();
539 
540         assertFalse( cursor.hasPrev() );
541         assertTrue( cursor.hasNext() );
542 
543         checkNext( cursor, 1L, "1", true, false );
544         checkNext( cursor, 1L, "2", true, true );
545         checkNext( cursor, 1L, "4", true, true );
546         checkNext( cursor, 2L, "3", true, true );
547         checkNext( cursor, 3L, "5", true, true );
548         checkNext( cursor, 3L, "6", true, true );
549         checkNext( cursor, 3L, "7", false, true );
550     }
551 
552 
553     /**
554      * Test the browse methods on a btree containing just a leaf with duplicate values
555      */
556     @Test
557     public void testBrowseBTreeLeafPrevDups1() throws IOException, BTreeAlreadyManagedException
558     {
559         // Inject some duplicate data
560         btree.insert( 1L, "1" );
561         btree.insert( 1L, "4" );
562         btree.insert( 1L, "2" );
563         btree.insert( 1L, "3" );
564         btree.insert( 1L, "5" );
565 
566         // Create the cursor
567         TupleCursor<Long, String> cursor = btree.browse();
568 
569         // Move backward
570         cursor.afterLast();
571 
572         assertTrue( cursor.hasPrev() );
573         assertFalse( cursor.hasNext() );
574 
575         checkPrev( cursor, 1L, "5", false, true );
576         checkPrev( cursor, 1L, "4", true, true );
577         checkPrev( cursor, 1L, "3", true, true );
578         checkPrev( cursor, 1L, "2", true, true );
579         checkPrev( cursor, 1L, "1", true, false );
580     }
581 
582 
583     /**
584      * Test the browse methods on a btree containing just a leaf with duplicate values
585      */
586     @Test
587     public void testBrowseBTreeLeafPrevDupsN() throws IOException, BTreeAlreadyManagedException
588     {
589         // Inject some duplicate data
590         btree.insert( 1L, "1" );
591         btree.insert( 1L, "4" );
592         btree.insert( 1L, "2" );
593         btree.insert( 2L, "3" );
594         btree.insert( 3L, "5" );
595         btree.insert( 3L, "7" );
596         btree.insert( 3L, "6" );
597 
598         // Create the cursor
599         TupleCursor<Long, String> cursor = btree.browse();
600 
601         // Move backward
602         cursor.afterLast();
603 
604         assertTrue( cursor.hasPrev() );
605         assertFalse( cursor.hasNext() );
606 
607         checkPrev( cursor, 3L, "7", false, true );
608         checkPrev( cursor, 3L, "6", true, true );
609         checkPrev( cursor, 3L, "5", true, true );
610         checkPrev( cursor, 2L, "3", true, true );
611         checkPrev( cursor, 1L, "4", true, true );
612         checkPrev( cursor, 1L, "2", true, true );
613         checkPrev( cursor, 1L, "1", true, false );
614     }
615 
616 
617     /**
618      * Test the browse methods on a btree containing nodes with duplicate values
619      */
620     @Test
621     public void testBrowseBTreeNodesNextDupsN() throws IOException, BTreeAlreadyManagedException
622     {
623         // Inject some data
624         for ( long i = 1; i < 1000L; i++ )
625         {
626             for ( long j = 1; j < 10; j++ )
627             {
628                 btree.insert( i, Long.toString( j ) );
629             }
630         }
631 
632         // Create the cursor
633         TupleCursor<Long, String> cursor = btree.browse();
634 
635         // Move backward
636         cursor.beforeFirst();
637 
638         assertFalse( cursor.hasPrev() );
639         assertTrue( cursor.hasNext() );
640         boolean next = true;
641         boolean prev = false;
642 
643         for ( long i = 1L; i < 1000L; i++ )
644         {
645             for ( long j = 1L; j < 10L; j++ )
646             {
647                 checkNext( cursor, i, Long.toString( j ), next, prev );
648 
649                 if ( ( i == 1L ) && ( j == 1L ) )
650                 {
651                     prev = true;
652                 }
653 
654                 if ( ( i == 999L ) && ( j == 8L ) )
655                 {
656                     next = false;
657                 }
658             }
659         }
660     }
661 
662 
663     /**
664      * Test the browse methods on a btree containing nodes with duplicate values
665      */
666     @Test
667     public void testBrowseBTreeNodesPrevDupsN() throws IOException, BTreeAlreadyManagedException
668     {
669         // Inject some data
670         for ( long i = 1; i < 1000L; i++ )
671         {
672             for ( int j = 1; j < 10; j++ )
673             {
674                 btree.insert( i, Long.toString( j ) );
675             }
676         }
677 
678         // Create the cursor
679         TupleCursor<Long, String> cursor = btree.browse();
680 
681         // Move backward
682         cursor.afterLast();
683 
684         assertTrue( cursor.hasPrev() );
685         assertFalse( cursor.hasNext() );
686         boolean next = false;
687         boolean prev = true;
688 
689         for ( long i = 999L; i > 0L; i-- )
690         {
691             for ( long j = 9L; j > 0L; j-- )
692             {
693                 checkPrev( cursor, i, Long.toString( j ), next, prev );
694 
695                 if ( ( i == 1L ) && ( j == 2L ) )
696                 {
697                     prev = false;
698                 }
699 
700                 if ( ( i == 999L ) && ( j == 9L ) )
701                 {
702                     next = true;
703                 }
704             }
705         }
706     }
707 
708 
709     /**
710      * Test the browse methods on a btree containing just a leaf with duplicate values
711      * stored into a sub btree
712      */
713     @Test
714     public void testBrowseBTreeLeafNextDupsSubBTree1() throws IOException, BTreeAlreadyManagedException
715     {
716         // Inject some duplicate data which will be stored into a sub btree
717         for ( long i = 1L; i < 32L; i++ )
718         {
719             btree.insert( 1L, toString( i, 2 ) );
720         }
721 
722         // Create the cursor
723         TupleCursor<Long, String> cursor = btree.browse();
724 
725         // Move forward
726         cursor.beforeFirst();
727 
728         assertFalse( cursor.hasPrev() );
729         assertTrue( cursor.hasNext() );
730 
731         checkNext( cursor, 1L, "01", true, false );
732 
733         for ( long i = 2L; i < 31L; i++ )
734         {
735             checkNext( cursor, 1L, toString( i, 2 ), true, true );
736         }
737 
738         checkNext( cursor, 1L, "31", false, true );
739     }
740 
741 
742     /**
743      * Test the browse methods on a btree containing just a leaf with duplicate values
744      */
745     @Test
746     public void testBrowseBTreeLeafPrevDupsSubBTree1() throws IOException, BTreeAlreadyManagedException
747     {
748         // Inject some duplicate data which will be stored into a sub btree
749         for ( long i = 1L; i < 32L; i++ )
750         {
751             btree.insert( 1L, toString( i, 2 ) );
752         }
753 
754         // Create the cursor
755         TupleCursor<Long, String> cursor = btree.browse();
756 
757         // Move backward
758         cursor.afterLast();
759 
760         assertTrue( cursor.hasPrev() );
761         assertFalse( cursor.hasNext() );
762 
763         checkPrev( cursor, 1L, "31", false, true );
764 
765         for ( long i = 30L; i > 1L; i-- )
766         {
767             checkPrev( cursor, 1L, toString( i, 2 ), true, true );
768         }
769 
770         checkPrev( cursor, 1L, "01", true, false );
771     }
772 
773 
774     //----------------------------------------------------------------------------------------
775     // The BrowseFrom tests
776     //----------------------------------------------------------------------------------------
777     /**
778      * Test the browseFrom method on an empty tree
779      */
780     @Test
781     public void testBrowseFromEmptyBTree() throws IOException, BTreeAlreadyManagedException
782     {
783         TupleCursor<Long, String> cursor = btree.browseFrom( 1L );
784 
785         assertFalse( cursor.hasNext() );
786         assertFalse( cursor.hasPrev() );
787 
788         try
789         {
790             cursor.next();
791             fail();
792         }
793         catch ( NoSuchElementException nsee )
794         {
795             // Expected
796         }
797 
798         try
799         {
800             cursor.prev();
801             fail();
802         }
803         catch ( NoSuchElementException nsee )
804         {
805             // Expected
806         }
807 
808         assertEquals( -1L, cursor.getRevision() );
809     }
810 
811 
812     /**
813      * Test the browseFrom methods on a btree containing just a leaf
814      */
815     @Test
816     public void testBrowseFromBTreeLeaf() throws IOException, BTreeAlreadyManagedException
817     {
818         // Inject some data
819         btree.insert( 1L, "1" );
820         btree.insert( 7L, "7" );
821         btree.insert( 3L, "3" );
822         btree.insert( 5L, "5" );
823         btree.insert( 9L, "9" );
824 
825         // Create the cursor, starting at 5
826         TupleCursor<Long, String> cursor = btree.browseFrom( 5L );
827 
828         assertTrue( cursor.hasPrev() );
829         assertTrue( cursor.hasNext() );
830 
831         // Move forward
832         checkNext( cursor, 5L, "5", true, true );
833         checkNext( cursor, 7L, "7", true, true );
834         checkNext( cursor, 9L, "9", false, true );
835 
836         cursor.close();
837 
838         // now, start at 5 and move backward
839         cursor = btree.browseFrom( 5L );
840 
841         assertTrue( cursor.hasPrev() );
842         assertTrue( cursor.hasNext() );
843 
844         // Move backward
845         checkPrev( cursor, 3L, "3", true, true );
846         checkPrev( cursor, 1L, "1", true, false );
847         cursor.close();
848 
849         // Start at the first key
850         cursor = btree.browseFrom( 1L );
851         assertFalse( cursor.hasPrev() );
852         assertTrue( cursor.hasNext() );
853 
854         checkNext( cursor, 1L, "1", true, false );
855         checkNext( cursor, 3L, "3", true, true );
856 
857         // Start before the first key
858         cursor = btree.browseFrom( 0L );
859         assertFalse( cursor.hasPrev() );
860         assertTrue( cursor.hasNext() );
861 
862         checkNext( cursor, 1L, "1", true, false );
863         checkNext( cursor, 3L, "3", true, true );
864 
865         // Start at the last key
866         cursor = btree.browseFrom( 9L );
867         assertTrue( cursor.hasPrev() );
868         assertTrue( cursor.hasNext() );
869 
870         checkNext( cursor, 9L, "9", false, true );
871         checkPrev( cursor, 7L, "7", true, true );
872 
873         // Start after the last key
874         cursor = btree.browseFrom( 10L );
875         assertTrue( cursor.hasPrev() );
876         assertFalse( cursor.hasNext() );
877 
878         checkPrev( cursor, 9L, "9", false, true );
879         checkPrev( cursor, 7L, "7", true, true );
880 
881         // Start in the middle with a non existent key
882         cursor = btree.browseFrom( 4L );
883         assertTrue( cursor.hasPrev() );
884         assertTrue( cursor.hasNext() );
885 
886         checkNext( cursor, 5L, "5", true, true );
887 
888         // Start in the middle with a non existent key
889         cursor = btree.browseFrom( 4L );
890 
891         checkPrev( cursor, 3L, "3", true, true );
892     }
893 
894 
895     /**
896      * Test the browseFrom method on a btree containing nodes with duplicate values
897      */
898     @Test
899     public void testBrowseFromBTreeNodesPrevDupsN() throws IOException, BTreeAlreadyManagedException
900     {
901         // Inject some data
902         for ( long i = 1; i < 1000L; i += 2 )
903         {
904             for ( int j = 1; j < 10; j++ )
905             {
906                 btree.insert( i, Long.toString( j ) );
907             }
908         }
909 
910         // Create the cursor
911         TupleCursor<Long, String> cursor = btree.browseFrom( 500L );
912 
913         // Move forward
914 
915         assertTrue( cursor.hasPrev() );
916         assertTrue( cursor.hasNext() );
917         boolean next = true;
918         boolean prev = true;
919 
920         for ( long i = 501L; i < 1000L; i += 2 )
921         {
922             for ( long j = 1L; j < 10L; j++ )
923             {
924                 if ( ( i == 999L ) && ( j == 9L ) )
925                 {
926                     next = false;
927                 }
928 
929                 checkNext( cursor, i, Long.toString( j ), next, prev );
930             }
931         }
932     }
933 
934 
935     //----------------------------------------------------------------------------------------
936     // The TupleCursor.moveToNext/PrevNonDuplicateKey method tests
937     //----------------------------------------------------------------------------------------
938     /**
939       * Test the TupleCursor.nextKey method on a btree containing nodes 
940       * with duplicate values.
941       */
942     @Test
943     public void testNextKey() throws IOException, BTreeAlreadyManagedException
944     {
945         // Inject some data
946         for ( long i = 1; i < 1000L; i++ )
947         {
948             for ( long j = 1; j < 10; j++ )
949             {
950                 btree.insert( i, Long.toString( j ) );
951             }
952         }
953 
954         // Create the cursor
955         TupleCursor<Long, String> cursor = btree.browse();
956 
957         // Move forward
958         cursor.beforeFirst();
959 
960         assertFalse( cursor.hasPrev() );
961         assertTrue( cursor.hasNext() );
962         boolean next = true;
963         boolean prev = false;
964 
965         for ( long i = 1L; i < 999L; i++ )
966         {
967             Tuple<Long, String> tuple = cursor.nextKey();
968 
969             checkTuple( tuple, i, "1" );
970 
971             if ( i == 999L )
972             {
973                 next = false;
974             }
975 
976             assertEquals( next, cursor.hasNext() );
977             assertEquals( prev, cursor.hasPrev() );
978 
979             if ( i == 1L )
980             {
981                 prev = true;
982             }
983         }
984     }
985 
986 
987     /**
988      * Test the TupleCursor.moveToPrevNonDuplicateKey method on a btree containing nodes 
989      * with duplicate values.
990      */
991     @Test
992     public void testPrevKey() throws IOException, BTreeAlreadyManagedException
993     {
994         // Inject some data
995         for ( long i = 1; i < 1000L; i++ )
996         {
997             for ( long j = 1; j < 10; j++ )
998             {
999                 btree.insert( i, Long.toString( j ) );
1000             }
1001         }
1002 
1003         // Create the cursor
1004         TupleCursor<Long, String> cursor = btree.browse();
1005 
1006         // Move backward
1007         cursor.afterLast();
1008 
1009         assertTrue( cursor.hasPrev() );
1010         assertFalse( cursor.hasNext() );
1011         boolean next = true;
1012         boolean prev = true;
1013 
1014         for ( long i = 999L; i > 0L; i-- )
1015         {
1016             Tuple<Long, String> tuple = cursor.prevKey();
1017 
1018             if ( i == 1L )
1019             {
1020                 prev = false;
1021             }
1022 
1023             checkTuple( tuple, i, "1" );
1024             assertEquals( next, cursor.hasNext() );
1025             assertEquals( prev, cursor.hasPrev() );
1026 
1027             if ( i == 999L )
1028             {
1029                 next = true;
1030             }
1031         }
1032     }
1033 }