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.exception.KeyNotFoundException;
30  import org.apache.directory.mavibot.btree.serializer.LongSerializer;
31  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
32  import org.junit.After;
33  import org.junit.Before;
34  import org.junit.Test;
35  
36  
37  /**
38   * A unit test class for Leaf
39   *
40   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
41   */
42  public class InMemoryLeafTest
43  {
44      private BTree<Long, String> btree = null;
45  
46  
47      /**
48       * Create a btree
49       */
50      @Before
51      public void setup() throws IOException
52      {
53          btree = BTreeFactory.createInMemoryBTree( "test", LongSerializer.INSTANCE, StringSerializer.INSTANCE );
54          btree.setPageSize( 8 );
55      }
56  
57  
58      @After
59      public void shutdown() throws IOException
60      {
61          btree.close();
62      }
63  
64  
65      /**
66       * A helper method to insert elements in a Leaf
67       * @throws IOException
68       */
69      private InMemoryLeaf<Long, String> insert( InMemoryLeaf<Long, String> leaf, long key, String value )
70          throws IOException
71      {
72          InsertResult<Long, String> result = leaf.insert( key, value, 1L );
73  
74          return ( InMemoryLeaf<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          InMemoryLeaf<Long, String> leaf = new InMemoryLeaf<Long, String>( btree );
86  
87          DeleteResult<Long, String> result = leaf.delete( 1L, null, 1L, 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         InMemoryLeaf<Long, String> leaf = new InMemoryLeaf<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( 5L, null, 2L, 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         InMemoryLeaf<Long, String> leaf = new InMemoryLeaf<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( 3L, null, 4L, 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         InMemoryLeaf<Long, String> leaf = new InMemoryLeaf<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( 1L, null, 4L, 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         InMemoryNode<Long, String> parent = new InMemoryNode<Long, String>( btree, 1L, 2 );
221         InMemoryLeaf<Long, String> left = new InMemoryLeaf<Long, String>( btree );
222         InMemoryLeaf<Long, String> target = new InMemoryLeaf<Long, String>( btree );
223         InMemoryLeaf<Long, String> right = new InMemoryLeaf<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.setPageHolder( 0, new PageHolder<Long, String>( btree, left ) );
245         parent.setPageHolder( 1, new PageHolder<Long, String>( btree, target ) );
246         parent.setPageHolder( 2, new PageHolder<Long, String>( btree, right ) );
247 
248         // Update the parent
249         parent.setKey( 0, new KeyHolder<Long>( 6L ) );
250         parent.setKey( 1, new KeyHolder<Long>( 10L ) );
251 
252         // Now, delete the element from the target page
253         DeleteResult<Long, String> result = target.delete( 7L, null, 2L, 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         InMemoryLeaf<Long, String> newLeaf = ( InMemoryLeaf<Long, String> ) borrowed.getModifiedPage();
264 
265         assertEquals( 4, newLeaf.getNbElems() );
266         assertEquals( Long.valueOf( 5L ), newLeaf.getKey( 0 ) );
267         assertEquals( Long.valueOf( 6L ), newLeaf.getKey( 1 ) );
268         assertEquals( Long.valueOf( 8L ), newLeaf.getKey( 2 ) );
269         assertEquals( Long.valueOf( 9L ), newLeaf.getKey( 3 ) );
270 
271         // Check the sibling
272         InMemoryLeaf<Long, String> leftSibling = ( InMemoryLeaf<Long, String> ) borrowed.getModifiedSibling();
273 
274         assertEquals( 4, leftSibling.getNbElems() );
275         assertEquals( Long.valueOf( 1L ), leftSibling.getKey( 0 ) );
276         assertEquals( Long.valueOf( 2L ), leftSibling.getKey( 1 ) );
277         assertEquals( Long.valueOf( 3L ), leftSibling.getKey( 2 ) );
278         assertEquals( Long.valueOf( 4L ), leftSibling.getKey( 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         InMemoryNode<Long, String> parent = new InMemoryNode<Long, String>( btree, 1L, 2 );
291         InMemoryLeaf<Long, String> left = new InMemoryLeaf<Long, String>( btree );
292         InMemoryLeaf<Long, String> target = new InMemoryLeaf<Long, String>( btree );
293         InMemoryLeaf<Long, String> right = new InMemoryLeaf<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.setPageHolder( 0, new PageHolder<Long, String>( btree, left ) );
315         parent.setPageHolder( 1, new PageHolder<Long, String>( btree, target ) );
316         parent.setPageHolder( 2, new PageHolder<Long, String>( btree, right ) );
317 
318         // Update the parent
319         parent.setKey( 0, new KeyHolder<Long>( 6L ) );
320         parent.setKey( 1, new KeyHolder<Long>( 10L ) );
321 
322         // Now, delete the element from the target page
323         DeleteResult<Long, String> result = target.delete( 7L, null, 2L, 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         InMemoryLeaf<Long, String> newLeaf = ( InMemoryLeaf<Long, String> ) borrowed.getModifiedPage();
335 
336         assertEquals( 4, newLeaf.getNbElems() );
337         assertEquals( Long.valueOf( 6L ), newLeaf.getKey( 0 ) );
338         assertEquals( Long.valueOf( 8L ), newLeaf.getKey( 1 ) );
339         assertEquals( Long.valueOf( 9L ), newLeaf.getKey( 2 ) );
340         assertEquals( Long.valueOf( 10L ), newLeaf.getKey( 3 ) );
341 
342         // Check the sibling
343         InMemoryLeaf<Long, String> rightSibling = ( InMemoryLeaf<Long, String> ) borrowed.getModifiedSibling();
344 
345         assertEquals( 4, rightSibling.getNbElems() );
346         assertEquals( Long.valueOf( 11L ), rightSibling.getKey( 0 ) );
347         assertEquals( Long.valueOf( 12L ), rightSibling.getKey( 1 ) );
348         assertEquals( Long.valueOf( 13L ), rightSibling.getKey( 2 ) );
349         assertEquals( Long.valueOf( 14L ), rightSibling.getKey( 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         InMemoryNode<Long, String> parent = new InMemoryNode<Long, String>( btree, 1L, 2 );
362         InMemoryLeaf<Long, String> left = new InMemoryLeaf<Long, String>( btree );
363         InMemoryLeaf<Long, String> target = new InMemoryLeaf<Long, String>( btree );
364         InMemoryLeaf<Long, String> right = new InMemoryLeaf<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.setPageHolder( 0, new PageHolder<Long, String>( btree, left ) );
385         parent.setPageHolder( 1, new PageHolder<Long, String>( btree, target ) );
386         parent.setPageHolder( 2, new PageHolder<Long, String>( btree, right ) );
387 
388         // Update the parent
389         parent.setKey( 0, new KeyHolder<Long>( 5L ) );
390         parent.setKey( 1, new KeyHolder<Long>( 9L ) );
391 
392         // Now, delete the element from the target page
393         DeleteResult<Long, String> result = target.delete( 7L, null, 2L, 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         InMemoryLeaf<Long, String> newLeaf = ( InMemoryLeaf<Long, String> ) merged.getModifiedPage();
404 
405         assertEquals( 7, newLeaf.getNbElems() );
406         assertEquals( Long.valueOf( 1L ), newLeaf.getKey( 0 ) );
407         assertEquals( Long.valueOf( 2L ), newLeaf.getKey( 1 ) );
408         assertEquals( Long.valueOf( 3L ), newLeaf.getKey( 2 ) );
409         assertEquals( Long.valueOf( 4L ), newLeaf.getKey( 3 ) );
410         assertEquals( Long.valueOf( 5L ), newLeaf.getKey( 4 ) );
411         assertEquals( Long.valueOf( 6L ), newLeaf.getKey( 5 ) );
412         assertEquals( Long.valueOf( 8L ), newLeaf.getKey( 6 ) );
413     }
414 
415 
416     /**
417      * Test the findPos() method
418      * @throws Exception
419      */
420     @Test
421     public void testFindPos() throws Exception
422     {
423         InMemoryLeaf<Long, String> leaf = new InMemoryLeaf<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 = ( InMemoryLeaf<Long, String> ) ( ( ModifyResult<Long, String> ) leaf.insert( value, "V" + value, 0L ) )
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 }