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.server.core.partition.impl.btree.jdbm;
21  
22  
23  import org.junit.Before;
24  import org.junit.Test;
25  import org.junit.After;
26  import static org.junit.Assert.assertEquals;
27  import static org.junit.Assert.assertFalse;
28  import static org.junit.Assert.assertNull;
29  import static org.junit.Assert.assertTrue;
30  import static org.junit.Assert.fail;
31  
32  import org.apache.commons.io.FileUtils;
33  import org.apache.directory.server.schema.bootstrap.*;
34  import org.apache.directory.server.schema.registries.*;
35  import org.apache.directory.server.schema.SerializableComparator;
36  import org.apache.directory.server.xdbm.Index;
37  import org.apache.directory.server.xdbm.IndexEntry;
38  import org.apache.directory.server.core.cursor.Cursor;
39  import org.apache.directory.server.core.entry.ServerEntry;
40  import org.apache.directory.shared.ldap.constants.SchemaConstants;
41  
42  import java.util.Set;
43  import java.util.HashSet;
44  import java.io.File;
45  import java.io.IOException;
46  
47  
48  /**
49   * Tests the JdbmIndex.
50   *
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   * @version $Rev$
53   */
54  public class JdbmIndexTest
55  {
56      AttributeTypeRegistry registry;
57      File dbFileDir;
58      Index<String,ServerEntry> idx;
59  
60  
61      @Before
62      public void setup() throws Exception
63      {
64          BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
65          OidRegistry oidRegistry = new DefaultOidRegistry();
66          Registries registries = new DefaultRegistries( "bootstrap", loader, oidRegistry );
67          SerializableComparator.setRegistry( registries.getComparatorRegistry() );
68  
69          // load essential bootstrap schemas
70          Set<Schema> bootstrapSchemas = new HashSet<Schema>();
71          bootstrapSchemas.add( new ApachemetaSchema() );
72          bootstrapSchemas.add( new ApacheSchema() );
73          bootstrapSchemas.add( new CoreSchema() );
74          bootstrapSchemas.add( new SystemSchema() );
75          loader.loadWithDependencies( bootstrapSchemas, registries );
76          this.registry = registries.getAttributeTypeRegistry();
77  
78          if ( dbFileDir != null )
79          {
80              dbFileDir.delete();
81          }
82  
83          File tmpIndexFile = File.createTempFile( JdbmIndexTest.class.getSimpleName(), "db" );
84          tmpIndexFile.deleteOnExit();
85          dbFileDir = new File( tmpIndexFile.getParentFile(),
86              JdbmIndexTest.class.getSimpleName() );
87  
88          dbFileDir.mkdirs();
89      }
90  
91  
92      @After
93      public void teardown() throws Exception
94      {
95          registry = null;
96          destroyIndex();
97          
98          if ( ( dbFileDir != null ) && dbFileDir.exists() )
99          {
100             FileUtils.deleteDirectory( dbFileDir );
101         }
102     }
103 
104 
105     void destroyIndex() throws Exception
106     {
107         if ( idx != null )
108         {
109             idx.sync();
110             idx.close();
111             File file = new File( idx.getWkDirPath(), idx.getAttribute().getName() + ".db" );
112             file.delete();
113         }
114         
115         idx = null;
116     }
117 
118 
119     void initIndex() throws Exception
120     {
121         initIndex( new JdbmIndex<String,ServerEntry>() );
122     }
123 
124 
125     void initIndex( JdbmIndex<String,ServerEntry> jdbmIdx ) throws Exception
126     {
127         if ( jdbmIdx == null )
128         {
129             jdbmIdx = new JdbmIndex<String,ServerEntry>();
130         }
131 
132         jdbmIdx.init( registry.lookup( SchemaConstants.OU_AT ), dbFileDir );
133         this.idx = jdbmIdx;
134     }
135 
136 
137     // -----------------------------------------------------------------------
138     // Property Test Methods
139     // -----------------------------------------------------------------------
140 
141 
142     @Test
143     public void testAttributeId() throws Exception
144     {
145         // uninitialized index
146         JdbmIndex jdbmIndex = new JdbmIndex();
147         jdbmIndex.setAttributeId( "foo" );
148         assertEquals( "foo", jdbmIndex.getAttributeId() );
149 
150         jdbmIndex = new JdbmIndex( "bar" );
151         assertEquals( "bar", jdbmIndex.getAttributeId() );
152 
153         // initialized index
154         initIndex();
155         try
156         {
157             idx.setAttributeId( "foo" );
158             fail( "Should not be able to set attributeId after initialization." );
159         }
160         catch ( Exception e )
161         {
162         }
163         assertEquals( "ou", idx.getAttributeId() );
164 
165         destroyIndex();
166         initIndex( new JdbmIndex<String,ServerEntry>( "foo" ) );
167         assertEquals( "foo", idx.getAttributeId() );
168     }
169 
170 
171     @Test
172     public void testCacheSize() throws Exception
173     {
174         // uninitialized index
175         JdbmIndex jdbmIndex = new JdbmIndex();
176         jdbmIndex.setCacheSize( 337 );
177         assertEquals( 337, jdbmIndex.getCacheSize() );
178 
179         // initialized index
180         initIndex();
181         try
182         {
183             idx.setCacheSize( 30 );
184             fail( "Should not be able to set cacheSize after initialization." );
185         }
186         catch ( Exception e )
187         {
188         }
189         assertEquals( Index.DEFAULT_INDEX_CACHE_SIZE, idx.getCacheSize() );
190     }
191 
192 
193     @Test
194     public void testWkDirPath() throws Exception
195     {
196         // uninitialized index
197         JdbmIndex<String,ServerEntry> jdbmIndex = new JdbmIndex<String,ServerEntry>();
198         jdbmIndex.setWkDirPath( new File( dbFileDir, "foo" ) );
199         assertEquals( "foo", jdbmIndex.getWkDirPath().getName() );
200 
201         // initialized index
202         initIndex();
203         try
204         {
205             idx.setWkDirPath( new File( dbFileDir, "foo" ) );
206             fail( "Should not be able to set wkDirPath after initialization." );
207         }
208         catch ( Exception e )
209         {
210         }
211         assertEquals( dbFileDir, idx.getWkDirPath() );
212 
213         destroyIndex();
214         jdbmIndex = new JdbmIndex<String,ServerEntry>();
215         File wkdir = new File( dbFileDir, "foo" );
216         wkdir.mkdirs();
217         jdbmIndex.setWkDirPath( wkdir );
218         initIndex( jdbmIndex );
219         assertEquals( wkdir, idx.getWkDirPath() );
220     }
221 
222 
223     @Test
224     public void testNumDupLimit() throws Exception
225     {
226         // uninitialized index
227         JdbmIndex jdbmIndex = new JdbmIndex();
228         jdbmIndex.setNumDupLimit( 337 );
229         assertEquals( 337, jdbmIndex.getNumDupLimit() );
230 
231         // initialized index
232         initIndex();
233         try
234         {
235             ( ( JdbmIndex ) idx).setNumDupLimit( 30 );
236             fail( "Should not be able to set numDupLimit after initialization." );
237         }
238         catch ( Exception e )
239         {
240         }
241         assertEquals( JdbmIndex.DEFAULT_DUPLICATE_LIMIT, ( ( JdbmIndex ) idx).getNumDupLimit() );
242     }
243 
244 
245     @Test
246     public void testGetAttribute() throws Exception
247     {
248         // uninitialized index
249         JdbmIndex jdbmIndex = new JdbmIndex();
250         assertNull( jdbmIndex.getAttribute() );
251 
252         initIndex();
253         assertEquals( registry.lookup( "ou" ), idx.getAttribute() );
254     }
255 
256 
257     @Test
258     public void testIsCountExact() throws Exception
259     {
260         assertFalse( new JdbmIndex().isCountExact() );
261     }
262 
263 
264     // -----------------------------------------------------------------------
265     // Count Test Methods
266     // -----------------------------------------------------------------------
267 
268 
269     @Test
270     public void testCount() throws Exception
271     {
272         initIndex();
273         assertEquals( 0, idx.count() );
274 
275         idx.add( "foo", 1234L );
276         assertEquals( 1, idx.count() );
277 
278         idx.add( "foo", 333L );
279         assertEquals( 2, idx.count() );
280 
281         idx.add( "bar", 555L );
282         assertEquals( 3, idx.count() );
283     }
284 
285 
286     @Test
287     public void testCountOneArg() throws Exception
288     {
289         initIndex();
290         assertEquals( 0, idx.count( "foo" ) );
291 
292         idx.add( "bar", 1234L );
293         assertEquals( 0, idx.count( "foo" ) );
294 
295         idx.add( "foo", 1234L );
296         assertEquals( 1, idx.count( "foo" ) );
297 
298         idx.add( "foo", 333L );
299         assertEquals( 2, idx.count( "foo" ) );
300     }
301 
302 
303     @Test
304     public void testGreaterThanCount() throws Exception
305     {
306         initIndex();
307         assertEquals( 0, idx.greaterThanCount( "a" ) );
308 
309         for ( char ch = 'a'; ch <= 'z'; ch++ )
310         {
311             idx.add( String.valueOf( ch ), ( long ) ch );
312         }
313         assertEquals( 26, idx.greaterThanCount( "a" ) );
314     }
315 
316 
317     @Test
318     public void testLessThanCount() throws Exception
319     {
320         initIndex();
321         assertEquals( 0, idx.lessThanCount( "z" ) );
322 
323         for ( char ch = 'a'; ch <= 'z'; ch++ )
324         {
325             idx.add( String.valueOf( ch ), ( long ) ch );
326         }
327         assertEquals( 26, idx.lessThanCount( "z" ) );
328     }
329 
330 
331     // -----------------------------------------------------------------------
332     // Add, Drop and Lookup Test Methods
333     // -----------------------------------------------------------------------
334 
335 
336     @Test
337     public void testLookups() throws Exception
338     {
339         initIndex();
340         assertNull( idx.forwardLookup( "foo" ) );
341         assertNull( idx.forwardLookup( "bar" ) );
342         assertNull( idx.reverseLookup( 0L ) );
343         assertFalse( idx.forwardGreaterOrEq( "foo", 0L ) );
344         assertFalse( idx.forwardGreaterOrEq( "foo", -24L ) );
345         assertFalse( idx.forwardGreaterOrEq( "foo", 24L ) );
346         assertFalse( idx.forwardLessOrEq( "foo", 0L ) );
347         assertFalse( idx.forwardLessOrEq( "foo", 24L ) );
348         assertFalse( idx.forwardLessOrEq( "foo", -24L ) );
349 
350         idx.add( "foo", 0L );
351         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
352         assertEquals( "foo", idx.reverseLookup( 0L ) );
353         assertTrue( idx.forward( "foo", 0L ) );
354         assertTrue( idx.forwardGreaterOrEq( "foo", 0L ) );
355         assertTrue( idx.forwardGreaterOrEq( "foo", -1L ) );
356         assertFalse( idx.forwardGreaterOrEq( "foo", 1L ) );
357         assertTrue( idx.forwardLessOrEq( "foo", 0L ) );
358         assertTrue( idx.forwardLessOrEq( "foo", 1L ) );
359         assertFalse( idx.forwardLessOrEq( "foo", -1L ) );
360 
361         idx.add( "foo", 1L );
362         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
363         assertEquals( "foo", idx.reverseLookup( 0L ) );
364         assertEquals( "foo", idx.reverseLookup( 1L ) );
365         assertTrue( idx.forward( "foo", 0L ) );
366         assertTrue( idx.forward( "foo", 1L ) );
367         assertTrue( idx.forwardGreaterOrEq( "foo", 0L ) );
368         assertTrue( idx.forwardGreaterOrEq( "foo", 1L ) );
369         assertTrue( idx.forwardGreaterOrEq( "foo", -1L ) );
370         assertFalse( idx.forwardGreaterOrEq( "foo", 2L ) );
371         assertTrue( idx.forwardLessOrEq( "foo", 0L ) );
372         assertTrue( idx.forwardLessOrEq( "foo", 1L ) );
373         assertTrue( idx.forwardLessOrEq( "foo", 2L ) );
374         assertFalse( idx.forwardLessOrEq( "foo", -1L ) );
375 
376         idx.add( "bar", 0L );
377         assertEquals( 0L, ( long ) idx.forwardLookup( "bar" ) );
378         assertEquals( "bar", idx.reverseLookup( 0L ) );  // reverse lookup returns first val
379         assertTrue( idx.forward( "bar", 0L ) );
380         assertTrue( idx.forward( "foo", 0L ) );
381         assertTrue( idx.forward( "foo", 1L ) );
382         assertTrue( idx.forwardGreaterOrEq( "bar", 0L ) );
383         assertTrue( idx.forwardGreaterOrEq( "foo", 0L ) );
384         assertTrue( idx.forwardGreaterOrEq( "foo", 1L ) );
385         assertTrue( idx.forwardGreaterOrEq( "foo", -1L ) );
386         assertFalse( idx.forwardGreaterOrEq( "foo", 2L ) );
387         assertFalse( idx.forwardGreaterOrEq( "bar", 1L ) );
388         assertTrue( idx.forwardLessOrEq( "bar", 0L ) );
389         assertTrue( idx.forwardLessOrEq( "foo", 0L ) );
390         assertTrue( idx.forwardLessOrEq( "foo", 1L ) );
391         assertTrue( idx.forwardLessOrEq( "foo", 2L ) );
392         assertFalse( idx.forwardLessOrEq( "foo", -1L ) );
393         assertFalse( idx.forwardLessOrEq( "bar", -1L ) );
394     }
395 
396 
397     @Test
398     public void testAddDropById() throws Exception
399     {
400         initIndex();
401         assertNull( idx.forwardLookup( "foo" ) );
402         assertNull( idx.forwardLookup( "bar" ) );
403         assertNull( idx.reverseLookup( 0L ) );
404         assertNull( idx.reverseLookup( 1L ) );
405 
406         // test add/drop without adding any duplicates
407         idx.add( "foo", 0L );
408         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
409         assertEquals( "foo", idx.reverseLookup( 0L ) );
410 
411         idx.drop( 0L );
412         assertNull( idx.forwardLookup( "foo" ) );
413         assertNull( idx.reverseLookup( 0L ) );
414 
415         // test add/drop with duplicates in bulk
416         idx.add( "foo", 0L );
417         idx.add( "foo", 1L );
418         idx.add( "bar", 0L );
419         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
420         assertEquals( 0L, ( long ) idx.forwardLookup( "bar" ) );
421         assertEquals( "bar", idx.reverseLookup( 0L ) );
422         assertEquals( "foo", idx.reverseLookup( 1L ) );
423 
424         idx.drop( 0L );
425         assertEquals( "foo", idx.reverseLookup( 1L ) );
426         assertFalse( idx.forward( "bar", 0L ) );
427         assertFalse( idx.forward( "foo", 0L ) );
428 
429         idx.drop( 1L );
430         assertNull( idx.forwardLookup( "foo" ) );
431         assertNull( idx.forwardLookup( "bar" ) );
432         assertNull( idx.reverseLookup( 0L ) );
433         assertNull( idx.reverseLookup( 1L ) );
434         assertEquals( 0, idx.count() );
435     }
436 
437 
438     @Test
439     public void testAddDropOneByOne() throws Exception
440     {
441         initIndex();
442         assertNull( idx.forwardLookup( "foo" ) );
443         assertNull( idx.forwardLookup( "bar" ) );
444         assertNull( idx.reverseLookup( 0L ) );
445         assertNull( idx.reverseLookup( 1L ) );
446 
447         // test add/drop without adding any duplicates
448         idx.add( "foo", 0L );
449         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
450         assertEquals( "foo", idx.reverseLookup( 0L ) );
451 
452         idx.drop( "foo", 0L );
453         assertNull( idx.forwardLookup( "foo" ) );
454         assertNull( idx.reverseLookup( 0L ) );
455 
456         // test add/drop with duplicates but one at a time
457         idx.add( "foo", 0L );
458         idx.add( "foo", 1L );
459         idx.add( "bar", 0L );
460         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
461         assertEquals( 0L, ( long ) idx.forwardLookup( "bar" ) );
462         assertEquals( "bar", idx.reverseLookup( 0L ) );
463         assertEquals( "foo", idx.reverseLookup( 1L ) );
464 
465         idx.drop( "bar", 0L );
466         assertEquals( 0L, ( long ) idx.forwardLookup( "foo" ) );
467         assertEquals( "foo", idx.reverseLookup( 0L ) );
468         assertEquals( "foo", idx.reverseLookup( 1L ) );
469         assertFalse( idx.forward( "bar", 0L ) );
470 
471         idx.drop( "foo", 0L );
472         assertEquals( 1L, ( long ) idx.forwardLookup( "foo" ) );
473         assertEquals( "foo", idx.reverseLookup( 1L ) );
474         assertFalse( idx.forward( "foo", 0L ) );
475 
476         idx.drop( "foo", 1L );
477         assertNull( idx.forwardLookup( "foo" ) );
478         assertNull( idx.forwardLookup( "bar" ) );
479         assertNull( idx.reverseLookup( 0L ) );
480         assertNull( idx.reverseLookup( 1L ) );
481         assertEquals( 0, idx.count() );
482     }
483 
484 
485     // -----------------------------------------------------------------------
486     // Miscellaneous Test Methods
487     // -----------------------------------------------------------------------
488 
489 
490     @Test
491     public void testCursors() throws Exception
492     {
493         initIndex();
494         assertEquals( 0, idx.count() );
495 
496         idx.add( "foo", 1234L );
497         assertEquals( 1, idx.count() );
498 
499         idx.add( "foo", 333L );
500         assertEquals( 2, idx.count() );
501 
502         idx.add( "bar", 555L );
503         assertEquals( 3, idx.count() );
504 
505         // use forward index's cursor
506         Cursor<IndexEntry<String,ServerEntry>> cursor = idx.forwardCursor();
507         cursor.beforeFirst();
508 
509         cursor.next();
510         IndexEntry<String,ServerEntry> e1 = cursor.get();
511         assertEquals( 555L, ( long ) e1.getId() );
512         assertEquals( "bar", e1.getValue() );
513 
514         cursor.next();
515         IndexEntry<String,ServerEntry> e2 = cursor.get();
516         assertEquals( 333L, ( long ) e2.getId() );
517         assertEquals( "foo", e2.getValue() );
518 
519         cursor.next();
520         IndexEntry<String,ServerEntry> e3 = cursor.get();
521         assertEquals( 1234L, ( long ) e3.getId() );
522         assertEquals( "foo", e3.getValue() );
523 
524         // use reverse index's cursor
525         cursor = idx.reverseCursor();
526         cursor.beforeFirst();
527 
528         cursor.next();
529         e1 = cursor.get();
530         assertEquals( 333L, ( long ) e1.getId() );
531         assertEquals( "foo", e1.getValue() );
532 
533         cursor.next();
534         e2 = cursor.get();
535         assertEquals( 555L, ( long ) e2.getId() );
536         assertEquals( "bar", e2.getValue() );
537 
538         cursor.next();
539         e3 = cursor.get();
540         assertEquals( 1234L, ( long ) e3.getId() );
541         assertEquals( "foo", e3.getValue() );
542     }
543 
544 
545     @Test
546     public void testNoEqualityMatching() throws Exception
547     {
548         JdbmIndex jdbmIndex = new JdbmIndex();
549 
550         try
551         {
552             jdbmIndex.init( new NoEqMatchAttribute(), dbFileDir );
553             fail( "should not get here" );
554         }
555         catch( IOException e )
556         {
557         }
558     }
559 
560 
561     // -----------------------------------------------------------------------
562     // Failing Tests
563     // -----------------------------------------------------------------------
564 
565 
566     @Test
567     public void testSingleValuedAttribute() throws Exception
568     {
569         JdbmIndex jdbmIndex = new JdbmIndex();
570         jdbmIndex.init( registry.lookup( SchemaConstants.CREATORS_NAME_AT ), dbFileDir );
571     }
572 }