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