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