/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Sep 1, 2010 */ package com.bigdata.bop.engine; import java.util.Properties; import junit.framework.TestCase2; import com.bigdata.bop.IBindingSet; import com.bigdata.journal.BufferMode; import com.bigdata.journal.Journal; import com.bigdata.relation.accesspath.IAsynchronousIterator; import com.bigdata.relation.accesspath.ThickAsynchronousIterator; /** * Test suite for DISTINCT solution operators when integrated with the query * engine. This test suite is designed to examine cases where the DISTINCT * operator will have to buffer multiple chunks of solutions before finally * reporting the aggregated solutions. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id: TestQueryEngine2.java 3489 2010-09-01 18:27:35Z thompsonbry $ * * @todo Test each DISTINCT implementation here. * * FIXME Finish this stress test. We can allocate an array of known * distinct solutions and then randomly sample from the array to create * redundancy. Correct operation can be easily verified by checking that * the reported solutions are those in the array. */ public class TestQueryEngine_DistinctOp extends TestCase2 { @Override public Properties getProperties() { final Properties p = new Properties(super.getProperties()); p.setProperty(Journal.Options.BUFFER_MODE, BufferMode.Transient .toString()); return p; } private Journal jnl; private QueryEngine queryEngine; @Override public void setUp() throws Exception { jnl = new Journal(getProperties()); queryEngine = new QueryEngine(jnl); queryEngine.init(); } @Override public void tearDown() throws Exception { if (queryEngine != null) { queryEngine.shutdownNow(); queryEngine = null; } if (jnl != null) { jnl.destroy(); jnl = null; } } /** * */ public TestQueryEngine_DistinctOp() { } /** * @param name */ public TestQueryEngine_DistinctOp(String name) { super(name); } public void test_something_StressThreadSafe() throws Exception { for (int i = 0; i < 100; i++) { try { test_something_distinct_threadSafe(); } catch (Throwable t) { fail("Failed after " + i + " trials", t); } } } /** * @todo WRITE TEST : Unit test for DISTINCT. How to judge correctness? */ public void test_something_distinct_threadSafe() throws Exception { final long timeout = 10000; // ms final int ntrials = 10000; final int poolSize = 10; // doDistinctTest(10000/* maxInt */, timeout, ntrials, poolSize); } /** * Return an {@link IAsynchronousIterator} that will read a single, chunk * containing all of the specified {@link IBindingSet}s. * * @param bindingSetChunks * the chunks of binding sets. */ protected ThickAsynchronousIterator<IBindingSet[]> newBindingSetIterator( final IBindingSet[][] bindingSetChunks) { return new ThickAsynchronousIterator<IBindingSet[]>(bindingSetChunks); } // /** // * // * @param timeout // * @param ntrials // * @param poolSize // * // * @return The #of successful trials. // * // * @throws Exception // */ // protected void doDistinctTest(final int maxInt, // final long timeout, final int ntrials, final int poolSize) // throws Exception { // // fail("write test helper"); // // int ngiven = 0; // final IVariable<?> a = Var.var("a"); // final IBindingSet[][] chunks = new IBindingSet[ntrials][]; // { // final Random r = new Random(); // for (int i = 0; i < chunks.length; i++) { // // random non-zero chunk size // chunks[i] = new IBindingSet[r.nextInt(10) + 1]; // for (int j = 0; j < chunks[i].length; j++) { // final IBindingSet bset = new ListBindingSet(); // bset.set(a, new Constant<Integer>(r.nextInt(maxInt))); // chunks[i][j] = bset; // ngiven++; // } // } // } // // final int startId = 1; // final int sortId = 2; // // /* // * Note: The StartOp breaks up the initial set of chunks into multiple // * IChunkMessages, which results in multiple invocations of the SortOp. // */ // final PipelineOp startOp = new StartOp(new BOp[]{}, NV.asMap(new NV[]{// // new NV(SliceOp.Annotations.BOP_ID, startId),// // new NV(MemorySortOp.Annotations.EVALUATION_CONTEXT, // BOpEvaluationContext.CONTROLLER),// // })); // // final PipelineOp query = new MemorySortOp(new BOp[] {startOp}, NV.asMap(new NV[] {// // new NV(SliceOp.Annotations.BOP_ID, sortId),// // new NV(MemorySortOp.Annotations.COMPARATOR, // new IntegerComparatorOp( // new ISortOrder[] { new SortOrder(a, // true) })),// // new NV(MemorySortOp.Annotations.EVALUATION_CONTEXT, // BOpEvaluationContext.CONTROLLER),// // new NV(MemorySortOp.Annotations.PIPELINED, false),// // })); // // final UUID queryId = UUID.randomUUID(); // final IRunningQuery q = queryEngine.eval(queryId, query, // new LocalChunkMessage<IBindingSet>(queryEngine, queryId, // startId, -1/* partitionId */, // newBindingSetIterator(chunks))); // // // consume solutions. // int nsolutions = 0; // final IAsynchronousIterator<IBindingSet[]> itr = q.iterator(); // while (itr.hasNext()) { // nsolutions += itr.next().length; // } // // // wait for the query to terminate. // q.get(); // // // Verify stats. // final BOpStats stats = (BOpStats) q.getStats().get(sortId); // if (log.isInfoEnabled()) // log.info(getClass().getName() + "." + getName() + " : " + stats); // assertNotNull(stats); // assertEquals(ngiven, nsolutions); // assertEquals(ngiven, stats.unitsIn.get()); // assertEquals(ngiven, stats.unitsOut.get()); // // } // // /** // * Helper class for comparing solution sets having variables which evaluate // * to {@link Integer} values. // */ // static private class IntegerComparatorOp extends ComparatorOp // { // // /** // * // */ // private static final long serialVersionUID = 1L; // // /** The sort order. */ // final private ISortOrder<?> [] _sors; // // public IntegerComparatorOp ( final ISortOrder<?> sors [] ) // { // super ( new BOp [] {}, NV.asMap ( new NV [] { new NV ( ComparatorOp.Annotations.ORDER, sors ) } ) ) ; // _sors = sors ; // } // // public int compare ( IBindingSet o1, IBindingSet o2 ) // { // for ( ISortOrder<?> sor : _sors ) // { // int ret = compare ( sor, o1, o2 ) ; // if ( 0 != ret ) // return ret ; // } // return 0 ; // } // // private int compare ( ISortOrder<?> sor, IBindingSet lhs, IBindingSet rhs ) // { // int compare = 0 ; // // IConstant<?> lhsv = lhs.get ( sor.getVariable () ) ; // IConstant<?> rhsv = rhs.get ( sor.getVariable () ) ; // // if ( null == lhsv && null == rhsv ) // return 0 ; // else if ( null == lhsv ) // compare = -1 ; // else if ( null == rhsv ) // compare = 1 ; // else // compare = ((Integer) lhsv.get()).compareTo(((Integer) rhsv // .get())) ; // // return compare * ( sor.isAscending () ? 1 : -1 ) ; // } // // } }