Package com.eviware.soapui.impl.wsdl.loadtest.data

Source Code of com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics$SamplesHolder

/*
*  soapUI, copyright (C) 2004-2011 eviware.com
*
*  soapUI is free software; you can redistribute it and/or modify it under the
*  terms of version 2.1 of the GNU Lesser General Public License as published by
*  the Free Software Foundation.
*
*  soapUI 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 Lesser General Public License for more details at gnu.org.
*/

package com.eviware.soapui.impl.wsdl.loadtest.data;

import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.swing.table.AbstractTableModel;

import org.apache.log4j.Logger;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.loadtest.ColorPalette;
import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
import com.eviware.soapui.model.support.TestSuiteListenerAdapter;
import com.eviware.soapui.model.testsuite.LoadTestRunContext;
import com.eviware.soapui.model.testsuite.LoadTestRunner;
import com.eviware.soapui.model.testsuite.TestCase;
import com.eviware.soapui.model.testsuite.TestCaseRunContext;
import com.eviware.soapui.model.testsuite.TestCaseRunner;
import com.eviware.soapui.model.testsuite.TestRunner;
import com.eviware.soapui.model.testsuite.TestStep;
import com.eviware.soapui.model.testsuite.TestStepResult;
import com.eviware.soapui.support.types.StringList;

/**
* Model holding statistics.. should be refactored into interface for different
* statistic models
*
* @author Ole.Matzura
*/

public final class LoadTestStatistics extends AbstractTableModel implements Runnable
{
  public final static String NO_STATS_TESTCASE_CANCEL_REASON = "NO_STATS_TESTCASE_CANCEL_REASON";
  private final static Logger log = Logger.getLogger( LoadTestStatistics.class );

  private final WsdlLoadTest loadTest;
  private long[][] data;

  private final static int MIN_COLUMN = 0;
  private final static int MAX_COLUMN = 1;
  private final static int AVG_COLUMN = 2;
  private final static int LAST_COLUMN = 3;
  private final static int CNT_COLUMN = 4;
  private final static int TPS_COLUMN = 5;
  private final static int BYTES_COLUMN = 6;
  private final static int BPS_COLUMN = 7;
  private final static int ERR_COLUMN = 8;
  private final static int SUM_COLUMN = 9;
  private final static int CURRENT_CNT_COLUMN = 10;
  private final static int RATIO_COLUMN = 11;

  public static final int TOTAL = -1;

  public static final int DEFAULT_SAMPLE_INTERVAL = 250;

  private InternalTestRunListener testRunListener;
  private InternalTestSuiteListener testSuiteListener;
  private InternalPropertyChangeListener propertyChangeListener;

  private StatisticsHistory history;

  private boolean changed;
  private long updateFrequency = DEFAULT_SAMPLE_INTERVAL;
  private Queue<SamplesHolder> samplesStack = new ConcurrentLinkedQueue<SamplesHolder>();
  private long currentThreadCountStartTime;
  private long totalAverageSum;
  private boolean resetStatistics;
  private boolean running;
  private boolean adding;

  public LoadTestStatistics( WsdlLoadTest loadTest )
  {
    this.loadTest = loadTest;

    testRunListener = new InternalTestRunListener();
    testSuiteListener = new InternalTestSuiteListener();
    propertyChangeListener = new InternalPropertyChangeListener();

    WsdlTestCase testCase = loadTest.getTestCase();
    loadTest.addPropertyChangeListener( propertyChangeListener );
    loadTest.addLoadTestRunListener( testRunListener );
    testCase.getTestSuite().addTestSuiteListener( testSuiteListener );

    for( TestStep testStep : testCase.getTestStepList() )
    {
      testStep.addPropertyChangeListener( propertyChangeListener );
    }

    history = new StatisticsHistory( this );

    init();
  }

  private void init()
  {
    data = new long[getRowCount()][11];
  }

  public StatisticsHistory getHistory()
  {
    return history;
  }

