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

Source Code of com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTestRunner$TestCaseStarter

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

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.xmlbeans.XmlException;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.LoadTestConfig;
import com.eviware.soapui.config.LoadTestLimitTypesConfig;
import com.eviware.soapui.config.SecurityTestConfig;
import com.eviware.soapui.config.TestCaseConfig;
import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLogMessageEntry;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCaseRunner;
import com.eviware.soapui.model.settings.Settings;
import com.eviware.soapui.model.support.TestRunListenerAdapter;
import com.eviware.soapui.model.testsuite.LoadTestRunListener;
import com.eviware.soapui.model.testsuite.LoadTestRunner;
import com.eviware.soapui.model.testsuite.TestCaseRunContext;
import com.eviware.soapui.model.testsuite.TestCaseRunner;
import com.eviware.soapui.model.testsuite.TestRunContext;
import com.eviware.soapui.model.testsuite.TestRunnable;
import com.eviware.soapui.model.testsuite.TestStep;
import com.eviware.soapui.model.testsuite.TestStepResult;
import com.eviware.soapui.settings.HttpSettings;
import com.eviware.soapui.settings.WsdlSettings;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.types.StringToObjectMap;
import com.eviware.x.dialogs.Worker;
import com.eviware.x.dialogs.XProgressDialog;
import com.eviware.x.dialogs.XProgressMonitor;

/**
* TestRunner for load-tests.
*
* @author Ole.Matzura
* @todo statistics should be calculated first after all threads have been
*       started..
*/

public class WsdlLoadTestRunner implements LoadTestRunner
{
  private final WsdlLoadTest loadTest;
  private Set<InternalTestCaseRunner> runners = new HashSet<InternalTestCaseRunner>();
  private long startTime = 0;
  private InternalPropertyChangeListener internalPropertyChangeListener = new InternalPropertyChangeListener();
  private InternalTestRunListener testRunListener = new InternalTestRunListener();
  private long runCount;
  private Status status;
  private WsdlLoadTestContext context;
  private String reason;
  private int threadCount;
  private int threadsWaitingToStart;
  private int startedCount;
  private boolean hasTearedDown;
  private TestCaseStarter testCaseStarter;
  private boolean stopped;
  private TestCaseConfig blueprintConfig;

  public WsdlLoadTestRunner( WsdlLoadTest test )
  {
    this.loadTest = test;
    status = Status.INITIALIZED;
  }

  public Status getStatus()
  {
    return status;
  }

  void start()
  {
    loadTest.getTestCase().beforeSave();

    runners.clear();
    runCount = 0;
    threadCount = 0;
    threadsWaitingToStart = 0;
    startedCount = 0;
    context = new WsdlLoadTestContext( this );

    try
    {
      loadTest.runSetupScript( context, this );
    }
    catch( Exception e1 )
    {
      SoapUI.logError( e1 );
    }

    for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
    {
      try
      {
        listener.beforeLoadTest( this, context );
      }
      catch( Throwable e )
      {
        SoapUI.logError( e );
      }
    }

    status = Status.RUNNING;

    loadTest.addPropertyChangeListener( WsdlLoadTest.THREADCOUNT_PROPERTY, internalPropertyChangeListener );

    XProgressDialog progressDialog = UISupport.getDialogs().createProgressDialog( "Starting threads",
        ( int )loadTest.getThreadCount(), "", true );
    try
    {
      testCaseStarter = new TestCaseStarter();
      progressDialog.run( testCaseStarter );
    }
    catch( Exception e )
    {
      SoapUI.logError( e );
    }

    if( status == Status.RUNNING )
    {
      for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
      {
        listener.loadTestStarted( this, context );
      }

      startStrategyThread();
    }
    else
    {
      stop();
    }
  }

  /**
   * Starts thread the calls the current strategy to recalculate
   */

