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.assertNull;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  
30  import java.io.IOException;
31  import java.lang.reflect.Array;
32  import java.util.ArrayList;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Random;
36  import java.util.Set;
37  
38  import org.apache.directory.mavibot.btree.BTree;
39  import org.apache.directory.mavibot.btree.BTreeConfiguration;
40  import org.apache.directory.mavibot.btree.Cursor;
41  import org.apache.directory.mavibot.btree.Leaf;
42  import org.apache.directory.mavibot.btree.MemoryHolder;
43  import org.apache.directory.mavibot.btree.Node;
44  import org.apache.directory.mavibot.btree.Page;
45  import org.apache.directory.mavibot.btree.Tuple;
46  import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
47  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
48  import org.apache.directory.mavibot.btree.serializer.IntSerializer;
49  import org.apache.directory.mavibot.btree.serializer.LongSerializer;
50  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
51  import org.junit.Ignore;
52  import org.junit.Test;
53  
54  
55  /**
56   * A unit test class for in-memory BTree
57   * 
58   * @author <a href="mailto:labs@labs.apache.org">Mavibot labs Project</a>
59   */
60  public class InMemoryBTreeTest
61  {
62      // Some values to inject in a btree
63      private static int[] sortedValues = new int[]
64          {
65              0, 1, 2, 4, 5, 6, 8, 9, 11, 12,
66              13, 14, 16, 19, 21, 22, 23, 25, 26, 28,
67              30, 31, 32, 34, 36, 37, 38, 39, 41, 42,
68              44, 45, 47, 50, 52, 53, 54, 55, 56, 58,
69              59, 60, 63, 64, 67, 68, 70, 72, 73, 74,
70              76, 77, 79, 80, 81, 82, 85, 88, 89, 90,
71              92, 93, 95, 97, 98, 100, 101, 102, 103, 104,
72              105, 106, 107, 109, 110, 111, 112, 117, 118, 120,
73              121, 128, 129, 130, 131, 132, 135, 136, 137, 138,
74              139, 140, 141, 142, 143, 146, 147, 148, 149, 150,
75              152, 154, 156, 160, 161, 162, 163, 165, 167, 168,
76              169, 171, 173, 174, 175, 176, 177, 178, 179, 180,
77              181, 182, 183, 189, 190, 193, 194, 195, 199, 200,
78              202, 203, 205, 206, 207, 208, 209, 210, 212, 215,
79              216, 217, 219, 220, 222, 223, 224, 225, 226, 227,
80              228, 230, 231, 235, 236, 238, 239, 241, 242, 243,
81              245, 246, 247, 249, 250, 251, 252, 254, 256, 257,
82              258, 259, 261, 262, 263, 264, 266, 268, 272, 273,
83              274, 276, 277, 278, 279, 282, 283, 286, 289, 290,
84              292, 293, 294, 296, 298, 299, 300, 301, 303, 305,
85              308, 310, 316, 317, 318, 319, 322, 323, 324, 326,
86              327, 329, 331, 333, 334, 335, 336, 337, 338, 339,
87              340, 341, 346, 347, 348, 349, 350, 351, 352, 353,
88              355, 356, 357, 358, 359, 361, 365, 366, 373, 374,
89              375, 379, 380, 381, 382, 384, 385, 387, 388, 389,
90              390, 392, 393, 395, 396, 397, 398, 399, 400, 401,
91              404, 405, 406, 407, 410, 411, 412, 416, 417, 418,
92              420, 421, 422, 424, 426, 427, 428, 430, 431, 432,
93              433, 436, 439, 441, 443, 444, 445, 446, 447, 448,
94              449, 450, 451, 452, 453, 454, 455, 456, 458, 459,
95              464, 466, 469, 470, 471, 472, 475, 477, 478, 482,
96              483, 484, 485, 486, 488, 490, 491, 492, 493, 495,
97              496, 497, 500, 502, 503, 504, 505, 506, 507, 509,
98              510, 514, 516, 518, 520, 521, 523, 524, 526, 527,
99              528, 529, 530, 532, 533, 535, 538, 539, 540, 542,
100             543, 544, 546, 547, 549, 550, 551, 553, 554, 558,
101             559, 561, 563, 564, 566, 567, 568, 569, 570, 571,
102             572, 576, 577, 578, 580, 582, 583, 586, 588, 589,
103             590, 592, 593, 596, 597, 598, 599, 600, 601, 604,
104             605, 606, 607, 609, 610, 613, 615, 617, 618, 619,
105             620, 621, 626, 627, 628, 631, 632, 633, 635, 636,
106             637, 638, 639, 640, 641, 643, 645, 647, 648, 649,
107             650, 651, 652, 653, 655, 656, 658, 659, 660, 662,
108             666, 669, 673, 674, 675, 676, 677, 678, 680, 681,
109             682, 683, 685, 686, 687, 688, 689, 690, 691, 692,
110             693, 694, 696, 698, 699, 700, 701, 705, 708, 709,
111             711, 713, 714, 715, 719, 720, 723, 725, 726, 727,
112             728, 731, 732, 733, 734, 735, 736, 739, 740, 743,
113             744, 745, 746, 747, 749, 750, 752, 753, 762, 763,
114             765, 766, 768, 770, 772, 773, 774, 776, 777, 779,
115             782, 784, 785, 788, 790, 791, 793, 794, 795, 798,
116             799, 800, 801, 803, 804, 805, 808, 810, 812, 813,
117             814, 816, 818, 821, 822, 823, 824, 827, 828, 829,
118             831, 832, 833, 834, 835, 837, 838, 839, 840, 843,
119             846, 847, 849, 852, 853, 854, 856, 857, 859, 860,
120             863, 864, 865, 866, 867, 868, 869, 872, 873, 877,
121             880, 881, 882, 883, 887, 888, 889, 890, 891, 894,
122             895, 897, 898, 899, 902, 904, 905, 907, 908, 910,
123             911, 912, 915, 916, 917, 918, 919, 923, 925, 926,
124             927, 928, 929, 930, 932, 935, 936, 937, 938, 939,
125             944, 945, 947, 952, 953, 954, 955, 956, 957, 958,
126             960, 967, 970, 971, 972, 974, 975, 976, 978, 979,
127             980, 981, 983, 984, 985, 987, 988, 989, 991, 995
128     };
129 
130 
131     /**
132      * Checks the created BTree contains the expected values
133      */
134     private boolean checkTreeLong( Set<Long> expected, BTree<Long, String> btree ) throws IOException
135     {
136         // We loop on all the expected value to see if they have correctly been inserted
137         // into the btree
138         for ( Long key : expected )
139         {
140             try
141             {
142                 btree.get( key );
143             }
144             catch ( KeyNotFoundException knfe )
145             {
146                 return false;
147             }
148         }
149 
150         return true;
151     }
152 
153 
154     /**
155      * Test the insertion of elements in a BTree. We will try 1000 times to insert 1000
156      * random elements in [0..1024], and check every tree to see if all the added elements
157      * are present. This pretty much validate the the insertion, assuming that due to the
158      * randomization of the injected values, we will statically meet all the use cases.
159      * @throws Exception
160      */
161     @Test
162     public void testPageInsert() throws Exception
163     {
164         Set<Long> expected = new HashSet<Long>();
165         List<Long> added = new ArrayList<Long>();
166 
167         Random random = new Random( System.nanoTime() );
168 
169         int nbError = 0;
170 
171         long l1 = System.currentTimeMillis();
172         int n = 0;
173         long delta = l1;
174         int nbTrees = 1000;
175         int nbElems = 1000;
176 
177         for ( int j = 0; j < nbTrees; j++ )
178         {
179             BTree<Long, String> btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
180             btree.setPageSize( 32 );
181 
182             for ( int i = 0; i < nbElems; i++ )
183             {
184                 Long key = ( long ) random.nextInt( 1024 );
185                 String value = "V" + key;
186                 expected.add( key );
187                 added.add( key );
188 
189                 //System.out.println( "Adding " + i + "th : " + key );
190 
191                 try
192                 {
193                     btree.insert( key, value );
194                 }
195                 catch ( Exception e )
196                 {
197                     e.printStackTrace();
198                     System.out.println( btree );
199                     System.out.println( "Error while adding " + value );
200                     nbError++;
201                     return;
202                 }
203             }
204 
205             assertTrue( checkTreeLong( expected, btree ) );
206 
207             /* For debug only
208             if ( !checkTree( expected, btree ) )
209             {
210                 boolean isFirst = true;
211                 
212                 for ( Long key : added )
213                 {
214                     if ( isFirst )
215                     {
216                         isFirst = false;
217                     }
218                     else
219                     {
220                         System.out.print( ", " );
221                     }
222                     
223                     System.out.print( key );
224                 }
225             }
226             */
227 
228             if ( j % 10000 == 0 )
229             {
230                 if ( n > 0 )
231                 {
232                     long t0 = System.currentTimeMillis();
233                     System.out.println( "Delta" + n + ": " + ( t0 - delta ) );
234                     delta = t0;
235                 }
236 
237                 n++;
238             }
239 
240             expected.clear();
241             added.clear();
242 
243             btree.close();
244         }
245 
246         long l2 = System.currentTimeMillis();
247 
248         System.out.println( "Delta : " + ( l2 - l1 ) + ", nbError = " + nbError
249             + ", Nb insertion per second : " + ( nbTrees * nbElems * 1000 ) / ( l2 - l1 ) );
250     }
251 
252 
253     /**
254      * Test the deletion of elements in a BTree. We will try 1000 times to delete 1000
255      * random elements in [0..1024], and check every tree to see if all the removed elements
256      * are absent. This pretty much validate the the deletion operation is valid, assuming 
257      * that due to the randomization of the deleted values, we will statically meet all the 
258      * use cases.
259      * @throws Exception
260      */
261     @Test
262     public void testPageDeleteRandom() throws IOException
263     {
264         Set<Long> expected = new HashSet<Long>();
265         List<Long> added = new ArrayList<Long>();
266 
267         Random random = new Random( System.nanoTime() );
268 
269         int nbError = 0;
270 
271         long l1 = System.currentTimeMillis();
272         int n = 0;
273         long delta = l1;
274         int nbTrees = 1000;
275         int nbElems = 1000;
276 
277         for ( int j = 0; j < nbTrees; j++ )
278         {
279             BTree<Long, String> btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
280             btree.setPageSize( 8 );
281 
282             for ( int i = 0; i < nbElems; i++ )
283             {
284                 Long key = ( long ) random.nextInt( 1024 );
285                 String value = "V" + key;
286                 expected.add( key );
287                 added.add( key );
288 
289                 //System.out.println( "Adding " + i + "th : " + key );
290 
291                 try
292                 {
293                     btree.insert( key, value );
294                 }
295                 catch ( Exception e )
296                 {
297                     e.printStackTrace();
298                     System.out.println( btree );
299                     System.out.println( "Error while adding " + value );
300                     nbError++;
301                     return;
302                 }
303             }
304 
305             assertTrue( checkTreeLong( expected, btree ) );
306 
307             // Now, delete the elements
308             /*
309             boolean isFirst = true;
310 
311             for ( long element : added )
312             {
313                 if ( isFirst )
314                 {
315                     isFirst = false;
316                 }
317                 else
318                 {
319                     System.out.print( ", " );
320                 }
321 
322                 System.out.print( element );
323             }
324 
325             //System.out.println( "\n--------------------" );
326              */
327 
328             //int i = 0;
329 
330             for ( long element : expected )
331             {
332                 //System.out.println( "Deleting #" + i + " : " + element );
333                 //i++;
334                 //System.out.println( btree );
335                 Tuple<Long, String> tuple = btree.delete( element );
336 
337                 if ( tuple == null )
338                 {
339                     System.out.println( btree );
340                 }
341 
342                 assertEquals( Long.valueOf( element ), tuple.getKey() );
343 
344                 checkNull( btree, element );
345 
346                 //System.out.println( "" );
347             }
348 
349             if ( j % 10000 == 0 )
350             {
351                 if ( n > 0 )
352                 {
353                     long t0 = System.currentTimeMillis();
354                     System.out.println( "Delta" + n + ": " + ( t0 - delta ) );
355                     delta = t0;
356                 }
357 
358                 n++;
359 
360             }
361 
362             expected.clear();
363             added.clear();
364 
365             btree.close();
366         }
367 
368         long l2 = System.currentTimeMillis();
369 
370         System.out.println( "Delta : " + ( l2 - l1 ) + ", nbError = " + nbError
371             + ", Nb deletion per second : " + ( nbTrees * nbElems * 1000 ) / ( l2 - l1 ) );
372     }
373 
374 
375     @Test
376     public void testDeleteDebug() throws IOException
377     {
378         long[] values = new long[]
379             {
380                 148, 746, 525, 327, 1, 705, 171, 1023, 769, 1021,
381                 128, 772, 744, 771, 925, 884, 346, 519, 989, 350,
382                 649, 895, 464, 164, 190, 298, 203, 69, 483, 38,
383                 266, 83, 88, 285, 879, 342, 231, 432, 722, 432,
384                 258, 307, 237, 151, 43, 36, 135, 166, 325, 886,
385                 878, 307, 925, 835, 800, 895, 519, 947, 703, 27,
386                 324, 668, 40, 943, 804, 230, 223, 584, 828, 575,
387                 69, 955, 344, 325, 896, 423, 855, 783, 225, 447,
388                 28, 23, 262, 679, 782, 517, 412, 878, 641, 940,
389                 368, 245, 1005, 226, 939, 320, 396, 437, 373, 61
390         };
391 
392         BTree<Long, String> btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
393         btree.setPageSize( 8 );
394 
395         for ( long value : values )
396         {
397             String strValue = "V" + value;
398 
399             try
400             {
401                 btree.insert( value, strValue );
402             }
403             catch ( Exception e )
404             {
405                 e.printStackTrace();
406                 System.out.println( btree );
407                 System.out.println( "Error while adding " + value );
408                 return;
409             }
410         }
411 
412         int i = 0;
413 
414         long[] deletes = new long[]
415             {
416                 1,
417                 828,
418                 285,
419                 804,
420                 258,
421                 262,
422         };
423 
424         for ( long value : deletes )
425         {
426             //System.out.println( "Deleting #" + i + " : " + value );
427             i++;
428             Tuple<Long, String> tuple = btree.delete( value );
429 
430             if ( tuple != null )
431             {
432                 assertEquals( Long.valueOf( value ), tuple.getKey() );
433             }
434 
435             checkNull( btree, value );
436         }
437 
438         btree.close();
439     }
440 
441 
442     /**
443      * Test the deletion of elements from a BTree.
444      */
445     @Test
446     public void testPageDelete() throws Exception
447     {
448         Set<Long> expected = new HashSet<Long>();
449         List<Long> added = new ArrayList<Long>();
450 
451         Random random = new Random( System.nanoTime() );
452 
453         BTree<Long, String> btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
454         btree.setPageSize( 8 );
455 
456         // Insert some values
457         for ( int i = 0; i < 8; i++ )
458         {
459             Long key = ( long ) random.nextInt( 1024 );
460             String value = "V" + key;
461             added.add( key );
462 
463             try
464             {
465                 btree.insert( key, value );
466             }
467             catch ( Exception e )
468             {
469                 e.printStackTrace();
470                 System.out.println( btree );
471                 System.out.println( "Error while adding " + value );
472                 return;
473             }
474         }
475 
476         assertTrue( checkTreeLong( expected, btree ) );
477 
478         // Now, delete entries
479         for ( long key : added )
480         {
481             //System.out.println( "Removing " + key + " from " + btree );
482             try
483             {
484                 btree.delete( key );
485             }
486             catch ( Exception e )
487             {
488                 e.printStackTrace();
489                 System.out.println( btree );
490                 System.out.println( "Error while deleting " + key );
491                 return;
492             }
493 
494             assertTrue( checkTreeLong( expected, btree ) );
495         }
496 
497         btree.close();
498     }
499 
500 
501     /**
502      * This test is used to debug some corner cases.
503      * We don't run it except to check a special condition
504      */
505     @Test
506     @Ignore
507     public void testPageInsertDebug() throws Exception
508     {
509         BTree<Long, String> btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
510         btree.setPageSize( 4 );
511 
512         Long[] elems = new Long[]
513             {
514                 235L, 135L, 247L, 181L, 12L, 112L, 117L, 253L,
515                 37L, 158L, 56L, 118L, 184L, 101L, 173L, 126L,
516                 61L, 81L, 140L, 173L, 32L, 163L, 224L, 114L,
517                 133L, 18L, 14L, 82L, 107L, 219L, 244L, 255L,
518                 6L, 103L, 170L, 151L, 134L, 196L, 155L, 97L,
519                 80L, 122L, 89L, 253L, 33L, 101L, 56L, 168L,
520                 253L, 187L, 99L, 58L, 151L, 206L, 34L, 96L,
521                 20L, 188L, 143L, 150L, 76L, 111L, 234L, 66L,
522                 12L, 194L, 164L, 190L, 19L, 192L, 161L, 147L,
523                 92L, 89L, 237L, 187L, 250L, 13L, 233L, 34L,
524                 187L, 232L, 248L, 237L, 129L, 1L, 233L, 252L,
525                 18L, 98L, 56L, 121L, 162L, 233L, 29L, 48L,
526                 176L, 48L, 182L, 130L
527         };
528 
529         int size = 0;
530         for ( Long elem : elems )
531         {
532             size++;
533             String value = "V" + elem;
534             btree.insert( elem, value );
535 
536             System.out.println( "Adding " + elem + " :\n" + btree );
537 
538             for ( int i = 0; i < size; i++ )
539             {
540                 try
541                 {
542                     btree.get( elems[i] );
543                 }
544                 catch ( KeyNotFoundException knfe )
545                 {
546                     System.out.println( "Bad tree, missing " + elems[i] + ", " + btree );
547                 }
548             }
549 
550             if ( size == 27 )
551             {
552                 System.out.println( btree );
553             }
554             //System.out.println( "added " + elem + ":\n" + btree );
555         }
556 
557         //System.out.println( btree );
558 
559         btree.close();
560     }
561 
562 
563     /*
564     @Test
565     public void testPageRemove() throws Exception
566     {
567         Long[] keys = new Long[]{  101L, 113L, 20L, 72L, 215L, 239L, 108L, 21L };
568         
569         BTree<Long, String> btree = new BTree<Long, String>( new LongComparator(), 8 );
570         System.out.println( btree );
571 
572         for ( Long key : keys )
573         {
574             btree.insert( key, "V" + key );
575         }
576         
577         System.out.println( btree );
578         
579         // Remove from the left
580         btree.remove( 20L );
581         System.out.println( btree );
582         
583         // Remove from the right
584         btree.remove( 239L );
585         System.out.println( btree );
586         
587         // Remove from the middle
588         btree.remove( 72L );
589         System.out.println( btree );
590         
591         // Remove all the remaining elements
592         btree.remove( 101L );
593         System.out.println( btree );
594         btree.remove( 108L );
595         System.out.println( btree );
596         btree.remove( 215L );
597         System.out.println( btree );
598         btree.remove( 113L );
599         System.out.println( btree );
600         btree.remove( 21L );
601         System.out.println( btree );
602         
603         btree.close();
604     }
605     */
606 
607     /**
608      * Test the browse method going forward
609      * @throws Exception
610      */
611     @Test
612     public void testBrowseForward() throws Exception
613     {
614         // Create a BTree with pages containing 8 elements
615         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
616         btree.setPageSize( 8 );
617 
618         // Inject the values
619         for ( int value : sortedValues )
620         {
621             String strValue = "V" + value;
622 
623             btree.insert( value, strValue );
624         }
625 
626         // Check that the tree contains all the values
627         for ( int key : sortedValues )
628         {
629             String value = btree.get( key );
630 
631             assertNotNull( value );
632         }
633 
634         // Browse starting at position 10
635         int pos = 10;
636         Cursor<Integer, String> cursor = btree.browseFrom( sortedValues[pos] );
637 
638         while ( cursor.hasNext() )
639         {
640             Tuple<Integer, String> tuple = cursor.next();
641 
642             assertNotNull( tuple );
643             Integer val = sortedValues[pos];
644             Integer res = tuple.getKey();
645             assertEquals( val, res );
646             pos++;
647         }
648 
649         cursor.close();
650 
651         // Now, start on a non existing key (7)
652         cursor = btree.browseFrom( 7 );
653 
654         // We should start reading values superior to 7, so value 8 at position 6 in the array
655         pos = 6;
656 
657         while ( cursor.hasNext() )
658         {
659             Tuple<Integer, String> tuple = cursor.next();
660 
661             assertNotNull( tuple );
662             Integer val = sortedValues[pos];
663             Integer res = tuple.getKey();
664             assertEquals( val, res );
665             pos++;
666         }
667 
668         cursor.close();
669 
670         // Last, let's browse with no key, we should get all the values
671         cursor = btree.browse();
672 
673         pos = 0;
674 
675         while ( cursor.hasNext() )
676         {
677             Tuple<Integer, String> tuple = cursor.next();
678 
679             assertNotNull( tuple );
680             Integer val = sortedValues[pos];
681             Integer res = tuple.getKey();
682             assertEquals( val, res );
683             pos++;
684         }
685 
686         cursor.close();
687         btree.close();
688     }
689 
690 
691     /**
692      * Test the browse method going backward
693      * @throws Exception
694      */
695     @Test
696     public void testBrowseBackward() throws Exception
697     {
698         // Create a BTree with pages containing 8 elements
699         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
700         btree.setPageSize( 8 );
701 
702         // Inject the values
703         for ( int value : sortedValues )
704         {
705             String strValue = "V" + value;
706 
707             btree.insert( value, strValue );
708         }
709 
710         // Check that the tree contains all the values
711         for ( int key : sortedValues )
712         {
713             String value = btree.get( key );
714 
715             assertNotNull( value );
716         }
717 
718         // Browse starting at position 10
719         int pos = 10;
720         Cursor<Integer, String> cursor = btree.browseFrom( sortedValues[pos] );
721 
722         while ( cursor.hasPrev() )
723         {
724             Tuple<Integer, String> tuple = cursor.prev();
725 
726             pos--;
727 
728             assertNotNull( tuple );
729             Integer val = sortedValues[pos];
730             Integer res = tuple.getKey();
731             assertEquals( val, res );
732         }
733 
734         cursor.close();
735 
736         // Now, start on a non existing key (7)
737         cursor = btree.browseFrom( 7 );
738 
739         // We should start reading values superior to 7, so value 8 at position 6 in the array
740         pos = 6;
741 
742         while ( cursor.hasPrev() )
743         {
744             Tuple<Integer, String> tuple = cursor.prev();
745 
746             pos--;
747             assertNotNull( tuple );
748             Integer val = sortedValues[pos];
749             Integer res = tuple.getKey();
750             assertEquals( val, res );
751         }
752 
753         cursor.close();
754 
755         // Last, let's browse with no key, we should get no values
756         cursor = btree.browse();
757 
758         pos = 0;
759 
760         assertFalse( cursor.hasPrev() );
761 
762         cursor.close();
763         btree.close();
764     }
765 
766 
767     /**
768      * Test a browse over an empty tree
769      */
770     @Test
771     public void testBrowseEmptyTree() throws Exception
772     {
773         // Create a BTree with pages containing 8 elements
774         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
775         btree.setPageSize( 8 );
776 
777         Cursor<Integer, String> cursor = btree.browse();
778 
779         assertFalse( cursor.hasNext() );
780         assertFalse( cursor.hasPrev() );
781 
782         cursor.close();
783         btree.close();
784     }
785 
786 
787     /**
788      * Test a browse forward and backward
789      */
790     @Test
791     public void testBrowseForwardBackward() throws Exception
792     {
793         // Create a BTree with pages containing 4 elements
794         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
795         btree.setPageSize( 4 );
796 
797         for ( int i = 0; i < 16; i++ )
798         {
799             String strValue = "V" + i;
800             btree.insert( i, strValue );
801         }
802 
803         // Start to browse in the middle
804         Cursor<Integer, String> cursor = btree.browseFrom( 8 );
805 
806         assertTrue( cursor.hasNext() );
807 
808         // Get 8
809         assertEquals( 8, cursor.next().getKey().intValue() );
810 
811         // get 9
812         assertEquals( 9, cursor.next().getKey().intValue() );
813 
814         // get 10
815         assertEquals( 10, cursor.next().getKey().intValue() );
816 
817         // get 11
818         assertEquals( 11, cursor.next().getKey().intValue() );
819 
820         // get 12 (now, we must have gone through at least 2 pages)
821         assertEquals( 12, cursor.next().getKey().intValue() );
822 
823         assertTrue( cursor.hasPrev() );
824 
825         // Lets go backward. We should get the same value, as the next() call have incremented the counter
826         assertEquals( 12, cursor.prev().getKey().intValue() );
827 
828         // Get 11
829         assertEquals( 11, cursor.prev().getKey().intValue() );
830 
831         // Get 10
832         assertEquals( 10, cursor.prev().getKey().intValue() );
833 
834         // Get 9
835         assertEquals( 9, cursor.prev().getKey().intValue() );
836 
837         // Get 8
838         assertEquals( 8, cursor.prev().getKey().intValue() );
839 
840         // Get 7
841         assertEquals( 7, cursor.prev().getKey().intValue() );
842 
843         cursor.close();
844         btree.close();
845     }
846 
847 
848     /**
849      * Test various deletions in a tree, when we have full leaves
850      */
851     @Test
852     public void testDeleteFromFullLeaves() throws Exception
853     {
854         // Create a BTree with pages containing 4 elements
855         BTree<Integer, String> btree = createTwoLevelBTreeFullLeaves();
856 
857         // Test removals leadings to various RemoveResult.
858         // The tree remains the same after the deletion
859         // First, no borrow nor merge
860         btree.delete( 1 );
861 
862         checkNull( btree, 1 );
863 
864         btree.insert( 1, "V1" );
865 
866         btree.delete( 3 );
867 
868         checkNull( btree, 3 );
869 
870         btree.insert( 3, "V3" );
871 
872         btree.delete( 4 );
873 
874         checkNull( btree, 4 );
875 
876         btree.insert( 4, "V4" );
877 
878         btree.delete( 11 );
879 
880         checkNull( btree, 11 );
881 
882         btree.insert( 11, "V11" );
883 
884         btree.delete( 20 );
885 
886         checkNull( btree, 20 );
887 
888         btree.insert( 20, "V20" );
889 
890         btree.delete( 0 );
891 
892         checkNull( btree, 0 );
893 
894         btree.delete( 5 );
895 
896         checkNull( btree, 5 );
897 
898         btree.delete( 9 );
899 
900         checkNull( btree, 9 );
901 
902         btree.close();
903     }
904 
905 
906     /**
907      * Test the exist() method
908      */
909     @Test
910     public void testExist() throws IOException
911     {
912         // Create a BTree with pages containing 4 elements
913         BTree<Integer, String> btree = createTwoLevelBTreeFullLeaves();
914 
915         for ( int i = 1; i < 21; i++ )
916         {
917             assertTrue( btree.hasKey( 5 ) );
918         }
919 
920         assertFalse( btree.hasKey( 0 ) );
921         assertFalse( btree.hasKey( 21 ) );
922     }
923 
924 
925     /**
926      * Test various deletions in a tree, leadings to borrowFromSibling
927      */
928     @Test
929     public void testDeleteBorrowFromSibling() throws Exception
930     {
931         // Create a BTree with pages containing 4 elements
932         BTree<Integer, String> btree = createTwoLevelBTreeFullLeaves();
933 
934         // Delete some useless elements to simulate the tree we want to test
935         // Make the left leaf to contain N/2 elements
936         btree.delete( 3 );
937         btree.delete( 4 );
938 
939         // Make the right leaf to contain N/2 elements
940         btree.delete( 19 );
941         btree.delete( 20 );
942 
943         // Make the middle leaf to contain N/2 elements
944         btree.delete( 11 );
945         btree.delete( 12 );
946 
947         // Delete the leftmost key
948         btree.delete( 1 );
949 
950         checkNull( btree, 1 );
951 
952         // Delete the rightmost key
953         btree.delete( 18 );
954 
955         checkNull( btree, 18 );
956 
957         // Delete one element in the left page, but not the first one
958         btree.delete( 5 );
959 
960         checkNull( btree, 5 );
961 
962         // Delete the one element in the right page, but the first one
963         btree.delete( 16 );
964 
965         checkNull( btree, 16 );
966 
967         btree.close();
968 
969         // Now do that with a deeper btree
970         btree = createMultiLevelBTreeLeavesHalfFull();
971 
972         // Add some more elements on the second leaf before deleting some elements in the first leaf
973         btree.insert( 8, "V8" );
974         btree.insert( 9, "V9" );
975 
976         // and delete some
977         btree.delete( 2 );
978 
979         checkNull( btree, 2 );
980 
981         btree.delete( 6 );
982 
983         checkNull( btree, 6 );
984 
985         // Add some more elements on the pre-last leaf before deleting some elements in the last leaf
986         btree.insert( 96, "V96" );
987         btree.insert( 97, "V97" );
988 
989         // and delete some
990         btree.delete( 98 );
991 
992         checkNull( btree, 98 );
993 
994         btree.delete( 99 );
995 
996         checkNull( btree, 99 );
997 
998         // Now try to delete elements in the middle
999         btree.insert( 48, "V48" );
1000 
1001         btree.delete( 42 );
1002 
1003         checkNull( btree, 42 );
1004 
1005         btree.insert( 72, "V72" );
1006 
1007         btree.delete( 67 );
1008 
1009         checkNull( btree, 67 );
1010 
1011         btree.close();
1012     }
1013 
1014 
1015     /**
1016      * Test the browse method with a non existing key 
1017      * @throws Exception
1018      */
1019     @Test
1020     public void testBrowseNonExistingKey() throws Exception
1021     {
1022         // Create a BTree with pages containing 8 elements
1023         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
1024         btree.setPageSize( 8 );
1025         for ( int i = 0; i < 11; i++ )
1026         {
1027             btree.insert( i, String.valueOf( i ) );
1028         }
1029 
1030         for ( int i = 0; i < 11; i++ )
1031         {
1032             assertNotNull( btree.get( i ) );
1033         }
1034 
1035         assertTrue( btree.hasKey( 8 ) );
1036         assertFalse( btree.hasKey( 11 ) );
1037 
1038         Cursor<Integer, String> cursor = btree.browseFrom( 11 );
1039         assertFalse( cursor.hasNext() );
1040     }
1041 
1042 
1043     private Page<Integer, String> createLeaf( BTree<Integer, String> btree, long revision,
1044         Tuple<Integer, String>... tuples )
1045     {
1046         Leaf<Integer, String> leaf = new Leaf<Integer, String>( btree );
1047         int pos = 0;
1048         leaf.revision = revision;
1049         leaf.nbElems = tuples.length;
1050         leaf.keys = new Integer[leaf.nbElems];
1051         leaf.values = ( MemoryHolder<Integer, String>[] ) Array
1052             .newInstance( MemoryHolder.class, leaf.nbElems );
1053 
1054         for ( Tuple<Integer, String> tuple : tuples )
1055         {
1056             leaf.keys[pos] = tuple.getKey();
1057             leaf.values[pos] = btree.createHolder( tuple.getValue() );
1058             pos++;
1059         }
1060 
1061         return leaf;
1062     }
1063 
1064 
1065     private void addPage( BTree<Integer, String> btree, Node<Integer, String> node, Page<Integer, String> page, int pos )
1066         throws EndOfFileExceededException, IOException
1067     {
1068         Tuple<Integer, String> leftmost = page.findLeftMost();
1069 
1070         if ( pos > 0 )
1071         {
1072             node.keys[pos - 1] = leftmost.getKey();
1073         }
1074 
1075         node.children[pos] = btree.createHolder( page );
1076     }
1077 
1078 
1079     /**
1080      * Creates a 2 level depth tree of full pages
1081      */
1082     private BTree<Integer, String> createTwoLevelBTreeFullLeaves() throws IOException
1083     {
1084         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
1085         btree.setPageSize( 4 );
1086 
1087         // Create a tree with 5 children containing 4 elements each. The tree is full.
1088         int[] keys = new int[]
1089             { 1, 2, 5, 6, 3, 4, 9, 10, 7, 8, 13, 14, 11, 12, 17, 18, 15, 16, 19, 20 };
1090 
1091         for ( int key : keys )
1092         {
1093             String value = "V" + key;
1094             btree.insert( key, value );
1095         }
1096 
1097         return btree;
1098     }
1099 
1100 
1101     /**
1102      * Creates a 2 level depth tree of half full pages
1103      */
1104     private BTree<Integer, String> createTwoLevelBTreeHalfFullLeaves() throws IOException
1105     {
1106         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
1107         btree.setPageSize( 4 );
1108 
1109         // Create a tree with 5 children containing 4 elements each. The tree is full.
1110         int[] keys = new int[]
1111             { 1, 2, 17, 18, 13, 14, 9, 10, 5, 6, 3 };
1112 
1113         for ( int key : keys )
1114         {
1115             String value = "V" + key;
1116             btree.insert( key, value );
1117         }
1118 
1119         // Regulate the tree by removing the last value added, so that all the leaves have only 2 elements
1120         btree.delete( 3 );
1121 
1122         return btree;
1123     }
1124 
1125     /** A set used to check that the tree contains the contained elements */
1126     private Set<Integer> EXPECTED1 = new HashSet<Integer>();
1127 
1128 
1129     /**
1130      * Creates a 3 level depth tree, with each page containing only N/2 elements
1131      */
1132     private BTree<Integer, String> createMultiLevelBTreeLeavesHalfFull() throws IOException
1133     {
1134         // Create a BTree with pages containing 4 elements
1135         int pageSize = 4;
1136 
1137         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer(),
1138             pageSize );
1139 
1140         Node<Integer, String> root = new Node<Integer, String>( btree, 1L, pageSize );
1141 
1142         // Create the tree with 3 levels, all the leaves containing only N/2 elements
1143         int counter = 1;
1144         for ( int i = 0; i < pageSize + 1; i++ )
1145         {
1146             Node<Integer, String> node = new Node<Integer, String>( btree, 1L, pageSize );
1147 
1148             for ( int j = 0; j < pageSize + 1; j++ )
1149             {
1150                 int even = counter * 2;
1151 
1152                 @SuppressWarnings("unchecked")
1153                 Page<Integer, String> leaf = createLeaf(
1154                     btree,
1155                     1L,
1156                     new Tuple<Integer, String>( even, "v" + even ),
1157                     new Tuple<Integer, String>( even + 1, "v" + ( even + 1 ) )
1158                     );
1159 
1160                 counter += 2;
1161 
1162                 addPage( btree, node, leaf, j );
1163 
1164                 EXPECTED1.add( even );
1165                 EXPECTED1.add( even + 1 );
1166             }
1167 
1168             addPage( btree, root, node, i );
1169         }
1170 
1171         btree.setRoot( root );
1172 
1173         return btree;
1174     }
1175 
1176 
1177     /**
1178      * Remove an element from the tree, checking that the removal was successful 
1179      * @param btree The btree on which we remove an element
1180      * @param element The removed element
1181      * @param expected The expected set of elements
1182      */
1183     private void checkRemoval( BTree<Integer, String> btree, int element, Set<Integer> expected ) throws IOException
1184     {
1185         Tuple<Integer, String> removed = btree.delete( element );
1186         assertEquals( element, removed.getKey().intValue() );
1187         assertEquals( "v" + element, removed.getValue() );
1188 
1189         checkNull( btree, element );
1190 
1191         expected.remove( element );
1192         checkTree( btree, expected );
1193     }
1194 
1195 
1196     /**
1197      * Check that the tree contains all the elements in the expected set, and that
1198      * all the elements in the tree are also present in the set
1199      * 
1200      * @param btree The tree to check
1201      * @param expected The set with the expected elements
1202      */
1203     private void checkTree( BTree<Integer, String> btree, Set<Integer> expected )
1204     {
1205         try
1206         {
1207             Cursor<Integer, String> cursor = btree.browse();
1208             Integer value = null;
1209 
1210             while ( cursor.hasNext() )
1211             {
1212                 Tuple<Integer, String> tuple = cursor.next();
1213 
1214                 if ( value == null )
1215                 {
1216                     value = tuple.getKey();
1217                 }
1218                 else
1219                 {
1220                     assertTrue( value < tuple.getKey() );
1221                     value = tuple.getKey();
1222                 }
1223 
1224                 assertTrue( expected.contains( value ) );
1225                 expected.remove( value );
1226             }
1227 
1228             assertEquals( 0, expected.size() );
1229         }
1230         catch ( IOException ioe )
1231         {
1232             fail();
1233         }
1234     }
1235 
1236 
1237     /**
1238      * Remove a set of values from a btree
1239      * 
1240      * @param btree The modified btree
1241      * @param expected The set of expected values to update
1242      * @param values The values to remove
1243      */
1244     private void delete( BTree<Integer, String> btree, Set<Integer> expected, int... values ) throws IOException
1245     {
1246         for ( int value : values )
1247         {
1248             btree.delete( value );
1249             expected.remove( value );
1250         }
1251     }
1252 
1253 
1254     /**
1255      * Test deletions in a tree with more than one level. We are specifically testing
1256      * the deletions that will generate a merge in the leaves.
1257      */
1258     @Test
1259     public void testDeleteMultiLevelsLeadingToLeafMerge() throws Exception
1260     {
1261         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1262 
1263         // Case 1 : delete the leftmost element in the btree in the leftmost leaf
1264         Tuple<Integer, String> removed = btree.delete( 2 );
1265         assertEquals( 2, removed.getKey().intValue() );
1266         assertEquals( "v2", removed.getValue() );
1267         checkNull( btree, 2 );
1268 
1269         // delete the third element in the first leaf
1270         removed = btree.delete( 7 );
1271         assertEquals( 7, removed.getKey().intValue() );
1272         assertEquals( "v7", removed.getValue() );
1273         checkNull( btree, 7 );
1274 
1275         // Case 2 : Delete the second element in the leftmost leaf
1276         removed = btree.delete( 6 );
1277         assertEquals( 6, removed.getKey().intValue() );
1278         assertEquals( "v6", removed.getValue() );
1279         checkNull( btree, 6 );
1280 
1281         // delete the third element in the first leaf
1282         removed = btree.delete( 11 );
1283         assertEquals( 11, removed.getKey().intValue() );
1284         assertEquals( "v11", removed.getValue() );
1285         checkNull( btree, 11 );
1286 
1287         // Case 3 : delete the rightmost element in the btree in the rightmost leaf
1288         removed = btree.delete( 99 );
1289         assertEquals( 99, removed.getKey().intValue() );
1290         assertEquals( "v99", removed.getValue() );
1291         checkNull( btree, 99 );
1292 
1293         // delete the third element in the last leaf
1294         removed = btree.delete( 98 );
1295         assertEquals( 98, removed.getKey().intValue() );
1296         assertEquals( "v98", removed.getValue() );
1297         checkNull( btree, 98 );
1298 
1299         // Case 2 : Delete the first element in the rightmost leaf
1300         removed = btree.delete( 94 );
1301         assertEquals( 94, removed.getKey().intValue() );
1302         assertEquals( "v94", removed.getValue() );
1303         checkNull( btree, 94 );
1304 
1305         // delete the third element in the last leaf
1306         removed = btree.delete( 95 );
1307         assertEquals( 95, removed.getKey().intValue() );
1308         assertEquals( "v95", removed.getValue() );
1309         checkNull( btree, 95 );
1310 
1311         // Case 5 : delete the leftmost element which is referred in the root node
1312         removed = btree.delete( 22 );
1313         assertEquals( 22, removed.getKey().intValue() );
1314         assertEquals( "v22", removed.getValue() );
1315         checkNull( btree, 22 );
1316 
1317         // delete the third element in the last leaf
1318         removed = btree.delete( 27 );
1319         assertEquals( 27, removed.getKey().intValue() );
1320         assertEquals( "v27", removed.getValue() );
1321         checkNull( btree, 27 );
1322 
1323         // Case 6 : delete the leftmost element in a leaf in the middle of the tree
1324         removed = btree.delete( 70 );
1325         assertEquals( 70, removed.getKey().intValue() );
1326         assertEquals( "v70", removed.getValue() );
1327         checkNull( btree, 70 );
1328 
1329         // delete the third element in the leaf
1330         removed = btree.delete( 71 );
1331         assertEquals( 71, removed.getKey().intValue() );
1332         assertEquals( "v71", removed.getValue() );
1333         checkNull( btree, 71 );
1334 
1335         // Case 7 : delete the rightmost element in a leaf in the middle of the tree
1336         removed = btree.delete( 51 );
1337         assertEquals( 51, removed.getKey().intValue() );
1338         assertEquals( "v51", removed.getValue() );
1339         checkNull( btree, 51 );
1340 
1341         // delete the third element in the leaf
1342         removed = btree.delete( 50 );
1343         assertEquals( 50, removed.getKey().intValue() );
1344         assertEquals( "v50", removed.getValue() );
1345         checkNull( btree, 50 );
1346 
1347         btree.close();
1348     }
1349 
1350 
1351     /**
1352      * Test various deletions in a two level high tree, when we have leaves
1353      * containing N/2 elements (thus each deletion leads to a merge)
1354      */
1355     @Test
1356     public void testDelete2LevelsTreeWithHalfFullLeaves() throws Exception
1357     {
1358         // Create a BTree with pages containing 4 elements
1359         BTree<Integer, String> btree = createTwoLevelBTreeHalfFullLeaves();
1360 
1361         // Test removals leadings to various merges.
1362         // Delete from the middle, not the leftmost value of the leaf
1363         btree.delete( 10 );
1364         checkNull( btree, 10 );
1365 
1366         // Delete the extraneous value
1367         btree.delete( 9 );
1368         checkNull( btree, 9 );
1369 
1370         // Delete the leftmost element in the middle
1371         btree.delete( 13 );
1372         checkNull( btree, 13 );
1373 
1374         // Delete the extraneous value
1375         btree.delete( 14 );
1376         checkNull( btree, 14 );
1377 
1378         // Delete the rightmost value
1379         btree.delete( 18 );
1380         checkNull( btree, 18 );
1381 
1382         // Delete the extraneous value
1383         btree.delete( 5 );
1384         checkNull( btree, 5 );
1385 
1386         // Delete the leftmost value of the right leaf
1387         btree.delete( 6 );
1388         checkNull( btree, 6 );
1389 
1390         btree.close();
1391     }
1392 
1393 
1394     /**
1395      * Test deletions in a tree with more than one level. We are specifically testing
1396      * the deletions that will make a node borrowing some element from a sibling.
1397      * 
1398      * 1: remove the leftmost element
1399      */
1400     @Test
1401     public void testDeleteMultiLevelsLeadingToNodeBorrowRight1() throws Exception
1402     {
1403         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1404 
1405         // deleting as many elements as necessary to get the node ready for a merge
1406         delete( btree, EXPECTED1, 2, 3, 6, 7 );
1407 
1408         // delete the element
1409         checkRemoval( btree, 10, EXPECTED1 );
1410 
1411         btree.close();
1412     }
1413 
1414 
1415     /**
1416      * Test deletions in a tree with more than one level. We are specifically testing
1417      * the deletions that will make a node borrowing some element from a sibling.
1418      * 
1419      * 2: remove an element on the leftmost page but not the first one
1420      */
1421     @Test
1422     public void testDeleteMultiLevelsLeadingToNodeBorrowRight2() throws Exception
1423     {
1424         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1425 
1426         // deleting as many elements as necessary to get the node ready for a merge
1427         delete( btree, EXPECTED1, 2, 3, 6, 7 );
1428 
1429         // delete the element
1430         checkRemoval( btree, 11, EXPECTED1 );
1431 
1432         btree.close();
1433     }
1434 
1435 
1436     /**
1437      * Test deletions in a tree with more than one level. We are specifically testing
1438      * the deletions that will make a node borrowing some element from a sibling.
1439      * 
1440      * 3: remove an element on the rightmost page on the leftmost node on the upper level
1441      */
1442     @Test
1443     public void testDeleteMultiLevelsLeadingToNodeBorrowRight3() throws Exception
1444     {
1445         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1446 
1447         // deleting as many elements as necessary to get the node ready for a merge
1448         delete( btree, EXPECTED1, 2, 3, 6, 7 );
1449 
1450         // delete the element
1451         checkRemoval( btree, 19, EXPECTED1 );
1452 
1453         btree.close();
1454     }
1455 
1456 
1457     /**
1458      * Test deletions in a tree with more than one level. We are specifically testing
1459      * the deletions that will make a node borrowing some element from a sibling.
1460      * 
1461      * 4: remove the first element in a page in the middle of the first node
1462      */
1463     @Test
1464     public void testDeleteMultiLevelsLeadingToNodeBorrowRight4() throws Exception
1465     {
1466         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1467 
1468         // deleting as many elements as necessary to get the node ready for a merge
1469         delete( btree, EXPECTED1, 2, 3, 6, 7 );
1470 
1471         // delete the element
1472         checkRemoval( btree, 14, EXPECTED1 );
1473 
1474         btree.close();
1475     }
1476 
1477 
1478     /**
1479      * Test deletions in a tree with more than one level. We are specifically testing
1480      * the deletions that will make a node borrowing some element from a sibling.
1481      * 
1482      * 5: remove the second element in a page in the middle of the first node
1483      */
1484     @Test
1485     public void testDeleteMultiLevelsLeadingToNodeBorrowRight5() throws Exception
1486     {
1487         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1488 
1489         // deleting as many elements as necessary to get the node ready for a merge
1490         delete( btree, EXPECTED1, 2, 3, 6, 7 );
1491 
1492         // delete the element
1493         checkRemoval( btree, 15, EXPECTED1 );
1494 
1495         btree.close();
1496     }
1497 
1498 
1499     /**
1500      * Test deletions in a tree with more than one level. We are specifically testing
1501      * the deletions that will make a node borrowing some element from a sibling.
1502      * 
1503      * 1: remove the rightmost element
1504      */
1505     @Test
1506     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft1() throws Exception
1507     {
1508         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1509 
1510         // deleting as many elements as necessary to get the node ready for a merge
1511         delete( btree, EXPECTED1, 94, 95, 98, 99 );
1512 
1513         // delete the element
1514         checkRemoval( btree, 91, EXPECTED1 );
1515 
1516         btree.close();
1517     }
1518 
1519 
1520     /**
1521      * Test deletions in a tree with more than one level. We are specifically testing
1522      * the deletions that will make a node borrowing some element from a sibling.
1523      * 
1524      * 1: remove the element before the rightmost element
1525      */
1526     @Test
1527     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft2() throws Exception
1528     {
1529         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1530 
1531         // deleting as many elements as necessary to get the node ready for a merge
1532         delete( btree, EXPECTED1, 94, 95, 98, 99 );
1533 
1534         // delete the element
1535         checkRemoval( btree, 90, EXPECTED1 );
1536 
1537         btree.close();
1538     }
1539 
1540 
1541     /**
1542      * Test deletions in a tree with more than one level. We are specifically testing
1543      * the deletions that will make a node borrowing some element from a sibling.
1544      * 
1545      * 1: remove the leftmost element  of the rightmost leaf
1546      */
1547     @Test
1548     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft3() throws Exception
1549     {
1550         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1551 
1552         // deleting as many elements as necessary to get the node ready for a merge
1553         delete( btree, EXPECTED1, 94, 95, 98, 99 );
1554 
1555         // delete the element
1556         checkRemoval( btree, 82, EXPECTED1 );
1557 
1558         btree.close();
1559     }
1560 
1561 
1562     /**
1563      * Test deletions in a tree with more than one level. We are specifically testing
1564      * the deletions that will make a node borrowing some element from a sibling.
1565      * 
1566      * 1: remove the second elemnt of the leftmost page on the rightmost second level node
1567      */
1568     @Test
1569     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft4() throws Exception
1570     {
1571         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1572 
1573         // deleting as many elements as necessary to get the node ready for a merge
1574         delete( btree, EXPECTED1, 94, 95, 98, 99 );
1575 
1576         // delete the element
1577         checkRemoval( btree, 83, EXPECTED1 );
1578 
1579         btree.close();
1580     }
1581 
1582 
1583     /**
1584      * Test deletions in a tree with more than one level. We are specifically testing
1585      * the deletions that will make a node borrowing some element from a sibling.
1586      * 
1587      * 6: remove the first element of a leaf in the middle of the tree
1588      */
1589     @Test
1590     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft6() throws Exception
1591     {
1592         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1593 
1594         // deleting as many elements as necessary to get the node ready for a merge
1595         delete( btree, EXPECTED1, 42, 43, 46, 47 );
1596 
1597         // delete 
1598         checkRemoval( btree, 50, EXPECTED1 );
1599 
1600         btree.close();
1601     }
1602 
1603 
1604     /**
1605      * Test deletions in a tree with more than one level. We are specifically testing
1606      * the deletions that will make a node borrowing some element from a sibling.
1607      * 
1608      * 7: remove the second element of a leaf in the middle of the tree
1609      */
1610     @Test
1611     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft7() throws Exception
1612     {
1613         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1614 
1615         // deleting as many elements as necessary to get the node ready for a merge
1616         delete( btree, EXPECTED1, 42, 43, 46, 47 );
1617 
1618         // delete 
1619         checkRemoval( btree, 51, EXPECTED1 );
1620 
1621         btree.close();
1622     }
1623 
1624 
1625     /**
1626      * Test deletions in a tree with more than one level. We are specifically testing
1627      * the deletions that will make a node borrowing some element from a sibling.
1628      * 
1629      * 8: remove the last element of a leaf in the middle of the tree
1630      */
1631     @Test
1632     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft8() throws Exception
1633     {
1634         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1635 
1636         // deleting as many elements as necessary to get the node ready for a merge
1637         delete( btree, EXPECTED1, 42, 43, 46, 47 );
1638 
1639         // delete 
1640         checkRemoval( btree, 59, EXPECTED1 );
1641 
1642         btree.close();
1643     }
1644 
1645 
1646     /**
1647      * Test deletions in a tree with more than one level. We are specifically testing
1648      * the deletions that will make a node borrowing some element from a sibling.
1649      * 
1650      * 9: remove the element before the last one of a leaf in the middle of the tree
1651      */
1652     @Test
1653     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft9() throws Exception
1654     {
1655         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1656 
1657         // deleting as many elements as necessary to get the node ready for a merge
1658         delete( btree, EXPECTED1, 42, 43, 46, 47 );
1659 
1660         // delete 
1661         checkRemoval( btree, 58, EXPECTED1 );
1662 
1663         btree.close();
1664     }
1665 
1666 
1667     /**
1668      * Test deletions in a tree with more than one level. We are specifically testing
1669      * the deletions that will make a node borrowing some element from a sibling.
1670      * 
1671      * 10: remove the mid element of a leaf in the middle of the tree
1672      */
1673     @Test
1674     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft10() throws Exception
1675     {
1676         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1677 
1678         // deleting as many elements as necessary to get the node ready for a merge
1679         delete( btree, EXPECTED1, 42, 43, 46, 47 );
1680 
1681         // delete 
1682         checkRemoval( btree, 54, EXPECTED1 );
1683 
1684         btree.close();
1685     }
1686 
1687 
1688     /**
1689      * Test deletions in a tree with more than one level. We are specifically testing
1690      * the deletions that will make a node borrowing some element from a sibling.
1691      * 
1692      * 11: remove the mid+1 element of a leaf in the middle of the tree
1693      */
1694     @Test
1695     public void testDeleteMultiLevelsLeadingToNodeBorrowLeft11() throws Exception
1696     {
1697         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1698 
1699         // deleting as many elements as necessary to get the node ready for a merge
1700         delete( btree, EXPECTED1, 42, 43, 46, 47 );
1701 
1702         // delete 
1703         checkRemoval( btree, 55, EXPECTED1 );
1704 
1705         btree.close();
1706     }
1707 
1708 
1709     /**
1710      * Test the addition of elements with null values
1711      */
1712     @Test
1713     public void testAdditionNullValues() throws IOException
1714     {
1715         BTree<Integer, String> btree = createMultiLevelBTreeLeavesHalfFull();
1716 
1717         // Adding an element with a null value
1718         btree.insert( 100, null );
1719 
1720         assertTrue( btree.hasKey( 100 ) );
1721 
1722         try
1723         {
1724             assertNull( btree.get( 100 ) );
1725         }
1726         catch ( KeyNotFoundException knfe )
1727         {
1728             fail();
1729         }
1730 
1731         Tuple<Integer, String> deleted = btree.delete( 100 );
1732 
1733         assertNotNull( deleted );
1734         assertNull( deleted.getValue() );
1735     }
1736 
1737 
1738     /**
1739      * Test the insertion of 5 million elements in a BTree
1740      * @throws Exception
1741      */
1742     @Test
1743     public void testBrowse500K() throws Exception
1744     {
1745         Random random = new Random( System.nanoTime() );
1746 
1747         int nbError = 0;
1748 
1749         int n = 0;
1750         int nbElems = 500000;
1751         long delta = System.currentTimeMillis();
1752 
1753         // Create a BTree with 5 million entries
1754         BTree<Long, String> btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
1755         btree.setPageSize( 32 );
1756 
1757         for ( int i = 0; i < nbElems; i++ )
1758         {
1759             Long key = ( long ) random.nextLong();
1760             String value = Long.toString( key );
1761 
1762             try
1763             {
1764                 btree.insert( key, value );
1765             }
1766             catch ( Exception e )
1767             {
1768                 e.printStackTrace();
1769                 System.out.println( btree );
1770                 System.out.println( "Error while adding " + value );
1771                 nbError++;
1772                 return;
1773             }
1774 
1775             if ( i % 100000 == 0 )
1776             {
1777                 if ( n > 0 )
1778                 {
1779                     long t0 = System.currentTimeMillis();
1780                     System.out.println( "Delta" + n + ": " + ( t0 - delta ) );
1781                     delta = t0;
1782                 }
1783 
1784                 n++;
1785             }
1786         }
1787 
1788         // Now browse them
1789         long l1 = System.currentTimeMillis();
1790 
1791         Cursor<Long, String> cursor = btree.browse();
1792 
1793         int nb = 0;
1794         long elem = Long.MIN_VALUE;
1795 
1796         while ( cursor.hasNext() )
1797         {
1798             Tuple<Long, String> res = cursor.next();
1799 
1800             if ( res.getKey() > elem )
1801             {
1802                 elem = res.getKey();
1803                 nb++;
1804             }
1805         }
1806 
1807         System.out.println( "Nb elements read : " + nb );
1808 
1809         cursor.close();
1810         btree.close();
1811 
1812         long l2 = System.currentTimeMillis();
1813 
1814         System.out.println( "Delta : " + ( l2 - l1 ) + ", nbError = " + nbError
1815             + ", Nb searches per second : " + ( ( nbElems * 1000 ) / ( l2 - l1 ) ) );
1816     }
1817 
1818 
1819     private void checkNull( BTree<Long, String> btree, long key ) throws IOException
1820     {
1821         try
1822         {
1823             btree.get( key );
1824             fail();
1825         }
1826         catch ( KeyNotFoundException knfe )
1827         {
1828             // expected
1829         }
1830     }
1831 
1832 
1833     private void checkNull( BTree<Integer, String> btree, int key ) throws IOException
1834     {
1835         try
1836         {
1837             btree.get( key );
1838             fail();
1839         }
1840         catch ( KeyNotFoundException knfe )
1841         {
1842             // expected
1843         }
1844     }
1845 
1846 
1847     /**
1848      * Test a browse forward and backward
1849      */
1850     @Test
1851     public void testBrowseForwardBackwardExtremes() throws Exception
1852     {
1853         // Create a BTree with pages containing 4 elements
1854         BTree<Integer, String> btree = new BTree<Integer, String>( "test", new IntSerializer(), new StringSerializer() );
1855         btree.setPageSize( 4 );
1856 
1857         for ( int i = 8; i < 13; i++ )
1858         {
1859             String strValue = "V" + i;
1860             btree.insert( i, strValue );
1861         }
1862 
1863         // Start to browse in the middle
1864         Cursor<Integer, String> cursor = btree.browseFrom( 8 );
1865 
1866         assertTrue( cursor.hasNext() );
1867 
1868         // Get 8
1869         assertEquals( 8, cursor.next().getKey().intValue() );
1870 
1871         // get 9
1872         assertEquals( 9, cursor.next().getKey().intValue() );
1873 
1874         // get 10
1875         assertEquals( 10, cursor.next().getKey().intValue() );
1876 
1877         // get 11
1878         assertEquals( 11, cursor.next().getKey().intValue() );
1879 
1880         // get 12 (now, we must have gone through at least 2 pages)
1881         assertEquals( 12, cursor.next().getKey().intValue() );
1882 
1883         assertFalse( cursor.hasNext() );
1884         assertTrue( cursor.hasPrev() );
1885 
1886         // Lets go backward. We should get the same value, as the next() call have incremented the counter
1887         assertEquals( 12, cursor.prev().getKey().intValue() );
1888 
1889         // Get 11
1890         assertEquals( 11, cursor.prev().getKey().intValue() );
1891 
1892         // Get 10
1893         assertEquals( 10, cursor.prev().getKey().intValue() );
1894 
1895         // Get 9
1896         assertEquals( 9, cursor.prev().getKey().intValue() );
1897 
1898         // Get 8
1899         assertEquals( 8, cursor.prev().getKey().intValue() );
1900 
1901         assertFalse( cursor.hasPrev() );
1902         assertTrue( cursor.hasNext() );
1903 
1904         cursor.close();
1905         btree.close();
1906     }
1907 
1908 
1909     @Test
1910     public void testNextAfterPrev() throws Exception
1911     {
1912         IntSerializer serializer = new IntSerializer();
1913 
1914         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
1915         config.setName( "master" );
1916         config.setPageSize( 4 );
1917         config.setSerializers( serializer, serializer );
1918         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
1919 
1920         int i = 7;
1921         for ( int k = 0; k < i; k++ )
1922         {
1923             btree.insert( k, k );
1924         }
1925 
1926         // 3 is the last element of the first leaf
1927         Cursor<Integer, Integer> cursor = btree.browseFrom( 4 );
1928 
1929         assertTrue( cursor.hasNext() );
1930         Tuple<Integer, Integer> tuple = cursor.next();
1931         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
1932         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
1933 
1934         assertTrue( cursor.hasPrev() );
1935         tuple = cursor.prev();
1936         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
1937         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
1938 
1939         assertTrue( cursor.hasNext() );
1940         tuple = cursor.next();
1941         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
1942         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
1943         cursor.close();
1944     }
1945 
1946 }