/* * 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) ; } } }