  private void startStrategyThread()
  {
    new Thread( new Runnable()
    {
      public void run()
      {
        while( getStatus() == Status.RUNNING )
        {
          try
          {
            loadTest.getLoadStrategy().recalculate( WsdlLoadTestRunner.this, context );

            long strategyInterval = loadTest.getStrategyInterval();
            if( strategyInterval < 1 )
              strategyInterval = WsdlLoadTest.DEFAULT_STRATEGY_INTERVAL;

            Thread.sleep( strategyInterval );
          }
          catch( InterruptedException e )
          {
            e.printStackTrace();
          }
        }
      }
    } ).start();
  }

  private InternalTestCaseRunner startTestCase( WsdlTestCase testCase )
  {
    InternalTestCaseRunner testCaseRunner = new InternalTestCaseRunner( testCase, threadCount++ );

    SoapUI.getThreadPool().submit( testCaseRunner );
    runners.add( testCaseRunner );
    return testCaseRunner;
  }

  public synchronized void cancel( String reason )
  {
    if( status != Status.RUNNING )
      return;

    this.reason = reason;
    status = Status.CANCELED;

    if( testCaseStarter != null )
      testCaseStarter.stop();

    InternalTestCaseRunner[] r = runners.toArray( new InternalTestCaseRunner[runners.size()] );

    for( InternalTestCaseRunner runner : r )
    {
      runner.cancel( reason, true );
    }

    String msg = "LoadTest [" + loadTest.getName() + "] canceled";
    if( reason != null )
      msg += "; " + reason;

    loadTest.getLoadTestLog().addEntry( new LoadTestLogMessageEntry( msg ) );

    for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
    {
      listener.loadTestStopped( this, context );
    }

    if( r.length == 0 )
      stop();
  }

  public synchronized void fail( String reason )
  {
    boolean wasRunning = ( status == Status.RUNNING );

    this.reason = reason;
    status = Status.FAILED;

    if( !wasRunning )
      return;

    if( testCaseStarter != null )
      testCaseStarter.stop();

    String msg = "LoadTest [" + loadTest.getName() + "] failed";
    if( reason != null )
      msg += "; " + reason;

    loadTest.getLoadTestLog().addEntry( new LoadTestLogMessageEntry( msg ) );

    for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
    {
      try
      {
        listener.loadTestStopped( this, context );
      }
      catch( Throwable e )
      {
        SoapUI.logError( e );
      }
    }

    InternalTestCaseRunner[] r = runners.toArray( new InternalTestCaseRunner[runners.size()] );

    for( InternalTestCaseRunner runner : r )
    {
      runner.cancel( reason, true );
    }

    if( r.length == 0 )
      stop();
  }

  private synchronized void tearDown()
  {
    if( hasTearedDown )
      return;

    try
    {
      loadTest.runTearDownScript( context, this );
    }
    catch( Exception e1 )
    {
      SoapUI.logError( e1 );
    }

    hasTearedDown = true;
  }

  public Status waitUntilFinished()
  {
    while( runners.size() > 0 || threadsWaitingToStart > 0 || !hasStopped() )
    {
      try
      {
        Thread.sleep( 200 );
      }
      catch( InterruptedException e )
      {
        SoapUI.logError( e );
      }
    }

    return getStatus();
  }

  public void finishTestCase( String reason, WsdlTestCase testCase )
  {
    for( InternalTestCaseRunner runner : runners )
    {
      if( runner.getTestCase() == testCase )
      {
        runner.cancel( reason, false );
        break;
      }
    }
  }

  public synchronized void finishRunner( InternalTestCaseRunner runner )
  {
    if( !runners.contains( runner ) )
    {
      throw new RuntimeException( "Trying to finish unknown runner.. " );
    }

    runners.remove( runner );

    if( getProgress() >= 1 || status != Status.RUNNING )
    {
      stop();
    }
  }

  private synchronized void stop()
  {
    if( stopped )
      return;

    loadTest.removePropertyChangeListener( WsdlLoadTest.THREADCOUNT_PROPERTY, internalPropertyChangeListener );

    if( testCaseStarter != null )
      testCaseStarter.stop();

    if( status == Status.RUNNING )
      status = Status.FINISHED;

    loadTest.getLoadTestLog().addEntry(
        new LoadTestLogMessageEntry( "LoadTest ended at " + new Date( System.currentTimeMillis() ) ) );

    try
    {
      tearDown();
    }
    catch( Throwable e )
    {
      SoapUI.logError( e );
    }

    for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
    {
      try
      {
        listener.afterLoadTest( this, context );
      }
      catch( Throwable e )
      {
        SoapUI.logError( e );
      }
    }

    context.clear();
    stopped = true;
    blueprintConfig = null;
  }

