blob: 195dd7b8218245b54fb7409152072c3b4bd51baf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.tdb.index;
import static java.lang.String.format ;
import static org.apache.jena.atlas.lib.ListUtils.asList ;
import static org.apache.jena.atlas.lib.ListUtils.unique ;
import static org.apache.jena.atlas.lib.RandomLib.random ;
import static org.apache.jena.atlas.test.Gen.permute ;
import static org.apache.jena.atlas.test.Gen.rand ;
import static org.apache.jena.atlas.test.Gen.strings ;
import static org.apache.jena.tdb.base.record.RecordLib.intToRecord ;
import static org.apache.jena.tdb.base.record.RecordLib.r ;
import static org.apache.jena.tdb.base.record.RecordLib.toIntList ;
import static org.junit.Assert.assertEquals ;
import static org.junit.Assert.assertNotNull ;
import static org.junit.Assert.fail ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.SortedSet ;
import java.util.TreeSet ;
import org.apache.jena.atlas.junit.BaseTest ;
import org.apache.jena.tdb.base.record.Record ;
import org.apache.jena.tdb.base.record.RecordLib ;
import org.apache.jena.tdb.index.Index ;
import org.apache.jena.tdb.index.RangeIndex ;
public class IndexTestLib
{
// ---------- Utilities
// public static RangeIndex buildRangeIndex(RangeIndexMaker maker, int[] keys)
// {
// RangeIndex index = maker.make() ;
// IndexTestLib.add(index, keys) ;
// return index ;
// }
public static Index buildIndex(IndexMaker maker, int[] keys)
{
Index index = maker.makeIndex() ;
IndexTestLib.add(index, keys) ;
return index ;
}
public static void testIteration(RangeIndex index, int[] keys, int numIterations)
{
// Shared across test-lets
SortedSet<Integer> x = new TreeSet<>() ;
for ( int v : keys )
x.add(v) ;
for ( int i = 0 ; i < numIterations ; i++ )
{
int lo = random.nextInt(keys.length) ;
int hi = random.nextInt(keys.length) ;
if ( lo > hi )
{
int t = lo ;
lo = hi ;
hi = t ;
}
// Does not consider nulls - assumed to be part of functional testing.
// Tweak lo and hi
if ( lo != 0 && random.nextFloat() < 0.5 )
lo-- ; // Negatives confuse the int/record code.
if ( random.nextFloat() < 0.5 )
hi++ ;
List<Integer> slice = r(index.iterator(r(lo), r(hi))) ;
List<Integer> expected = new ArrayList<>(keys.length) ;
for ( Integer ii : x.subSet(lo, hi) )
expected.add(ii) ;
assertEquals(format("(%d,%d)",lo, hi), expected, slice) ;
}
}
/* One random test : print the keys if there was a problem */
public static void randTest(Index index, int maxValue, int numKeys)
{
if ( numKeys >= 5000 )
System.err.printf("Warning: too many keys\n") ;
int[] keys1 = rand(numKeys, 0, maxValue) ;
int[] keys2 = permute(keys1) ;
try {
testInsert(index, keys1);
if ( true )
{
// Checking tests.
testIndexContents(index, keys2);
// Test iteration - quite expensive.
if ( index instanceof RangeIndex )
testIteration((RangeIndex)index, keys1, 10) ;
}
testDelete(index, keys2) ;
index.close() ;
} catch (RuntimeException ex)
{
System.err.printf("Index : %s\n", index.getClass().getName()) ;
System.err.printf("int[] keys1 = {%s} ;\n", strings(keys1)) ;
System.err.printf("int[] keys2 = {%s}; \n", strings(keys2)) ;
throw ex ;
}
}
// ---- Test utils
public static void testInsert(Index index, int[] keys)
{
IndexTestLib.add(index, keys) ;
testIndexContents(index, keys);
}
public static Index testInsert(IndexMaker maker, int[] keys)
{
Index index = maker.makeIndex() ;
testInsert(index, keys);
return index ;
}
public static void testInsertDelete(Index index, int[] buildKeys, int[] deleteKeys)
{
testInsert(index, buildKeys) ;
testDelete(index, deleteKeys) ;
}
public static void testDelete(Index index, int[] vals)
{
long size1 = index.size() ;
int count = 0 ;
count = delete(index, vals) ;
List<Record> x = intToRecord(vals, RecordLib.TestRecordLength) ;
for ( Record r : x )
{
boolean b = index.delete(r) ;
if ( b )
count ++ ;
}
for ( Record r : x )
BaseTest.assertFalse(index.contains(r)) ;
long size2 = index.size() ;
assertEquals(size1-count, size2) ;
}
public static int delete(Index index, int[] vals)
{
int count = 0 ;
for ( int v : vals )
{
boolean b = index.delete(r(v)) ;
if ( b )
count ++ ;
}
return count ;
}
public static void add(Index index, int[] vals)
{
//System.out.println("Add: "+Arrays.toString(vals)) ;
List<Record> x = intToRecord(vals, RecordLib.TestRecordLength) ;
for ( Record r : x )
{
//System.out.println(" Add: "+r) ;
index.add(r) ;
}
}
public static void testIndexContents(Index index, int[] records)
{
List<Integer> x = toIntList(index.iterator());
// Make a unique list of expected records. Remove duplicates
List<Integer> y = unique(asList(records)) ;
assertEquals("Expected records size and tree size different", y.size(), index.size()) ;
assertEquals("Expected records size and iteration over all keys are of different sizes", y.size(), x.size()) ;
if ( index instanceof RangeIndex )
{
// Check sorted order
for ( int i = 0 ; i < x.size()-2 ; i++ )
{
if ( x.get(i) > x.get(i+1) )
{
fail("check failed: "+strings(records)) ;
return ;
}
}
}
// Check each expected record is in the tree
for ( int k : y)
{
Record rec = intToRecord(k) ;
Record r2 = index.find(rec) ;
assertNotNull("Finding "+rec, r2) ;
}
}
}