  public int getRowCount()
  {
    return loadTest.getTestCase().getTestStepCount() + 1;
  }

  public WsdlLoadTest getLoadTest()
  {
    return loadTest;
  }

  public int getColumnCount()
  {
    return 12;
  }

  public String getColumnName( int columnIndex )
  {
    switch( columnIndex )
    {
    case 0 :
      return " ";
    case 1 :
      return "Test Step";
    case 2 :
      return Statistic.MININMUM.getName();
    case 3 :
      return Statistic.MAXIMUM.getName();
    case 4 :
      return Statistic.AVERAGE.getName();
    case 5 :
      return Statistic.LAST.getName();
    case 6 :
      return Statistic.COUNT.getName();
    case 7 :
      return Statistic.TPS.getName();
    case 8 :
      return Statistic.BYTES.getName();
    case 9 :
      return Statistic.BPS.getName();
    case 10 :
      return Statistic.ERRORS.getName();
    case 11 :
      return Statistic.ERRORRATIO.getName();
    }
    return null;
  }

  public Class<?> getColumnClass( int columnIndex )
  {
    switch( columnIndex )
    {
    case 0 :
      return Color.class;
    case 1 :
      return String.class;
    case 4 :
    case 7 :
      return Float.class;
    default :
      return Long.class;
    }
  }

  public boolean isCellEditable( int rowIndex, int columnIndex )
  {
    return false;
  }

  public long getStatistic( int stepIndex, Statistic statistic )
  {
    if( stepIndex == TOTAL )
      stepIndex = data.length - 1;

    switch( statistic )
    {
    case TPS :
    case AVERAGE :
      return data[stepIndex][statistic.getIndex()] / 100;
    case ERRORRATIO :
      return data[stepIndex][Statistic.COUNT.getIndex()] == 0 ? 0
          : ( long )( ( ( ( float )data[stepIndex][Statistic.ERRORS.getIndex()] / ( float )data[stepIndex][Statistic.COUNT
              .getIndex()] ) + 0.5 ) * 100 );
    default :
      return data[stepIndex][statistic.getIndex()];
    }
  }

  public Object getValueAt( int rowIndex, int columnIndex )
  {
    WsdlTestCase testCase = loadTest.getTestCase();

    switch( columnIndex )
    {
    case 0 :
      return rowIndex == testCase.getTestStepCount() ? null : ColorPalette.getColor( testCase
          .getTestStepAt( rowIndex ) );
    case 1 :
    {
      if( rowIndex == testCase.getTestStepCount() )
      {
        return "TestCase:";
      }
      else
      {
        return testCase.getTestStepAt( rowIndex ).getLabel();
      }
    }
    case 4 :
    case 7 :
      return new Float( ( float )data[rowIndex][columnIndex - 2] / 100 );
    case 11 :
      return data[rowIndex][Statistic.COUNT.getIndex()] == 0 ? 0
          : ( long )( ( ( float )data[rowIndex][Statistic.ERRORS.getIndex()] / ( float )data[rowIndex][Statistic.COUNT
              .getIndex()] ) * 100 );
    default :
    {
      return data == null || rowIndex >= data.length ? new Long( 0 ) : new Long( data[rowIndex][columnIndex - 2] );
    }
    }
  }

  public void pushSamples( long[] samples, long[] sizes, long[] sampleCounts, long startTime, long timeTaken,
      boolean complete )
  {
    if( !running || samples.length == 0 || sizes.length == 0 )
      return;

    samplesStack.add( new SamplesHolder( samples, sizes, sampleCounts, startTime, timeTaken, complete ) );
  }

  public void run()
  {
    Thread.currentThread().setName( loadTest.getName() + " LoadTestStatistics" );

    while( running || !samplesStack.isEmpty() )
    {
      try
      {
        while( !samplesStack.isEmpty() )
        {
          SamplesHolder holder = samplesStack.poll();
          if( holder != null )
            addSamples( holder );
        }

        Thread.sleep( 200 );
      }
      catch( EmptyStackException e )
      {
      }
      catch( Exception e )
      {
        SoapUI.logError( e );
      }
    }
  }

  private synchronized void addSamples( SamplesHolder holder )
  {
    if( adding )
      throw new RuntimeException( "Already adding!" );

    adding = true;

    int totalIndex = data.length - 1;
    if( holder.samples.length != totalIndex || holder.sizes.length != totalIndex )
    {
      adding = false;
      throw new RuntimeException( "Unexpected number of samples: " + holder.samples.length + ", exptected "
          + ( totalIndex ) );
    }

    // discard "old" results
    if( holder.startTime < currentThreadCountStartTime )
    {
      adding = false;
      return;
    }

    // first check that this is not a
    long timePassed = ( holder.startTime + holder.timeTaken ) - currentThreadCountStartTime;

    if( resetStatistics )
    {
      for( int c = 0; c < data.length; c++ )
      {
        data[c][CURRENT_CNT_COLUMN] = 0;
        data[c][AVG_COLUMN] = 0;
        data[c][SUM_COLUMN] = 0;
        data[c][TPS_COLUMN] = 0;
        data[c][BYTES_COLUMN] = 0;
      }

      totalAverageSum = 0;
      resetStatistics = false;
    }

    long totalMin = 0;
    long totalMax = 0;
    long totalBytes = 0;
    long totalAvg = 0;
    long totalSum = 0;
    long totalLast = 0;

    long threadCount = loadTest.getThreadCount();

    for( int c = 0; c < holder.samples.length; c++ )
    {
      if( holder.sampleCounts[c] > 0 )
      {
        // only update when appropriate
        if( holder.complete != loadTest.getUpdateStatisticsPerTestStep() )
        {
          long sampleAvg = holder.samples[c] / holder.sampleCounts[c];

          data[c][LAST_COLUMN] = sampleAvg;
          data[c][CNT_COLUMN] += holder.sampleCounts[c];
          data[c][CURRENT_CNT_COLUMN] += holder.sampleCounts[c];
          data[c][SUM_COLUMN] += holder.samples[c];

          if( sampleAvg > 0 && ( sampleAvg < data[c][MIN_COLUMN] || data[c][MIN_COLUMN] == 0 ) )
            data[c][MIN_COLUMN] = sampleAvg;

          if( sampleAvg > data[c][MAX_COLUMN] )
            data[c][MAX_COLUMN] = sampleAvg;

          float average = ( float )data[c][SUM_COLUMN] / ( float )data[c][CURRENT_CNT_COLUMN];

          data[c][AVG_COLUMN] = ( long )( average * 100 );
          data[c][BYTES_COLUMN] += holder.sizes[c];

          if( timePassed > 0 )
          {
            if( loadTest.getCalculateTPSOnTimePassed() )
            {
              data[c][TPS_COLUMN] = ( data[c][CURRENT_CNT_COLUMN] * 100000 ) / timePassed;
              data[c][BPS_COLUMN] = ( data[c][BYTES_COLUMN] * 1000 ) / timePassed;
            }
            else
            {
              data[c][TPS_COLUMN] = ( long )( data[c][AVG_COLUMN] > 0 ? ( 100000F / average ) * threadCount : 0 );

              long avgBytes = data[c][CNT_COLUMN] == 0 ? 0 : data[c][BYTES_COLUMN] / data[c][CNT_COLUMN];
              data[c][BPS_COLUMN] = ( avgBytes * data[c][TPS_COLUMN] ) / 100;
            }
          }
        }

        totalMin += data[c][MIN_COLUMN] * holder.sampleCounts[c];
        totalMax += data[c][MAX_COLUMN] * holder.sampleCounts[c];
        totalBytes += data[c][BYTES_COLUMN] * holder.sampleCounts[c];
        totalAvg += data[c][AVG_COLUMN] * holder.sampleCounts[c];
        totalSum += data[c][SUM_COLUMN] * holder.sampleCounts[c];
        totalLast += data[c][LAST_COLUMN] * holder.sampleCounts[c];
      }
      else
      {
        totalMin += data[c][MIN_COLUMN];
        totalMax += data[c][MAX_COLUMN];
        totalBytes += data[c][BYTES_COLUMN];
      }
    }

    if( holder.complete )
    {
      data[totalIndex][CNT_COLUMN]++ ;
      data[totalIndex][CURRENT_CNT_COLUMN]++ ;

      totalAverageSum += totalLast * 100;
      data[totalIndex][AVG_COLUMN] = ( long )( ( float )totalAverageSum / ( float )data[totalIndex][CURRENT_CNT_COLUMN] );
      data[totalIndex][BYTES_COLUMN] = totalBytes;

      if( timePassed > 0 )
      {
        if( loadTest.getCalculateTPSOnTimePassed() )
        {
          data[totalIndex][TPS_COLUMN] = ( data[totalIndex][CURRENT_CNT_COLUMN] * 100000 ) / timePassed;
          data[totalIndex][BPS_COLUMN] = ( data[totalIndex][BYTES_COLUMN] * 1000 ) / timePassed;
        }
        else
        {
          data[totalIndex][TPS_COLUMN] = ( long )( data[totalIndex][AVG_COLUMN] > 0 ? ( 10000000F / data[totalIndex][AVG_COLUMN] )
              * threadCount
              : 0 );

          long avgBytes = data[totalIndex][CNT_COLUMN] == 0 ? 0 : data[totalIndex][BYTES_COLUMN]
              / data[totalIndex][CNT_COLUMN];

          data[totalIndex][BPS_COLUMN] = ( avgBytes * data[totalIndex][TPS_COLUMN] ) / 100;
        }
      }

      data[totalIndex][MIN_COLUMN] = totalMin;
      data[totalIndex][MAX_COLUMN] = totalMax;
      data[totalIndex][SUM_COLUMN] = totalSum;
      data[totalIndex][LAST_COLUMN] = totalLast;
    }

    if( updateFrequency == 0 )
      fireTableDataChanged();
    else
      changed = true;

    adding = false;
  }

  private final class Updater implements Runnable
  {
    public void run()
    {
      Thread.currentThread().setName( loadTest.getName() + " LoadTestStatistics Updater" );

      // check all these for catching threading issues
      while( running || changed || !samplesStack.isEmpty() )
      {
        if( changed )
        {
          fireTableDataChanged();
          changed = false;
        }

        if( !running && samplesStack.isEmpty() )
          break;

        try
        {
          Thread.sleep( updateFrequency < 1 ? 1000 : updateFrequency );
        }
        catch( InterruptedException e )
        {
          SoapUI.logError( e );
        }
      }
    }
  }

  private void stop()
  {
    running = false;
  }

  /**
   * Collect testresult samples
   *
   * @author Ole.Matzura
   */

  private class InternalTestRunListener extends LoadTestRunListenerAdapter
  {
    public void beforeLoadTest( LoadTestRunner loadTestRunner, LoadTestRunContext context )
    {
      samplesStack.clear();

      running = true;
      SoapUI.getThreadPool().submit( updater );
      SoapUI.getThreadPool().submit( LoadTestStatistics.this );

      currentThreadCountStartTime = System.currentTimeMillis();
      totalAverageSum = 0;
    }

    @Override
    public void afterTestStep( LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
        TestCaseRunContext runContext, TestStepResult testStepResult )
    {
      if( loadTest.getUpdateStatisticsPerTestStep() )
      {
        TestCase testCase = testRunner.getTestCase();

        if( testStepResult == null )
        {
          log.warn( "Result is null in TestCase [" + testCase.getName() + "]" );
          return;
        }

        long[] samples = new long[testCase.getTestStepCount()];
        long[] sizes = new long[samples.length];
        long[] sampleCounts = new long[samples.length];

        int index = testCase.getIndexOfTestStep( testStepResult.getTestStep() );
        sampleCounts[index]++ ;

        samples[index] += testStepResult.getTimeTaken();
        sizes[index] += testStepResult.getSize();

        pushSamples( samples, sizes, sampleCounts, testRunner.getStartTime(), testRunner.getTimeTaken(), false );
      }
    }

    public void afterTestCase( LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
        TestCaseRunContext runContext )
    {
      if( testRunner.getStatus() == TestRunner.Status.CANCELED
          && testRunner.getReason().equals( NO_STATS_TESTCASE_CANCEL_REASON ) )
        return;

      List<TestStepResult> results = testRunner.getResults();
      TestCase testCase = testRunner.getTestCase();

      long[] samples = new long[testCase.getTestStepCount()];
      long[] sizes = new long[samples.length];
      long[] sampleCounts = new long[samples.length];

      for( int c = 0; c < results.size(); c++ )
      {
        TestStepResult testStepResult = results.get( c );
        if( testStepResult == null )
        {
          log.warn( "Result [" + c + "] is null in TestCase [" + testCase.getName() + "]" );
          continue;
        }

        int index = testCase.getIndexOfTestStep( testStepResult.getTestStep() );
        if( index >= 0 )
        {
        sampleCounts[index]++ ;

        samples[index] += testStepResult.getTimeTaken();
        sizes[index] += testStepResult.getSize();
      }
      }

      pushSamples( samples, sizes, sampleCounts, testRunner.getStartTime(), testRunner.getTimeTaken(), true );
    }

    @Override
    public void afterLoadTest( LoadTestRunner loadTestRunner, LoadTestRunContext context )
    {
      stop();
    }
  }

  public int getStepCount()
  {
    return loadTest.getTestCase().getTestStepCount();
  }

  public void reset()
  {
    init();
    fireTableDataChanged();
  }

  public void release()
  {
    reset();

    loadTest.removeLoadTestRunListener( testRunListener );
    loadTest.getTestCase().getTestSuite().removeTestSuiteListener( testSuiteListener );

    for( TestStep testStep : loadTest.getTestCase().getTestStepList() )
    {
      testStep.removePropertyChangeListener( propertyChangeListener );
    }
  }

  private class InternalTestSuiteListener extends TestSuiteListenerAdapter
  {
    public void testStepAdded( TestStep testStep, int index )
    {
      if( testStep.getTestCase() == loadTest.getTestCase() )
      {
        init();
        testStep.addPropertyChangeListener( TestStep.NAME_PROPERTY, propertyChangeListener );
        fireTableDataChanged();

        history.reset();
      }
    }

    public void testStepRemoved( TestStep testStep, int index )
    {
      if( testStep.getTestCase() == loadTest.getTestCase() )
      {
        init();
        testStep.removePropertyChangeListener( propertyChangeListener );
        fireTableDataChanged();

        history.reset();
      }
    }
  }

  private class InternalPropertyChangeListener implements PropertyChangeListener
  {
    public void propertyChange( PropertyChangeEvent evt )
    {
      if( evt.getSource() == loadTest && evt.getPropertyName().equals( WsdlLoadTest.THREADCOUNT_PROPERTY ) )
      {
        if( loadTest.getResetStatisticsOnThreadCountChange() )
        {
          resetStatistics = true;
          currentThreadCountStartTime = System.currentTimeMillis();
        }
      }
      else if( evt.getPropertyName().equals( TestStep.NAME_PROPERTY )
          || evt.getPropertyName().equals( TestStep.DISABLED_PROPERTY ) )
      {
        if( evt.getSource() instanceof TestStep )
          fireTableCellUpdated( loadTest.getTestCase().getIndexOfTestStep( ( TestStep )evt.getSource() ), 1 );
      }
      else if( evt.getPropertyName().equals( WsdlLoadTest.HISTORYLIMIT_PROPERTY ) )
      {
        if( loadTest.getHistoryLimit() == 0 )
          history.reset();
      }
    }
  }

