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

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

/*
* Copyright 2004-2014 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/

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

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.