  public boolean hasStopped()
  {
    return stopped;
  }

  public int getRunningThreadCount()
  {
    return runners.size();
  }

  public float getProgress()
  {
    long testLimit = loadTest.getTestLimit();
    if( testLimit == 0 )
      return -1;

    if( loadTest.getLimitType() == LoadTestLimitTypesConfig.COUNT )
      return ( float )runCount / ( float )testLimit;

    if( loadTest.getLimitType() == LoadTestLimitTypesConfig.COUNT_PER_THREAD )
      return ( float )runCount / ( float )( testLimit * loadTest.getThreadCount() );

    if( loadTest.getLimitType() == LoadTestLimitTypesConfig.TIME )
      return startTime == 0 ? 0 : ( float )getTimeTaken() / ( float )( testLimit * 1000 );

    return -1;
  }

  private synchronized boolean afterRun( InternalTestCaseRunner runner )
  {
    if( status != Status.RUNNING )
      return false;

    runCount++ ;

    if( loadTest.getTestLimit() < 1 )
      return true;

    if( loadTest.getLimitType() == LoadTestLimitTypesConfig.COUNT_PER_THREAD )
      return runner.getRunCount() < loadTest.getTestLimit();

    if( loadTest.getLimitType() == LoadTestLimitTypesConfig.COUNT )
      return runCount + runners.size() + threadsWaitingToStart <= loadTest.getTestLimit();

    if( loadTest.getLimitType() == LoadTestLimitTypesConfig.TIME )
      return getTimeTaken() < loadTest.getTestLimit() * 1000;

    return true;
  }

  private final class TestCaseStarter extends Worker.WorkerAdapter
  {
    private List<WsdlTestCase> testCases = new ArrayList<WsdlTestCase>();
    private boolean canceled;

    public Object construct( XProgressMonitor monitor )
    {
      int startDelay = loadTest.getStartDelay();

      for( int c = 0; c < loadTest.getThreadCount() && !canceled; c++ )
      {
        monitor.setProgress( 1, "Creating Virtual User " + ( c + 1 ) );
        testCases.add( createTestCase() );
      }

      startTime = System.currentTimeMillis();

      if( canceled )
      {
        loadTest.getLoadTestLog().addEntry(
            new LoadTestLogMessageEntry( "LoadTest canceled during startup at " + new Date( startTime ) ) );
        return null;
      }

      loadTest.getLoadTestLog().addEntry(
          new LoadTestLogMessageEntry( "LoadTest started at " + new Date( startTime ) ) );

      threadsWaitingToStart = testCases.size();
      int cnt = 0;
      while( !testCases.isEmpty() && !canceled )
      {
        if( startDelay > 0 )
        {
          try
          {
            Thread.sleep( startDelay );
          }
          catch( InterruptedException e )
          {
            SoapUI.logError( e );
          }
        }

        if( status != Status.RUNNING || getProgress() >= 1 )
        {
          while( !testCases.isEmpty() )
            testCases.remove( 0 ).release();

          threadsWaitingToStart = 0;
          break;
        }

        // could have been canceled..
        if( !testCases.isEmpty() )
        {
          startTestCase( testCases.remove( 0 ) );
          monitor.setProgress( 1, "Started thread " + ( ++cnt ) );
          threadsWaitingToStart-- ;
        }
      }

      return null;
    }

    public boolean onCancel()
    {
      cancel( "Stopped from UI during start-up" );
      stop();

      return false;
    }

    public void stop()
    {
      if( !canceled )
      {
        canceled = true;
        while( !testCases.isEmpty() )
          testCases.remove( 0 ).release();

        threadsWaitingToStart = 0;
      }
    }
  }

