All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.grinder.engine.process.TestData Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2000 Paco Gomez
// Copyright (C) 2000 - 2012 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

package net.grinder.engine.process;

import net.grinder.common.Test;
import net.grinder.common.UncheckedGrinderException;
import net.grinder.engine.common.EngineException;
import net.grinder.engine.process.DispatchContext.DispatchStateException;
import net.grinder.script.NonInstrumentableTypeException;
import net.grinder.script.NotWrappableTypeException;
import net.grinder.script.Statistics.StatisticsForTest;
import net.grinder.script.Test.InstrumentationFilter;
import net.grinder.script.TestRegistry.RegisteredTest;
import net.grinder.scriptengine.Instrumenter;
import net.grinder.scriptengine.Recorder;
import net.grinder.statistics.StatisticsSet;
import net.grinder.statistics.StatisticsSetFactory;
import net.grinder.util.TimeAuthority;

import org.slf4j.Marker;
import org.slf4j.MarkerFactory;


/**
 * Represents an individual test. Holds configuration information and
 * the tests statistics.
 *
 * Package scope.
 *
 * @author Philip Aston
 */
final class TestData implements RegisteredTest, Recorder {

  private final StatisticsSetFactory m_statisticsSetFactory;
  private final TestStatisticsHelper m_testStatisticsHelper;
  private final TimeAuthority m_timeAuthority;
  private final Instrumenter m_instrumenter;
  private final ThreadContextLocator m_threadContextLocator;
  private final Test m_test;
  private final Marker m_logMarker;

  /**
   * Cumulative statistics for our test that haven't yet been set to
   * the console.
   */
  private final StatisticsSet m_testStatistics;

  private final RecorderHolderThreadLocal m_recorderHolderTL =
    new RecorderHolderThreadLocal();

  TestData(ThreadContextLocator threadContextLocator,
           StatisticsSetFactory statisticsSetFactory,
           TestStatisticsHelper testStatisticsHelper,
           TimeAuthority timeAuthority,
           Instrumenter instrumenter,
           Test testDefinition) {
    m_statisticsSetFactory = statisticsSetFactory;
    m_testStatisticsHelper = testStatisticsHelper;
    m_timeAuthority = timeAuthority;
    m_instrumenter = instrumenter;
    m_threadContextLocator = threadContextLocator;
    m_test = testDefinition;
    m_testStatistics = m_statisticsSetFactory.create();

    m_logMarker = MarkerFactory.getMarker("test-" + testDefinition.getNumber());
  }

  Test getTest() {
    return m_test;
  }

  Marker getLogMarker() {
    return m_logMarker;
  }

  StatisticsSet getTestStatistics() {
    return m_testStatistics;
  }

  /**
   * {@inheritDoc}
   */
  @Override public Object createProxy(Object o)
    throws NotWrappableTypeException {
    return m_instrumenter.createInstrumentedProxy(getTest(), this, o);
  }

  /**
   * {@inheritDoc}
   */
  @Override public void instrument(Object target)
    throws NonInstrumentableTypeException {
    m_instrumenter.instrument(getTest(), this, target);
  }

  /**
   * {@inheritDoc}
   */
  @Override public void instrument(Object target,
                                   InstrumentationFilter filter)
    throws NonInstrumentableTypeException {
    m_instrumenter.instrument(getTest(), this, target, filter);
  }

  public void start() throws EngineException {
    m_recorderHolderTL.getHolder().start();
  }

  public void end(boolean success) throws EngineException {
    m_recorderHolderTL.getHolder().end(success);
  }

  /**
   * Thread local storage which keeps a {@link RecorderHolder} for each
   * worker thread that has ever used this test.
   */
  private final class RecorderHolderThreadLocal {
    private final ThreadLocal m_threadLocal =
      new ThreadLocal() {

      public RecorderHolder initialValue() {
        final ThreadContext threadContext = m_threadContextLocator.get();

        if (threadContext == null) {
          throw new UncheckedException("Only Worker Threads can invoke tests");
        }

        final TestRecorder recorder =
          new TestRecorder(threadContext.getDispatchResultReporter(),
                           new StopWatchImplementation(m_timeAuthority));

        return new RecorderHolder(threadContext, recorder);
      }
    };

    public RecorderHolder getHolder() throws EngineException {
      try {
        return m_threadLocal.get();
      }
      catch (UncheckedException e) {
        throw new EngineException(e.getMessage());
      }
    }
  }

