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.assertTrue;
25  import static org.junit.Assert.fail;
26  
27  import java.io.IOException;
28  
29  import org.apache.directory.mavibot.btree.BTree;
30  import org.apache.directory.mavibot.btree.BorrowedFromLeftResult;
31  import org.apache.directory.mavibot.btree.BorrowedFromRightResult;
32  import org.apache.directory.mavibot.btree.DeleteResult;
33  import org.apache.directory.mavibot.btree.InsertResult;
34  import org.apache.directory.mavibot.btree.Leaf;
35  import org.apache.directory.mavibot.btree.MergedWithSiblingResult;
36  import org.apache.directory.mavibot.btree.ModifyResult;
37  import org.apache.directory.mavibot.btree.Node;
38  import org.apache.directory.mavibot.btree.NotPresentResult;
39  import org.apache.directory.mavibot.btree.Page;
40  import org.apache.directory.mavibot.btree.ReferenceHolder;
41  import org.apache.directory.mavibot.btree.RemoveResult;
42  import org.apache.directory.mavibot.btree.Tuple;
43  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
44  import org.apache.directory.mavibot.btree.serializer.LongSerializer;
45  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
46  import org.junit.After;
47  import org.junit.Before;
48  import org.junit.Test;
49  
50  
51  /**
52   * A unit test class for Leaf
53   * 
54   * @author <a href="mailto:labs@labs.apache.org">Mavibot labs Project</a>
55   */
56  public class LeafTest
57  {
58      private BTree<Long, String> btree = null;
59  
60  
61      /**
62       * Create a btree
63       */
64      @Before
65      public void setup() throws IOException
66      {
67          btree = new BTree<Long, String>( "test", new LongSerializer(), new StringSerializer() );
68          btree.setPageSize( 8 );
69      }
70  
71  
72      @After
73      public void shutdown() throws IOException
74      {
75          btree.close();
76      }
77  
78  
79      /**
80       * A helper method to insert elements in a Leaf
81       * @throws IOException 
82       */
83      private Leaf<Long, String> insert( Leaf<Long, String> leaf, long key, String value ) throws IOException
84      {
85          InsertResult<Long, String> result = leaf.insert( 1L, key, value );
86  
87          return ( Leaf<Long, String> ) ( ( ModifyResult<Long, String> ) result ).getModifiedPage();
88      }
89  
90  
91      /**
92       * Test that deleting an entry from an empty page returns a NOT_PRESENT result
93       * @throws IOException
94       */
95      @Test
96      public void testDeleteFromEmptyLeaf() throws IOException
97      {
98          Leaf<Long, String> leaf = new Leaf<Long, String>( btree );
99  
100         DeleteResult<Long, String> result = leaf.delete( 1L, 1L, null, null, -1 );
101 
102         assertEquals( NotPresentResult.NOT_PRESENT, result );
103     }
104 
105 
106     /**
107      * Test that deleting an entry which is not present in the leaf works
108      * @throws IOException
109      */
110     @Test
111     public void testDeleteNotPresentElementFromRootLeaf() throws IOException
112     {
113         Leaf<Long, String> leaf = new Leaf<Long, String>( btree );
114         leaf = insert( leaf, 1L, "v1" );
115         leaf = insert( leaf, 2L, "v2" );
116         leaf = insert( leaf, 3L, "v3" );
117         leaf = insert( leaf, 4L, "v4" );
118 
119         DeleteResult<Long, String> result = leaf.delete( 2L, 5L, null, null, -1 );
120 
121         assertEquals( NotPresentResult.NOT_PRESENT, result );
122     }
123 
124 
125     /**
126      * Test that deleting an entry which is present in the leaf works
127      * @throws IOException
128      */
129     @Test
130     public void testDeletePresentElementFromRootLeaf() throws IOException
131     {
132         Leaf<Long, String> leaf = new Leaf<Long, String>( btree );
133         leaf = insert( leaf, 1L, "v1" );
134         leaf = insert( leaf, 2L, "v2" );
135         leaf = insert( leaf, 3L, "v3" );
136         leaf = insert( leaf, 4L, "v4" );
137 
138         DeleteResult<Long, String> result = leaf.delete( 4L, 3L, null, null, -1 );
139 
140         assertTrue( result instanceof RemoveResult );
141 
142         Tuple<Long, String> removedElement = ( ( RemoveResult<Long, String> ) result ).getRemovedElement();
143         Page<Long, String> newLeaf = ( ( RemoveResult<Long, String> ) result ).getModifiedPage();
144 
145         assertEquals( Long.valueOf( 3L ), removedElement.getKey() );
146         assertEquals( "v3", removedElement.getValue() );
147         assertEquals( 3, newLeaf.getNbElems() );
148 
149         try
150         {
151             assertEquals( "v1", newLeaf.get( 1L ) );
152             assertEquals( "v2", newLeaf.get( 2L ) );
153             assertEquals( "v4", newLeaf.get( 4L ) );
154         }
155         catch ( KeyNotFoundException knfe )
156         {
157             fail();
158         }
159 
160         try
161         {
162             newLeaf.get( 3L );
163             fail();
164         }
165         catch ( KeyNotFoundException knfe )
166         {
167             // Expected
168         }
169     }
170 
171 
172     /**
173      * Test that deleting the first element return the correct result
174      * @throws IOException
175      */
176     @Test
177     public void testDeleteFirstElementFromRootLeaf() throws IOException
178     {
179         Leaf<Long, String> leaf = new Leaf<Long, String>( btree );
180         leaf = insert( leaf, 1L, "v1" );
181         leaf = insert( leaf, 2L, "v2" );
182         leaf = insert( leaf, 3L, "v3" );
183         leaf = insert( leaf, 4L, "v4" );
184 
185         DeleteResult<Long, String> result = leaf.delete( 4L, 1L, null, null, -1 );
186 
187         assertTrue( result instanceof RemoveResult );
188 
189         RemoveResult<Long, String> removeResult = ( RemoveResult<Long, String> ) result;
190 
191         Tuple<Long, String> removedElement = removeResult.getRemovedElement();
192         Page<Long, String> newLeaf = removeResult.getModifiedPage();
193 
194         assertEquals( Long.valueOf( 1L ), removedElement.getKey() );
195         assertEquals( "v1", removedElement.getValue() );
196         assertEquals( 3, newLeaf.getNbElems() );
197 
198         try
199         {
200             newLeaf.get( 1L );
201             fail();
202         }
203         catch ( KeyNotFoundException knfe )
204         {
205             // expected
206         }
207 
208         try
209         {
210             assertEquals( "v2", newLeaf.get( 2L ) );
211             assertEquals( "v3", newLeaf.get( 3L ) );
212             assertEquals( "v4", newLeaf.get( 4L ) );
213         }
214         catch ( KeyNotFoundException knfe )
215         {
216             fail();
217         }
218     }
219 
220 
221     /**
222      * Check that deleting an element from a leaf with N/2 element works when we borrow
223      * an element in a left page with more than N/2 elements
224      * @throws IOException 
225      */
226     @Test
227     public void testDeleteBorrowingFromLeftSibling() throws IOException
228     {
229         Node<Long, String> parent = new Node<Long, String>( btree, 1L, 2 );
230         Leaf<Long, String> left = new Leaf<Long, String>( btree );
231         Leaf<Long, String> target = new Leaf<Long, String>( btree );
232         Leaf<Long, String> right = new Leaf<Long, String>( btree );
233 
234         // Fill the left page
235         left = insert( left, 1L, "v1" );
236         left = insert( left, 2L, "v2" );
237         left = insert( left, 3L, "v3" );
238         left = insert( left, 4L, "v4" );
239         left = insert( left, 5L, "v5" );
240 
241         // Fill the target page
242         target = insert( target, 6L, "v6" );
243         target = insert( target, 7L, "v7" );
244         target = insert( target, 8L, "v8" );
245         target = insert( target, 9L, "v9" );
246 
247         // Fill the right page
248         right = insert( right, 10L, "v10" );
249         right = insert( right, 11L, "v11" );
250         right = insert( right, 12L, "v12" );
251         right = insert( right, 13L, "v13" );
252 
253         parent.children[0] = new ReferenceHolder<Page<Long, String>, Long, String>( null, left,
254             left.getOffset(), left.getLastOffset() );
255         parent.children[1] = new ReferenceHolder<Page<Long, String>, Long, String>( null, target,
256             target.getOffset(), target.getLastOffset() );
257         parent.children[2] = new ReferenceHolder<Page<Long, String>, Long, String>( null, right,
258             right.getOffset(), right.getLastOffset() );
259 
260         // Update the parent
261         parent.keys[0] = 6L;
262         parent.keys[1] = 10L;
263 
264         // Now, delete the element from the target page
265         DeleteResult<Long, String> result = target.delete( 2L, 7L, null, parent, 1 );
266 
267         assertTrue( result instanceof BorrowedFromLeftResult );
268 
269         BorrowedFromLeftResult<Long, String> borrowed = ( BorrowedFromLeftResult<Long, String> ) result;
270         Tuple<Long, String> removedKey = borrowed.getRemovedElement();
271 
272         assertEquals( Long.valueOf( 7L ), removedKey.getKey() );
273 
274         // Check the modified leaf
275         Leaf<Long, String> newLeaf = ( Leaf<Long, String> ) borrowed.getModifiedPage();
276 
277         assertEquals( 4, newLeaf.nbElems );
278         assertEquals( Long.valueOf( 5L ), newLeaf.keys[0] );
279         assertEquals( Long.valueOf( 6L ), newLeaf.keys[1] );
280         assertEquals( Long.valueOf( 8L ), newLeaf.keys[2] );
281         assertEquals( Long.valueOf( 9L ), newLeaf.keys[3] );
282 
283         // Check the sibling
284         Leaf<Long, String> leftSibling = ( Leaf<Long, String> ) borrowed.getModifiedSibling();
285 
286         assertEquals( 4, leftSibling.nbElems );
287         assertEquals( Long.valueOf( 1L ), leftSibling.keys[0] );
288         assertEquals( Long.valueOf( 2L ), leftSibling.keys[1] );
289         assertEquals( Long.valueOf( 3L ), leftSibling.keys[2] );
290         assertEquals( Long.valueOf( 4L ), leftSibling.keys[3] );
291     }
292 
293 
294     /**
295      * Check that deleting an element from a leaf with N/2 element works when we borrow
296      * an element in a right page with more than N/2 elements
297      * @throws IOException 
298      */
299     @Test
300     public void testDeleteBorrowingFromRightSibling() throws IOException
301     {
302         Node<Long, String> parent = new Node<Long, String>( btree, 1L, 2 );
303         Leaf<Long, String> left = new Leaf<Long, String>( btree );
304         Leaf<Long, String> target = new Leaf<Long, String>( btree );
305         Leaf<Long, String> right = new Leaf<Long, String>( btree );
306 
307         // Fill the left page
308         left = insert( left, 1L, "v1" );
309         left = insert( left, 2L, "v2" );
310         left = insert( left, 3L, "v3" );
311         left = insert( left, 4L, "v4" );
312 
313         // Fill the target page
314         target = insert( target, 6L, "v6" );
315         target = insert( target, 7L, "v7" );
316         target = insert( target, 8L, "v8" );
317         target = insert( target, 9L, "v9" );
318 
319         // Fill the right page
320         right = insert( right, 10L, "v10" );
321         right = insert( right, 11L, "v11" );
322         right = insert( right, 12L, "v12" );
323         right = insert( right, 13L, "v13" );
324         right = insert( right, 14L, "v14" );
325 
326         parent.children[0] = new ReferenceHolder<Page<Long, String>, Long, String>( null, left,
327             left.getOffset(), left.getLastOffset() );
328         parent.children[1] = new ReferenceHolder<Page<Long, String>, Long, String>( null, target,
329             target.getOffset(), target.getLastOffset() );
330         parent.children[2] = new ReferenceHolder<Page<Long, String>, Long, String>( null, right,
331             right.getOffset(), right.getLastOffset() );
332 
333         // Update the parent
334         parent.keys[0] = 6L;
335         parent.keys[1] = 10L;
336 
337         // Now, delete the element from the target page
338         DeleteResult<Long, String> result = target.delete( 2L, 7L, null, parent, 1 );
339 
340         assertTrue( result instanceof BorrowedFromRightResult );
341 
342         BorrowedFromRightResult<Long, String> borrowed = ( BorrowedFromRightResult<Long, String> ) result;
343         assertEquals( Long.valueOf( 11L ), borrowed.getModifiedSibling().getKey( 0 ) );
344         Tuple<Long, String> removedKey = borrowed.getRemovedElement();
345 
346         assertEquals( Long.valueOf( 7L ), removedKey.getKey() );
347 
348         // Check the modified leaf
349         Leaf<Long, String> newLeaf = ( Leaf<Long, String> ) borrowed.getModifiedPage();
350 
351         assertEquals( 4, newLeaf.nbElems );
352         assertEquals( Long.valueOf( 6L ), newLeaf.keys[0] );
353         assertEquals( Long.valueOf( 8L ), newLeaf.keys[1] );
354         assertEquals( Long.valueOf( 9L ), newLeaf.keys[2] );
355         assertEquals( Long.valueOf( 10L ), newLeaf.keys[3] );
356 
357         // Check the sibling
358         Leaf<Long, String> rightSibling = ( Leaf<Long, String> ) borrowed.getModifiedSibling();
359 
360         assertEquals( 4, rightSibling.nbElems );
361         assertEquals( Long.valueOf( 11L ), rightSibling.keys[0] );
362         assertEquals( Long.valueOf( 12L ), rightSibling.keys[1] );
363         assertEquals( Long.valueOf( 13L ), rightSibling.keys[2] );
364         assertEquals( Long.valueOf( 14L ), rightSibling.keys[3] );
365     }
366 
367 
368     /**
369      * Check that deleting an element from a leaf with N/2 element works when we merge
370      * it with one of its sibling, if both has N/2 elements
371      * @throws IOException 
372      */
373     @Test
374     public void testDeleteMergeWithSibling() throws IOException
375     {
376         Node<Long, String> parent = new Node<Long, String>( btree, 1L, 2 );
377         Leaf<Long, String> left = new Leaf<Long, String>( btree );
378         Leaf<Long, String> target = new Leaf<Long, String>( btree );
379         Leaf<Long, String> right = new Leaf<Long, String>( btree );
380 
381         // Fill the left page
382         left = insert( left, 1L, "v1" );
383         left = insert( left, 2L, "v2" );
384         left = insert( left, 3L, "v3" );
385         left = insert( left, 4L, "v4" );
386 
387         // Fill the target page
388         target = insert( target, 5L, "v5" );
389         target = insert( target, 6L, "v6" );
390         target = insert( target, 7L, "v7" );
391         target = insert( target, 8L, "v8" );
392 
393         // Fill the right page
394         right = insert( right, 9L, "v9" );
395         right = insert( right, 10L, "v10" );
396         right = insert( right, 11L, "v11" );
397         right = insert( right, 12L, "v12" );
398 
399         parent.children[0] = new ReferenceHolder<Page<Long, String>, Long, String>( null, left,
400             left.getOffset(), left.getLastOffset() );
401         parent.children[1] = new ReferenceHolder<Page<Long, String>, Long, String>( null, target,
402             target.getOffset(), target.getLastOffset() );;
403         parent.children[2] = new ReferenceHolder<Page<Long, String>, Long, String>( null, right,
404             right.getOffset(), right.getLastOffset() );
405 
406         // Update the parent
407         parent.keys[0] = 5L;
408         parent.keys[1] = 9L;
409 
410         // Now, delete the element from the target page
411         DeleteResult<Long, String> result = target.delete( 2L, 7L, null, parent, 1 );
412 
413         assertTrue( result instanceof MergedWithSiblingResult );
414 
415         MergedWithSiblingResult<Long, String> merged = ( MergedWithSiblingResult<Long, String> ) result;
416         Tuple<Long, String> removedKey = merged.getRemovedElement();
417 
418         assertEquals( Long.valueOf( 7L ), removedKey.getKey() );
419 
420         // Check the modified leaf
421         Leaf<Long, String> newLeaf = ( Leaf<Long, String> ) merged.getModifiedPage();
422 
423         assertEquals( 7, newLeaf.nbElems );
424         assertEquals( Long.valueOf( 1L ), newLeaf.keys[0] );
425         assertEquals( Long.valueOf( 2L ), newLeaf.keys[1] );
426         assertEquals( Long.valueOf( 3L ), newLeaf.keys[2] );
427         assertEquals( Long.valueOf( 4L ), newLeaf.keys[3] );
428         assertEquals( Long.valueOf( 5L ), newLeaf.keys[4] );
429         assertEquals( Long.valueOf( 6L ), newLeaf.keys[5] );
430         assertEquals( Long.valueOf( 8L ), newLeaf.keys[6] );
431     }
432 
433 
434     /**
435      * Test the findPos() method
436      * @throws Exception
437      */
438     @Test
439     public void testFindPos() throws Exception
440     {
441         Leaf<Long, String> leaf = new Leaf<Long, String>( btree );
442 
443         // Inject the values
444         for ( long i = 0; i < 8; i++ )
445         {
446             long value = i + i + 1;
447             leaf = ( Leaf<Long, String> ) ( ( ModifyResult<Long, String> ) leaf.insert( 0L, value, "V" + value ) )
448                 .getModifiedPage();
449         }
450 
451         // Check the findPos() method now
452         for ( long i = 0; i < 17; i++ )
453         {
454             if ( i % 2 == 1 )
455             {
456                 assertEquals( -( i / 2 + 1 ), leaf.findPos( i ) );
457             }
458             else
459             {
460                 assertEquals( i / 2, leaf.findPos( i ) );
461             }
462         }
463     }
464 }