  public class InternalTestCaseRunner implements Runnable
  {
    private final WsdlTestCase testCase;
    private boolean canceled;
    private long runCount;
    private WsdlTestCaseRunner runner;
    private final int threadIndex;

    public InternalTestCaseRunner( WsdlTestCase testCase, int threadIndex )
    {
      this.testCase = testCase;
      this.threadIndex = threadIndex;
    }

    public void run()
    {
      try
      {
        if( System.getProperty( "soapui.enablenamedthreads" ) != null )
          Thread.currentThread().setName(
              testCase.getName() + " " + loadTest.getName() + " ThreadIndex = " + threadIndex );

        runner = new WsdlTestCaseRunner( testCase, new StringToObjectMap() );

        while( !canceled )
        {
          try
          {
            runner.getRunContext().reset();
            runner.getRunContext().setProperty( TestCaseRunContext.THREAD_INDEX, threadIndex );
            runner.getRunContext().setProperty( TestCaseRunContext.RUN_COUNT, runCount );
            runner.getRunContext().setProperty( TestCaseRunContext.LOAD_TEST_RUNNER, WsdlLoadTestRunner.this );
            runner.getRunContext().setProperty( TestCaseRunContext.LOAD_TEST_CONTEXT, context );
            synchronized( this )
            {
              runner.getRunContext().setProperty( TestCaseRunContext.TOTAL_RUN_COUNT, startedCount++ );
            }

            runner.run();
          }
          catch( Throwable e )
          {
            System.err.println( "Error running testcase: " + e );
            SoapUI.logError( e );
          }

          runCount++ ;

          if( !afterRun( this ) )
            break;
        }
      }
      finally
      {
        finishRunner( this );
        testCase.release();
        testCase.removeTestRunListener( testRunListener );
      }
    }

    public void cancel( String reason, boolean cancelRunner )
    {
      if( runner != null && cancelRunner )
        runner.cancel( reason );

      this.canceled = true;
    }

    public boolean isCanceled()
    {
      return canceled;
    }

    public long getRunCount()
    {
      return runCount;
    }

    public WsdlTestCase getTestCase()
    {
      return testCase;
    }
  }

  public WsdlLoadTest getLoadTest()
  {
    return loadTest;
  }

  public class InternalPropertyChangeListener implements PropertyChangeListener
  {
    public void propertyChange( PropertyChangeEvent evt )
    {
      updateThreadCount();
    }
  }

  public synchronized void updateThreadCount()
  {
    if( status != Status.RUNNING )
      return;

    long newCount = loadTest.getThreadCount();

    // get list of active runners
    Iterator<InternalTestCaseRunner> iterator = runners.iterator();
    List<InternalTestCaseRunner> activeRunners = new ArrayList<InternalTestCaseRunner>();
    while( iterator.hasNext() )
    {
      InternalTestCaseRunner runner = iterator.next();
      if( !runner.isCanceled() )
        activeRunners.add( runner );
    }

    long diff = newCount - activeRunners.size();

    if( diff == 0 )
      return;

    // cancel runners if thread count has been decreased
    if( diff < 0 && loadTest.getCancelExcessiveThreads() )
    {
      diff = Math.abs( diff );
      for( int c = 0; c < diff && c < activeRunners.size(); c++ )
      {
        activeRunners.get( c ).cancel( "excessive thread", false );
      }
    }
    // start new runners if thread count has been increased
    else if( diff > 0 )
    {
      for( int c = 0; c < diff; c++ )
      {
        int startDelay = loadTest.getStartDelay();
        if( startDelay > 0 )
        {
          try
          {
            Thread.sleep( startDelay );
          }
          catch( InterruptedException e )
          {
            SoapUI.logError( e );
          }
        }

        if( status == Status.RUNNING )
          startTestCase( createTestCase() );
      }
    }
  }

  /**
   * Creates a copy of the underlying WsdlTestCase with all LoadTests removed
   * and configured for LoadTesting
   */

  private synchronized WsdlTestCase createTestCase()
  {
    WsdlTestCase testCase = loadTest.getTestCase();
    TestCaseConfig config = null;

    if( blueprintConfig == null )
    {
      try
      {
        blueprintConfig = TestCaseConfig.Factory.parse( testCase.getConfig().xmlText() );
        blueprintConfig.setLoadTestArray( new LoadTestConfig[0] );
        blueprintConfig.setSecurityTestArray( new SecurityTestConfig[0] );
      }
      catch( XmlException e )
      {
        e.printStackTrace();
      }
    }

    config = ( TestCaseConfig )blueprintConfig.copy();

    // clone entire testCase
    WsdlTestCase tc = testCase.getTestSuite().buildTestCase( config, true );
    tc.afterLoad();
    tc.addTestRunListener( testRunListener );
    Settings settings = tc.getSettings();
    settings.setBoolean( HttpSettings.INCLUDE_REQUEST_IN_TIME_TAKEN,
        loadTest.getSettings().getBoolean( HttpSettings.INCLUDE_REQUEST_IN_TIME_TAKEN ) );
    settings.setBoolean( HttpSettings.INCLUDE_RESPONSE_IN_TIME_TAKEN,
        loadTest.getSettings().getBoolean( HttpSettings.INCLUDE_RESPONSE_IN_TIME_TAKEN ) );
    settings.setBoolean( HttpSettings.CLOSE_CONNECTIONS,
        loadTest.getSettings().getBoolean( HttpSettings.CLOSE_CONNECTIONS ) );

    // disable default pretty-printing since it takes time
    settings.setBoolean( WsdlSettings.PRETTY_PRINT_RESPONSE_MESSAGES, false );

    // don't discard.. the WsdlLoadTests internal listener will discard after
    // asserting..
    tc.setDiscardOkResults( false );
    tc.setMaxResults( 0 );
    return tc;
  }

  public String getReason()
  {
    return reason;
  }

  public long getTimeTaken()
  {
    return System.currentTimeMillis() - startTime;
  }

  private class InternalTestRunListener extends TestRunListenerAdapter
  {
    public void beforeRun( TestCaseRunner testRunner, TestCaseRunContext runContext )
    {
      if( getProgress() > 1 && loadTest.getCancelOnReachedLimit() )
      {
        testRunner.cancel( "LoadTest Limit reached" );
      }
      else
        for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
        {
          listener.beforeTestCase( WsdlLoadTestRunner.this, context, testRunner, runContext );
        }
    }

    public void beforeStep( TestCaseRunner testRunner, TestCaseRunContext runContext, TestStep testStep )
    {
      if( getProgress() > 1 && loadTest.getCancelOnReachedLimit() )
      {
        testRunner.cancel( "LoadTest Limit reached" );
      }
      else if( runContext.getCurrentStep() != null )
      {
        for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
        {
          listener.beforeTestStep( WsdlLoadTestRunner.this, context, testRunner, runContext, testStep );
        }
      }
    }

    public void afterStep( TestCaseRunner testRunner, TestCaseRunContext runContext, TestStepResult result )
    {
      for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
      {
        listener.afterTestStep( WsdlLoadTestRunner.this, context, testRunner, runContext, result );
      }
    }

    public void afterRun( TestCaseRunner testRunner, TestCaseRunContext runContext )
    {
      for( LoadTestRunListener listener : loadTest.getLoadTestRunListeners() )
      {
        listener.afterTestCase( WsdlLoadTestRunner.this, context, testRunner, runContext );
      }
    }
  }

  public boolean isRunning()
  {
    return status == Status.RUNNING;
  }

  public TestRunContext getRunContext()
  {
    return context;
  }

  public long getStartTime()
  {
    return startTime;
  }

  public void start( boolean async )
  {
    start();
  }

  public TestRunnable getTestRunnable()
  {
    return loadTest;
  }

  public void release()
  {
    loadTest.removePropertyChangeListener( WsdlLoadTest.THREADCOUNT_PROPERTY, internalPropertyChangeListener );
  }
}
TOP

Related Classes of com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTestRunner$TestCaseStarter

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.