  /**
   * Cache a single {@link TestRecorder} for a particular worker thread.
   *
   * 

* Ensure each worker thread ignores any nested methods instrumented with the * our Test. That is, if the thread makes nested calls to methods instrumented * with the same Test, the instrumentation is applied only at the outermost * stack frame, *

* *

* This prevents nested invocations for the same test/thread from being * recorded multiple times, making life simpler for the user, and for the * script engine instrumentation. *

*/ private static final class RecorderHolder implements Recorder { private final ThreadContext m_threadContext; private final TestRecorder m_recorder; private int m_nestingDepth = 0; public RecorderHolder(ThreadContext threadContext, TestRecorder recorder) { m_threadContext = threadContext; m_recorder = recorder; } public void start() throws DispatchStateException { if (m_nestingDepth++ == 0) { // Entering outer frame. m_threadContext.pushDispatchContext(m_recorder); m_recorder.start(); } } public void end(boolean success) { if (--m_nestingDepth == 0) { // Leaving outer frame. m_recorder.end(success); m_threadContext.popDispatchContext(); } } } /** * Three states: *
    *
  • initialised Start time is -1. Dispatch time is -1. * m_statisticsForTest is null.
  • *
  • dispatching Start time is valid. Dispatch time is -1. * m_statisticsForTest is not null.
  • *
  • complete Ready to report. Start time is valid. Dispatch * time is valid. m_statisticsForTest is null.
  • *
* * {@link ThreadContextImplementation#getDispatchContext()} takes care to only * return references to Dispatchers that are dispatching or * complete. */ private final class TestRecorder implements DispatchContext, Recorder { private final DispatchResultReporter m_resultReporter; private final StopWatch m_pauseTimer; private long m_startTime = -1; private long m_dispatchTime = -1; private StatisticsForTestImplementation m_statisticsForTest; public TestRecorder(DispatchResultReporter resultReporter, StopWatch pauseTimer) { m_resultReporter = resultReporter; m_pauseTimer = pauseTimer; } public void start() throws DispatchStateException { if (m_startTime != -1 || m_dispatchTime != -1) { throw new DispatchStateException("Last statistics were not reported"); } m_pauseTimer.reset(); m_statisticsForTest = new StatisticsForTestImplementation( this, m_testStatisticsHelper, m_statisticsSetFactory.create()); // Make it more likely that the timed section has a "clear run". Thread.yield(); m_startTime = m_timeAuthority.getTimeInMilliseconds(); } public void end(boolean success) { m_dispatchTime = Math.max(m_timeAuthority.getTimeInMilliseconds() - m_startTime, 0); if (m_pauseTimer.isRunning()) { m_pauseTimer.stop(); } if (!success && m_statisticsForTest != null) { // Always mark as an error if the test threw an exception. m_testStatisticsHelper.setSuccess( m_statisticsForTest.getStatistics(), false); // We don't log the exception. If the script doesn't handle the // exception it will be logged when the run is aborted, // otherwise we assume the script writer knows what they're // doing. } } public void report() throws DispatchStateException { if (m_dispatchTime < 0) { throw new DispatchStateException("No statistics to report"); } final StatisticsSet statistics = m_statisticsForTest.getStatistics(); m_testStatisticsHelper.recordTest(statistics, getElapsedTime()); m_resultReporter.report(getTest(), m_startTime, statistics); if (m_testStatisticsHelper.getSuccess(statistics)) { getTestStatistics().add(statistics); } else { // If an error, we consider other information to be unreliable, // so do not aggregate it. m_testStatisticsHelper.incrementErrors(getTestStatistics()); } // Disassociate ourselves from m_statisticsForTest; m_statisticsForTest.freeze(); m_statisticsForTest = null; m_startTime = -1; m_dispatchTime = -1; } public Test getTest() { return TestData.this.getTest(); } @Override public Marker getLogMarker() { return TestData.this.getLogMarker(); } public StopWatch getPauseTimer() { return m_pauseTimer; } public long getElapsedTime() { if (m_startTime == -1) { return -1; } final long unadjustedTime; if (m_dispatchTime == -1) { unadjustedTime = m_timeAuthority.getTimeInMilliseconds() - m_startTime; } else { unadjustedTime = m_dispatchTime; } return Math.max(unadjustedTime - m_pauseTimer.getTime(), 0); } public StatisticsForTest getStatisticsForTest() { return m_statisticsForTest; } public void setHasNestedContexts() { m_testStatistics.setIsComposite(); } } private static final class UncheckedException extends UncheckedGrinderException { public UncheckedException(String message) { super(message); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy