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