  public TestStep getTestStepAtRow( int selectedRow )
  {
    if( selectedRow < getRowCount() - 1 )
      return loadTest.getTestCase().getTestStepAt( selectedRow );
    else
      return null;
  }

  public long getUpdateFrequency()
  {
    return updateFrequency;
  }

  public void setUpdateFrequency( long updateFrequency )
  {
    this.updateFrequency = updateFrequency;
  }

  public void addError( int stepIndex )
  {
    if( stepIndex != -1 )
    {
      data[stepIndex][ERR_COLUMN]++ ;
    }

    data[data.length - 1][ERR_COLUMN]++ ;
    changed = true;
  }

  public synchronized StringList[] getSnapshot()
  {
    long[][] clone = data.clone();

    StringList[] result = new StringList[getRowCount()];

    for( int c = 0; c < clone.length; c++ )
    {
      StringList values = new StringList();

      for( int columnIndex = 2; columnIndex < getColumnCount(); columnIndex++ )
      {
        switch( columnIndex )
        {
        case 4 :
        case 7 :
          values.add( String.valueOf( ( float )data[c][columnIndex - 2] / 100 ) );
          break;
        default :
          values.add( String.valueOf( data[c][columnIndex - 2] ) );
        }
      }

      result[c] = values;
    }

    return result;
  }

  private final static Map<Integer, Statistic> statisticIndexMap = new HashMap<Integer, Statistic>();

  private Updater updater = new Updater();

  public enum Statistic
  {
    MININMUM( MIN_COLUMN, "min", "the minimum measured teststep time" ), MAXIMUM( MAX_COLUMN, "max",
        "the maximum measured testste time" ), AVERAGE( AVG_COLUMN, "avg", "the average measured teststep time" ), LAST(
        LAST_COLUMN, "last", "the last measured teststep time" ), COUNT( CNT_COLUMN, "cnt",
        "the number of teststep samples measured" ), TPS( TPS_COLUMN, "tps",
        "the number of transactions per second for this teststep" ), BYTES( BYTES_COLUMN, "bytes",
        "the total number of bytes returned by this teststep" ), BPS( BPS_COLUMN, "bps",
        "the number of bytes per second returned by this teststep" ), ERRORS( ERR_COLUMN, "err",
        "the total number of assertion errors for this teststep" ), SUM( SUM_COLUMN, "sum", "internal sum" ), CURRENT_CNT(
        CURRENT_CNT_COLUMN, "ccnt", "internal cnt" ), ERRORRATIO( RATIO_COLUMN, "rat",
        "the ratio between exections and failures" );

    private final String description;
    private final String name;
    private final int index;

    Statistic( int index, String name, String description )
    {
      this.index = index;
      this.name = name;
      this.description = description;

      statisticIndexMap.put( index, this );
    }

    public String getDescription()
    {
      return description;
    }

    public int getIndex()
    {
      return index;
    }

    public String getName()
    {
      return name;
    }

    public static Statistic forIndex( int column )
    {
      return statisticIndexMap.get( column );
    }
  }

  /**
   * Holds all sample values for a testcase run
   *
   * @author ole.matzura
   */

  private static final class SamplesHolder
  {
    private final long[] samples;
    private final long[] sizes;
    private final long[] sampleCounts;

    private final long startTime;
    private final long timeTaken;
    private final boolean complete;

    public SamplesHolder( long[] samples, long[] sizes, long[] sampleCounts, long startTime, long timeTaken,
        boolean complete )
    {
      this.samples = samples;
      this.sizes = sizes;
      this.startTime = startTime;
      this.timeTaken = timeTaken;
      this.sampleCounts = sampleCounts;
      this.complete = complete;
    }
  }

  public synchronized void finish()
  {
    // push leftover samples
    while( !samplesStack.isEmpty() )
    {
      SamplesHolder holder = samplesStack.poll();
      if( holder != null )
        addSamples( holder );
    }
  }
}
TOP

Related Classes of com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics$SamplesHolder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.