/**

Copyright (C) SYSTAP, LLC 2006-2015.  All rights reserved.

Contact:
     SYSTAP, LLC
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@systap.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 Oct 14, 2006
 */

package com.bigdata.rdf.sail.webapp;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import junit.extensions.proxy.IProxyTest;
import junit.framework.Test;

import org.openrdf.model.Graph;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;

import com.bigdata.journal.AbstractJournalTestCase;
import com.bigdata.journal.IIndexManager;

/**
 * <p>
 * This class provides proxy delegation logic for abstract methods declared by
 * {@link AbstractJournalTestCase} and is used to extend the set of tests that will be
 * applied to all implementations of the generic object model Java API. If you
 * want to test a new implementation, you MUST extend the
 * {@link AbstractJournalTestCase} instead and implement its abstract methods for your
 * implementation. This class provides an implementation neutral way to add new
 * tests, not a means for testing specific generic object model Java API
 * implementations.
 * </p>
 * <p>
 * In order to add new tests for the generic object model Java APIs, you extend
 * this class and write test methods.
 * </p>
 * 
 * @see AbstractJournalTestCase
 */
public abstract class ProxyTestCase<S extends IIndexManager>
    extends AbstractIndexManagerTestCase<S>
    implements IProxyTest
{

    public ProxyTestCase() {}
    public ProxyTestCase(String name){super(name);}
    
    //************************************************************
    //************************ IProxyTest ************************
    //************************************************************

    private AbstractIndexManagerTestCase<S> m_delegate = null;

    @SuppressWarnings("unchecked")
	@Override
    public void setDelegate(final Test delegate) {

        m_delegate = (AbstractIndexManagerTestCase<S>)delegate;

    }

    @Override
    public Test getDelegate() throws IllegalStateException {

        return m_delegate;

    }

    /**
     * Returns the delegate after first making sure that it is non-null and
     * extends {@link AbstractJournalTestCase}.
     */

    public AbstractIndexManagerTestCase<S> getOurDelegate() {

        if (m_delegate == null) {

            /*
             * This property gives the class name of the concrete instance of
             * AbstractTestSuite that we need to instantiate so that we can run
             * or debug a single test at a time! This is designed to support
             * running or debugging a single test that has failed after running
             * the entire test suite in an IDE such as Eclipse.
             * 
             * Note: We reach out to System.getProperty() and not
             * getProperties() to avoid infinite recursion through
             * getOurDelegate. The only way this makes sense anyway is when you
             * define -DtestClass=... as a JVM property.
             * 
             * @todo document.
             */
            final String testClass = System.getProperty("testClass");
            if (testClass == null) {

                throw new IllegalStateException(
                        "testClass: property not defined, could not configure delegate.");

            }
            try {
                final Class<?> cl = Class.forName(testClass);
                m_delegate = (AbstractIndexManagerTestCase<S>) cl.newInstance();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

            // throw new IllegalStateException
            // ( "The delegate is not configured."
            // );

        }

        if (m_delegate instanceof AbstractIndexManagerTestCase) {

            return (AbstractIndexManagerTestCase<S>) m_delegate;

        }

        throw new IllegalStateException("The delegate MUST extend "
                + AbstractIndexManagerTestCase.class.getName() + ", not "
                + m_delegate.getClass().getName());

    }

    //************************************************************
    //********************* proxied methods **********************
    //************************************************************

    /*
     * Note: All methods on the delegate MUST be proxied here or they will be
     * answered by our base class which is the same Class as the delegate, but
     * whose instance fields have not been initialized! (An instance of the
     * proxy is created for each test, while one instance of the delegate serves
     * an entire suite of tests.)
     */
    
    private int startupActiveThreads = 0;
    
    @Override
    public void setUp() throws Exception {

        startupActiveThreads = Thread.currentThread().getThreadGroup().activeCount();
    	
        getOurDelegate().setUp(this);
        
    }

    private static boolean s_checkThreads = true;

    @Override
    public void tearDown() throws Exception {

        getOurDelegate().tearDown(this);
        
        if (s_checkThreads) {

            final ThreadGroup grp = Thread.currentThread().getThreadGroup();
            final int tearDownActiveThreads = grp.activeCount();
            if (startupActiveThreads != tearDownActiveThreads) {
                final Thread[] threads = new Thread[tearDownActiveThreads];
                grp.enumerate(threads);
                final StringBuilder info = new StringBuilder();
                boolean first = true;
                for (Thread t : threads) {
                    if (t == null)
                        continue;
                    if (!first)
                        info.append(',');
                    info.append("[" + t.getName() + "]");
                    first = false;
                }

                final String failMessage = "Threads left active after task"
                        + ": test="
                        + getName()//
                        + ", delegate=" + getOurDelegate().getClass().getName()
                        + ", startupCount=" + startupActiveThreads
                        + ", teardownCount=" + tearDownActiveThreads
                        + ", thisThread=" + Thread.currentThread().getName()
                        + ", threads: " + info;

                if (grp.activeCount() != startupActiveThreads)
                    log.error(failMessage);

                /*
                 * Wait up to 2 seconds for threads to die off so the next test
                 * will run more cleanly.
                 */
                for (int i = 0; i < 20; i++) {
                    Thread.sleep(100);
                    if (grp.activeCount() != startupActiveThreads)
                        break;
                }

            }

        }

        super.tearDown();

    }

    @Override
    public Properties getProperties() {
        return getOurDelegate().getProperties();
    }

    @Override
    public S getIndexManager() {
        return getOurDelegate().getIndexManager();
    }

    /**
     * Count matches of the triple pattern.
     */
    static protected int countMatches(final Graph g, final Resource s,
            final URI p, final Value o) {

        int n = 0;

        final Iterator<Statement> itr = g.match(s, p, o);

        while (itr.hasNext()) {

            itr.next();
            
            n++;

        }

        return n;

    }

    /**
     * Return the statements matching the triple pattern.
     */
    static protected Statement[] getMatches(final Graph g, final Resource s,
            final URI p, final Value o) {

        final List<Statement> out = new LinkedList<Statement>();

        final Iterator<Statement> itr = g.match(s, p, o);

        while (itr.hasNext()) {

            out.add(itr.next());

        }

        return out.toArray(new Statement[out.size()]);

    }

}
