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.util.NoSuchElementException;
32  import java.util.UUID;
33  
34  import org.apache.directory.mavibot.btree.BTree;
35  import org.apache.directory.mavibot.btree.BTreeConfiguration;
36  import org.apache.directory.mavibot.btree.Cursor;
37  import org.apache.directory.mavibot.btree.Tuple;
38  import org.apache.directory.mavibot.btree.serializer.IntSerializer;
39  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
40  import org.junit.Test;
41  
42  
43  /**
44   * TODO BTreeDuplicateKeyTest.
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   */
48  public class BTreeDuplicateKeyTest
49  {
50      @Test
51      public void testInsertNullValue() throws IOException
52      {
53          IntSerializer serializer = new IntSerializer();
54  
55          BTree<Integer, Integer> btree = new BTree<Integer, Integer>( "master", serializer, serializer );
56          btree.init();
57  
58          btree.insert( 1, null );
59  
60          Cursor<Integer, Integer> cursor = btree.browse();
61          assertTrue( cursor.hasNext() );
62  
63          Tuple<Integer, Integer> t = cursor.next();
64  
65          assertEquals( Integer.valueOf( 1 ), t.getKey() );
66          assertEquals( null, t.getValue() );
67  
68          cursor.close();
69      }
70  
71  
72      @Test
73      public void testBrowseEmptyTree() throws IOException
74      {
75          IntSerializer serializer = new IntSerializer();
76  
77          BTree<Integer, Integer> btree = new BTree<Integer, Integer>( "master", serializer, serializer );
78          btree.init();
79  
80          Cursor<Integer, Integer> cursor = btree.browse();
81          assertFalse( cursor.hasNext() );
82          assertFalse( cursor.hasPrev() );
83  
84          try
85          {
86              cursor.next();
87              fail( "Should not reach here" );
88          }
89          catch ( NoSuchElementException e )
90          {
91              assertTrue( true );
92          }
93  
94          try
95          {
96              cursor.prev();
97              fail( "Should not reach here" );
98          }
99          catch ( NoSuchElementException e )
100         {
101             assertTrue( true );
102         }
103 
104         cursor.close();
105     }
106 
107 
108     @Test
109     public void testDuplicateKey() throws IOException
110     {
111         IntSerializer serializer = new IntSerializer();
112 
113         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
114         config.setAllowDuplicates( true );
115         config.setName( "master" );
116         config.setSerializers( serializer, serializer );
117         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
118 
119         btree.insert( 1, 1 );
120         btree.insert( 1, 2 );
121 
122         Cursor<Integer, Integer> cursor = btree.browse();
123         assertTrue( cursor.hasNext() );
124 
125         Tuple<Integer, Integer> t = cursor.next();
126 
127         assertEquals( Integer.valueOf( 1 ), t.getKey() );
128         assertEquals( Integer.valueOf( 1 ), t.getValue() );
129 
130         assertTrue( cursor.hasNext() );
131 
132         t = cursor.next();
133 
134         assertEquals( Integer.valueOf( 1 ), t.getKey() );
135         assertEquals( Integer.valueOf( 2 ), t.getValue() );
136 
137         assertFalse( cursor.hasNext() );
138 
139         // test backward move
140         assertTrue( cursor.hasPrev() );
141 
142         t = cursor.prev();
143 
144         assertEquals( Integer.valueOf( 1 ), t.getKey() );
145         assertEquals( Integer.valueOf( 2 ), t.getValue() );
146 
147         assertTrue( cursor.hasPrev() );
148 
149         t = cursor.prev();
150 
151         assertEquals( Integer.valueOf( 1 ), t.getKey() );
152         assertEquals( Integer.valueOf( 1 ), t.getValue() );
153 
154         assertFalse( cursor.hasPrev() );
155 
156         // again forward
157         assertTrue( cursor.hasNext() );
158 
159         t = cursor.next();
160 
161         assertEquals( Integer.valueOf( 1 ), t.getKey() );
162         assertEquals( Integer.valueOf( 1 ), t.getValue() );
163 
164         assertTrue( cursor.hasNext() );
165 
166         t = cursor.next();
167 
168         assertEquals( Integer.valueOf( 1 ), t.getKey() );
169         assertEquals( Integer.valueOf( 2 ), t.getValue() );
170 
171         assertFalse( cursor.hasNext() );
172 
173         cursor.close();
174     }
175 
176 
177     @Test
178     public void testGetDuplicateKey() throws Exception
179     {
180         IntSerializer serializer = new IntSerializer();
181 
182         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
183         config.setAllowDuplicates( true );
184         config.setName( "master" );
185         config.setSerializers( serializer, serializer );
186         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
187 
188         Integer retVal = btree.insert( 1, 1 );
189         assertNull( retVal );
190 
191         retVal = btree.insert( 1, 2 );
192         assertNull( retVal );
193 
194         // check the return value when an existing value is added again
195         retVal = btree.insert( 1, 2 );
196         assertEquals( Integer.valueOf( 2 ), retVal );
197 
198         assertEquals( Integer.valueOf( 1 ), btree.get( 1 ) );
199         assertTrue( btree.contains( 1, 1 ) );
200         assertTrue( btree.contains( 1, 2 ) );
201 
202         assertFalse( btree.contains( 1, 0 ) );
203         assertFalse( btree.contains( 0, 1 ) );
204         assertFalse( btree.contains( 0, 0 ) );
205         assertFalse( btree.contains( null, 0 ) );
206         assertFalse( btree.contains( 0, null ) );
207         assertFalse( btree.contains( null, null ) );
208     }
209 
210 
211     @Test
212     public void testRemoveDuplicateKey() throws Exception
213     {
214         IntSerializer serializer = new IntSerializer();
215 
216         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
217         config.setAllowDuplicates( true );
218         config.setName( "master" );
219         config.setSerializers( serializer, serializer );
220         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
221 
222         btree.insert( 1, 1 );
223         btree.insert( 1, 2 );
224 
225         assertEquals( 2l, btree.getNbElems() );
226 
227         Tuple<Integer, Integer> t = btree.delete( 1, 1 );
228         assertEquals( Integer.valueOf( 1 ), t.getKey() );
229         assertEquals( Integer.valueOf( 1 ), t.getValue() );
230 
231         assertEquals( 1l, btree.getNbElems() );
232 
233         t = btree.delete( 1, 2 );
234         assertEquals( Integer.valueOf( 1 ), t.getKey() );
235         assertEquals( Integer.valueOf( 2 ), t.getValue() );
236 
237         assertEquals( 0l, btree.getNbElems() );
238 
239         t = btree.delete( 1, 2 );
240         assertNull( t );
241     }
242 
243 
244     @Test
245     public void testFullPage() throws Exception
246     {
247         StringSerializer serializer = new StringSerializer();
248 
249         BTreeConfiguration<String, String> config = new BTreeConfiguration<String, String>();
250         config.setAllowDuplicates( true );
251         config.setName( "master" );
252         config.setSerializers( serializer, serializer );
253         BTree<String, String> btree = new BTree<String, String>( config );
254 
255         int i = 7;
256         for ( char ch = 'a'; ch <= 'z'; ch++ )
257         {
258             for( int k = 0; k< i; k++ )
259             {
260                 btree.insert( String.valueOf( ch ), UUID.randomUUID().toString() );
261             }
262         }
263 
264         Cursor<String, String> cursor = btree.browse();
265 
266         char ch = 'a';
267         int k = 0;
268         
269         while ( cursor.hasNext() )
270         {
271             Tuple<String, String> t = cursor.next();
272             assertEquals( String.valueOf( ch ), t.getKey() );
273             k++;
274             
275             if( ( k % i ) == 0 )
276             {
277                 ch++;
278             }
279         }
280         
281         assertEquals( ( 'z' + 1 ) , ch );
282         
283         ch = 'z';
284         
285         while(cursor.hasPrev())
286         {
287             Tuple<String, String> t = cursor.prev();
288             assertEquals( String.valueOf( ch ), t.getKey() );
289             k--;
290             
291             if( ( k % i ) == 0 )
292             {
293                 ch--;
294             }
295         }
296 
297         assertEquals( ( 'a' - 1 ) , ch );
298         cursor.close();
299     }
300 
301     @Test
302     public void testMoveFirst() throws Exception
303     {
304         StringSerializer serializer = new StringSerializer();
305 
306         BTreeConfiguration<String, String> config = new BTreeConfiguration<String, String>();
307         config.setAllowDuplicates( true );
308         config.setName( "master" );
309         config.setSerializers( serializer, serializer );
310         BTree<String, String> btree = new BTree<String, String>( config );
311 
312         for ( char ch = 'a'; ch <= 'z'; ch++ )
313         {
314             btree.insert( String.valueOf( ch ), UUID.randomUUID().toString() );
315         }
316 
317         // add one more value for 'a'
318         btree.insert( String.valueOf( 'a' ), UUID.randomUUID().toString() );
319         
320         Cursor<String, String> cursor = btree.browseFrom( "c" );
321 
322         int i = 0;
323         while( cursor.hasNext() )
324         {
325             assertNotNull( cursor.next() );
326             i++;
327         }
328         assertEquals( 24, i );
329         
330         // now move the cursor first
331         cursor.beforeFirst();
332         assertTrue( cursor.hasNext() );
333         assertEquals( "c", cursor.next().getKey() );
334         
335         i = 0;
336         while( cursor.hasNext() )
337         {
338             assertNotNull( cursor.next() );
339             i++;
340         }
341         assertEquals( 23, i );
342         
343         cursor.close();
344         
345         cursor = btree.browse();
346         
347         i = 0;
348         while( cursor.hasNext() )
349         {
350             assertNotNull( cursor.next() );
351             i++;
352         }
353         assertEquals( 27, i );
354         
355         // now move the cursor first
356         cursor.beforeFirst();
357         assertTrue( cursor.hasNext() );
358         assertEquals( "a", cursor.next().getKey() );
359         
360         i = 0;
361         while( cursor.hasNext() )
362         {
363             assertNotNull( cursor.next() );
364             i++;
365         }
366         assertEquals( 26, i );
367     }
368 
369 
370     @Test(expected = NoSuchElementException.class)
371     public void testMoveLast() throws Exception
372     {
373         StringSerializer serializer = new StringSerializer();
374 
375         BTreeConfiguration<String, String> config = new BTreeConfiguration<String, String>();
376         config.setAllowDuplicates( true );
377         config.setName( "master" );
378         config.setSerializers( serializer, serializer );
379         BTree<String, String> btree = new BTree<String, String>( config );
380 
381         for ( char ch = 'a'; ch <= 'z'; ch++ )
382         {
383             btree.insert( String.valueOf( ch ), UUID.randomUUID().toString() );
384         }
385         
386         btree.insert( String.valueOf( 'z' ), UUID.randomUUID().toString() );
387         
388         Cursor<String, String> cursor = btree.browseFrom( "c" );
389         cursor.afterLast();
390         
391         assertFalse( cursor.hasNext() );
392         assertTrue( cursor.hasPrev() );
393         assertEquals( "z", cursor.prev().getKey() );
394         assertEquals( "z", cursor.prev().getKey() );
395         assertEquals( "y", cursor.prev().getKey() );
396         
397         cursor.beforeFirst();
398         assertEquals( "c", cursor.next().getKey() );
399 
400         cursor.afterLast();
401         assertFalse( cursor.hasNext() );
402         // make sure it throws NoSuchElementException
403         cursor.next();
404     }
405 
406     
407     @Test(expected = NoSuchElementException.class)
408     public void testMoveToNextPrevNonDuplicateKey() throws Exception
409     {
410         StringSerializer serializer = new StringSerializer();
411 
412         BTreeConfiguration<String, String> config = new BTreeConfiguration<String, String>();
413         config.setAllowDuplicates( true );
414         config.setName( "master" );
415         config.setSerializers( serializer, serializer );
416         BTree<String, String> btree = new BTree<String, String>( config );
417 
418         int i = 7;
419         for ( char ch = 'a'; ch <= 'z'; ch++ )
420         {
421             for( int k = 0; k< i; k++ )
422             {
423                 btree.insert( String.valueOf( ch ), String.valueOf( k ) );
424             }
425         }
426         
427         Cursor<String, String> cursor = btree.browse();
428 
429         assertTrue( cursor.hasNext() );
430         assertFalse( cursor.hasPrev() );
431         for(int k =0; k < 2; k++)
432         {
433             assertEquals( "a", cursor.next().getKey() );
434         }
435         
436         assertEquals( "a", cursor.next().getKey() );
437         
438         cursor.moveToNextNonDuplicateKey();
439         
440         assertEquals( "b", cursor.next().getKey() );
441         
442         for ( char ch = 'b'; ch < 'z'; ch++ )
443         {
444             assertEquals( String.valueOf( ch ), cursor.next().getKey() );
445             cursor.moveToNextNonDuplicateKey();
446             char t = ch;
447             assertEquals( String.valueOf( ++t ), cursor.next().getKey() );
448         }
449 
450         for(int k =0; k < i-1; k++)
451         {
452             assertEquals( "z", cursor.next().getKey() );
453         }
454 
455         assertFalse( cursor.hasNext() );
456         assertTrue( cursor.hasPrev() );
457         Tuple<String, String> tuple = cursor.prev();
458         assertEquals( "z", tuple.getKey() );
459         assertEquals( "6", tuple.getValue() );
460         
461         for ( char ch = 'z'; ch > 'a'; ch-- )
462         {
463             char t = ch;
464             t--;
465             
466             assertEquals( String.valueOf( ch ), cursor.prev().getKey() );
467             
468             cursor.moveToPrevNonDuplicateKey();
469 
470             tuple = cursor.prev();
471             assertEquals( String.valueOf( t ), tuple.getKey() );
472         }
473         
474         for(int k =5; k >=0; k--)
475         {
476             tuple = cursor.prev();
477             assertEquals( "a", tuple.getKey() );
478             assertEquals( String.valueOf( k ), tuple.getValue() );
479         }
480 
481         assertTrue( cursor.hasNext() );
482         assertFalse( cursor.hasPrev() );
483         tuple = cursor.next();
484         assertEquals( "a", tuple.getKey() );
485         assertEquals( "0", tuple.getValue() );
486 
487         cursor.close();
488         
489         cursor = btree.browseFrom("y");
490         cursor.moveToNextNonDuplicateKey();
491         assertTrue( cursor.hasPrev() );
492         tuple = cursor.prev();
493         assertEquals( "y", tuple.getKey() );
494         assertEquals( "6", tuple.getValue() );
495         cursor.close();
496         
497         cursor = btree.browse();
498         cursor.beforeFirst();
499         assertFalse( cursor.hasPrev() );
500         // make sure it throws NoSuchElementException
501         cursor.prev();
502     }
503     
504     
505     /**
506      * Test for moving between two leaves. When moveToNextNonDuplicateKey is called
507      * and cursor is on the last element of the current leaf.
508      *
509      * @throws Exception
510      */
511     @Test
512     public void testMoveToNextAndPrevWithPageBoundaries() throws Exception
513     {
514         IntSerializer serializer = new IntSerializer();
515 
516         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
517         config.setAllowDuplicates( true );
518         config.setName( "master" );
519         config.setPageSize( 4 );
520         config.setSerializers( serializer, serializer );
521         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
522 
523         int i = 7;
524         for ( int k=0; k < i; k++ )
525         {
526             btree.insert( k, k );
527         }
528         
529         // 3 is the last element of the first leaf
530         Cursor<Integer, Integer> cursor = btree.browseFrom(3);
531         cursor.moveToNextNonDuplicateKey();
532 
533         assertTrue( cursor.hasNext() );
534         Tuple<Integer, Integer> tuple = cursor.next();
535         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
536         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
537         cursor.close();
538 
539         cursor = btree.browseFrom(3);
540         cursor.moveToNextNonDuplicateKey();
541 
542         assertTrue( cursor.hasPrev() );
543         tuple = cursor.prev();
544         assertEquals( Integer.valueOf( 3 ), tuple.getKey() );
545         assertEquals( Integer.valueOf( 3 ), tuple.getValue() );
546         cursor.close();
547         
548         // 4 is the first element of the second leaf
549         cursor = btree.browseFrom(4);
550         cursor.moveToPrevNonDuplicateKey();
551 
552         assertTrue( cursor.hasPrev() );
553         tuple = cursor.prev();
554         assertEquals( Integer.valueOf( 3 ), tuple.getKey() );
555         assertEquals( Integer.valueOf( 3 ), tuple.getValue() );
556         
557         // the below assertion won't work cause of the index position
558         // issue when prev() and next() are called subsequently (in any order) 
559 //        assertTrue( cursor.hasNext() );
560 //        tuple = cursor.next();
561 //        assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
562 //        assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
563         cursor.close();
564         
565         // test the extremes of the BTree instead of that of leaves
566         cursor = btree.browseFrom(6);
567         cursor.moveToNextNonDuplicateKey();
568         assertFalse( cursor.hasNext() );
569         assertTrue( cursor.hasPrev() );
570         tuple = cursor.prev();
571         assertEquals( Integer.valueOf( 6 ), tuple.getKey() );
572         assertEquals( Integer.valueOf( 6 ), tuple.getValue() );
573         cursor.close();
574         
575         cursor = btree.browse();
576         cursor.moveToPrevNonDuplicateKey();
577         assertTrue( cursor.hasNext() );
578         assertFalse( cursor.hasPrev() );
579         tuple = cursor.next();
580         assertEquals( Integer.valueOf( 0 ), tuple.getKey() );
581         assertEquals( Integer.valueOf( 0 ), tuple.getValue() );
582         cursor.close();
583     }
584     
585     
586     @Test
587     public void testNextAfterPrev() throws Exception
588     {
589         IntSerializer serializer = new IntSerializer();
590 
591         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
592         config.setAllowDuplicates( true );
593         config.setName( "master" );
594         config.setPageSize( 4 );
595         config.setSerializers( serializer, serializer );
596         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
597 
598         int i = 7;
599         for ( int k=0; k < i; k++ )
600         {
601             btree.insert( k, k );
602         }
603         
604         // 3 is the last element of the first leaf
605         Cursor<Integer, Integer> cursor = btree.browseFrom(4);
606 
607         assertTrue( cursor.hasNext() );
608         Tuple<Integer, Integer> tuple = cursor.next();
609         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
610         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
611         
612         assertTrue( cursor.hasPrev() );
613         tuple = cursor.prev();
614         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
615         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
616 
617         assertTrue( cursor.hasNext() );
618         tuple = cursor.next();
619         assertEquals( Integer.valueOf( 4 ), tuple.getKey() );
620         assertEquals( Integer.valueOf( 4 ), tuple.getValue() );
621         cursor.close();
622 
623     }
624     
625     
626     /**
627      * Test for moving after a key and traversing backwards.
628      *
629      * @throws Exception
630      */
631     @Test
632     public void testMoveToNextAndTraverseBackward() throws Exception
633     {
634         IntSerializer serializer = new IntSerializer();
635 
636         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
637         config.setAllowDuplicates( true );
638         config.setName( "master" );
639         config.setPageSize( 8 );
640         config.setSerializers( serializer, serializer );
641         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
642 
643         int i = 5;
644         for ( int k=0; k < i; k++ )
645         {
646             btree.insert( k, k );
647         }
648         
649         // 4 is the last element in the tree
650         Cursor<Integer, Integer> cursor = btree.browseFrom(4);
651         cursor.moveToNextNonDuplicateKey();
652         
653         int currentKey = 4;
654         while( cursor.hasPrev() )
655         {
656         	assertEquals( Integer.valueOf( currentKey ), cursor.prev().getKey() );
657         	currentKey--;
658         }
659         
660         cursor.close();
661     }
662     
663     
664     /**
665      * Test for moving after a key and traversing backwards.
666      *
667      * @throws Exception
668      */
669     @Test
670     public void testMoveToPrevAndTraverseForward() throws Exception
671     {
672         IntSerializer serializer = new IntSerializer();
673 
674         BTreeConfiguration<Integer, Integer> config = new BTreeConfiguration<Integer, Integer>();
675         config.setAllowDuplicates( true );
676         config.setName( "master" );
677         config.setPageSize( 8 );
678         config.setSerializers( serializer, serializer );
679         BTree<Integer, Integer> btree = new BTree<Integer, Integer>( config );
680 
681         int i = 5;
682         for ( int k=0; k < i; k++ )
683         {
684             btree.insert( k, k );
685         }
686         
687         // 4 is the last element in the tree
688         Cursor<Integer, Integer> cursor = btree.browseFrom(0);
689         cursor.moveToPrevNonDuplicateKey();
690         
691         int currentKey = 0;
692         while( cursor.hasNext() )
693         {
694         	assertEquals( Integer.valueOf( currentKey ), cursor.next().getKey() );
695         	currentKey++;
696         }
697         
698         cursor.close();
699     }
700 
701 }