/* * SoapUI, Copyright (C) 2004-2016 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.panels.loadtest; import com.eviware.soapui.SoapUI; import com.eviware.soapui.analytics.Analytics; import com.eviware.soapui.analytics.SoapUIActions; import com.eviware.soapui.config.LoadTestLimitTypesConfig; import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction; import com.eviware.soapui.impl.wsdl.actions.loadtest.LoadTestOptionsAction; import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest; import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTestRunner; import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction; import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog; import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategy; import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyFactory; import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyRegistry; import com.eviware.soapui.impl.wsdl.panels.support.MockLoadTestRunContext; import com.eviware.soapui.impl.wsdl.panels.support.MockLoadTestRunner; import com.eviware.soapui.impl.wsdl.panels.teststeps.support.AbstractGroovyEditorModel; import com.eviware.soapui.impl.wsdl.support.HelpUrls; import com.eviware.soapui.model.ModelItem; import com.eviware.soapui.model.support.LoadTestRunListenerAdapter; import com.eviware.soapui.model.testsuite.LoadTestRunContext; import com.eviware.soapui.model.testsuite.LoadTestRunListener; import com.eviware.soapui.model.testsuite.LoadTestRunner; import com.eviware.soapui.model.testsuite.TestRunner.Status; import com.eviware.soapui.support.UISupport; import com.eviware.soapui.support.action.swing.SwingActionDelegate; import com.eviware.soapui.support.components.GroovyEditorComponent; import com.eviware.soapui.support.components.GroovyEditorInspector; import com.eviware.soapui.support.components.JComponentInspector; import com.eviware.soapui.support.components.JInspectorPanel; import com.eviware.soapui.support.components.JInspectorPanelFactory; import com.eviware.soapui.support.components.JXToolBar; import com.eviware.soapui.ui.desktop.DesktopPanel; import com.eviware.soapui.ui.support.DesktopListenerAdapter; import com.eviware.soapui.ui.support.KeySensitiveModelItemDesktopPanel; import com.jgoodies.forms.builder.ButtonBarBuilder; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JSpinner; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; /** * Desktop panel for LoadTests * * @author Ole.Matzura */ @SuppressWarnings("serial") public class WsdlLoadTestDesktopPanel extends KeySensitiveModelItemDesktopPanel<WsdlLoadTest> implements PropertyChangeListener { private static final String SECONDS_LIMIT = "Seconds"; private static final String RUNS_LIMIT = "Total Runs"; private static final String RUNS_PER_THREAD_LIMIT = "Runs per Thread"; @SuppressWarnings("unused") private JSplitPane mainSplit; @SuppressWarnings("unused") private JTabbedPane mainTabs; @SuppressWarnings("unused") private JPanel graphPanel; protected JButton runButton; protected JButton cancelButton; protected JButton statisticsGraphButton; private WsdlLoadTestRunner runner; protected JSpinner threadsSpinner; private LoadTestRunListener internalLoadTestListener = new InternalLoadTestListener(); protected JComboBox strategyCombo; protected JPanel loadStrategyConfigurationPanel; protected JButton resetButton; private LoadTestLog loadTestLog; protected JButton optionsButton; protected JButton testTimesGraphButton; @SuppressWarnings("unused") private Object limit; private JSpinner limitSpinner; private JComboBox limitTypeCombo; private SpinnerNumberModel limitSpinnerModel; protected JProgressBar progressBar; private StatisticsDesktopPanel statisticsDesktopPanel; private StatisticsHistoryDesktopPanel statisticsHistoryDesktopPanel; public boolean loadTestIsRunning; private InternalDesktopListener desktopListener; protected JButton exportButton; private JLoadTestAssertionsTable assertionsTable; private JStatisticsTable statisticsTable; private GroovyEditorComponent tearDownGroovyEditor; private GroovyEditorComponent setupGroovyEditor; private JInspectorPanel inspectorPanel; public WsdlLoadTestDesktopPanel(WsdlLoadTest loadTest) { super(loadTest); loadTestLog = loadTest.getLoadTestLog(); loadTest.addPropertyChangeListener(this); loadTest.addLoadTestRunListener(internalLoadTestListener); desktopListener = new InternalDesktopListener(); SoapUI.getDesktop().addDesktopListener(desktopListener); buildUI(); } private void buildUI() { add(buildToolbar(), BorderLayout.NORTH); add(buildContent(), BorderLayout.CENTER); setPreferredSize(new Dimension(600, 500)); } private JComponent buildContent() { inspectorPanel = JInspectorPanelFactory.build(buildStatistics()); addInspectors(inspectorPanel); inspectorPanel.setDefaultDividerLocation(0.6F); inspectorPanel.setCurrentInspector("LoadTest Log"); return inspectorPanel.getComponent(); } protected void addInspectors(JInspectorPanel inspectorPanel) { inspectorPanel.addInspector(new JComponentInspector<JComponent>(buildLog(), "LoadTest Log", "The current LoadTest execution log", true)); inspectorPanel.addInspector(new JComponentInspector<JComponent>(buildAssertions(), "LoadTest Assertions", "The assertions for this LoadTest", true)); inspectorPanel.addInspector(new GroovyEditorInspector(buildSetupScriptPanel(), "Setup Script", "Script to run before tunning a TestCase")); inspectorPanel.addInspector(new GroovyEditorInspector(buildTearDownScriptPanel(), "TearDown Script", "Script to run after a TestCase Run")); } protected GroovyEditorComponent buildTearDownScriptPanel() { tearDownGroovyEditor = new GroovyEditorComponent(new TearDownScriptGroovyEditorModel(), null); return tearDownGroovyEditor; } protected GroovyEditorComponent buildSetupScriptPanel() { setupGroovyEditor = new GroovyEditorComponent(new SetupScriptGroovyEditorModel(), null); return setupGroovyEditor; } protected JComponent buildStatistics() { statisticsTable = new JStatisticsTable(getModelItem()); return statisticsTable; } protected JComponent buildLog() { JLoadTestLogTable loadTestLogTable = new JLoadTestLogTable(loadTestLog); return loadTestLogTable; } protected JComponent buildAssertions() { assertionsTable = new JLoadTestAssertionsTable(getModelItem()); return assertionsTable; } protected JComponent buildToolbar() { WsdlLoadTest loadTest = getModelItem(); JXToolBar toolbar = UISupport.createToolbar(); // ButtonBarBuilder builder = new ButtonBarBuilder(); runButton = UISupport.createToolbarButton(new RunLoadTestAction()); cancelButton = UISupport.createToolbarButton(new CancelRunTestCaseAction(), false); resetButton = UISupport.createToolbarButton(new ResetAction()); exportButton = UISupport.createToolbarButton(new ExportStatisticsAction(loadTest.getStatisticsModel())); statisticsGraphButton = UISupport.createToolbarButton(new ShowStatisticsGraphAction()); testTimesGraphButton = UISupport.createToolbarButton(new ShowTestTimesGraphAction()); statisticsGraphButton.setEnabled(getModelItem().getHistoryLimit() != 0); testTimesGraphButton.setEnabled(getModelItem().getHistoryLimit() != 0); AbstractAction optionsDelegate = SwingActionDelegate.createDelegate(LoadTestOptionsAction.SOAPUI_ACTION_ID, loadTest); optionsDelegate.putValue(Action.SMALL_ICON, UISupport.createImageIcon("/preferences.png")); optionsButton = UISupport.createToolbarButton(optionsDelegate); strategyCombo = new JComboBox(LoadStrategyRegistry.getInstance().getStrategies()); strategyCombo.setToolTipText("Selects which LoadTest Strategy to use"); UISupport.setPreferredHeight(strategyCombo, 18); strategyCombo.setSelectedItem(loadTest.getLoadStrategy().getType()); strategyCombo.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { Object item = e.getItem(); if (item == null) { return; } setLoadStrategy(item.toString()); } }); toolbar.add(runButton); toolbar.add(cancelButton); toolbar.add(statisticsGraphButton); toolbar.add(testTimesGraphButton); toolbar.add(resetButton); toolbar.add(exportButton); toolbar.add(optionsButton); toolbar.add(UISupport.createToolbarButton(new ShowOnlineHelpAction(HelpUrls.LOADTESTEDITOR_HELP_URL))); toolbar.add(Box.createHorizontalGlue()); buildLimitBar(toolbar); toolbar.addSeparator(); progressBar = new JProgressBar(0, 100); progressBar.setPreferredSize(new Dimension(70, 20)); toolbar.addFixed(progressBar); ButtonBarBuilder builder = new ButtonBarBuilder(); builder.addFixed(new JLabel("Threads:")); builder.addRelatedGap(); threadsSpinner = new JSpinner(new SpinnerNumberModel(getModelItem().getThreadCount(), 1, 9999, 1)); threadsSpinner.setToolTipText("Sets the number of threads (\"Virtual Users\") to run this TestCase"); UISupport.setPreferredHeight(threadsSpinner, 18); threadsSpinner.getModel().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { getModelItem().setThreadCount(((SpinnerNumberModel) threadsSpinner.getModel()).getNumber().intValue()); } }); builder.addFixed(threadsSpinner); builder.addUnrelatedGap(); LoadStrategy loadStrategy = loadTest.getLoadStrategy(); builder.addFixed(new JLabel("Strategy")); builder.addRelatedGap(); builder.addFixed(strategyCombo); builder.addUnrelatedGap(); loadStrategyConfigurationPanel = new JPanel(new BorderLayout()); loadStrategyConfigurationPanel.add(loadStrategy.getConfigurationPanel(), BorderLayout.CENTER); builder.addFixed(loadStrategyConfigurationPanel); builder.setBorder(BorderFactory.createEmptyBorder(2, 3, 3, 3)); return UISupport.buildPanelWithToolbar(toolbar, builder.getPanel()); } public void buildLimitBar(JXToolBar toolbar) { limitSpinnerModel = new SpinnerNumberModel(getModelItem().getTestLimit(), 0, Long.MAX_VALUE, 100); limitSpinner = new JSpinner(limitSpinnerModel); limitSpinner.setPreferredSize(new Dimension(70, 20)); limitSpinner.setToolTipText("Sets the limit for this test; total number of requests or seconds to run"); limitSpinner.getModel().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int intValue = ((SpinnerNumberModel) limitSpinner.getModel()).getNumber().intValue(); getModelItem().setTestLimit(intValue); } }); toolbar.addSeparator(); toolbar.addFixed(new JLabel("Limit:")); toolbar.addSeparator(); toolbar.addFixed(limitSpinner); toolbar.addSeparator(); limitTypeCombo = new JComboBox(new String[]{WsdlLoadTestDesktopPanel.RUNS_LIMIT, WsdlLoadTestDesktopPanel.SECONDS_LIMIT, WsdlLoadTestDesktopPanel.RUNS_PER_THREAD_LIMIT}); if (getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME) { limitTypeCombo.setSelectedIndex(1); } else if (getModelItem().getLimitType() == LoadTestLimitTypesConfig.COUNT_PER_THREAD) { limitTypeCombo.setSelectedIndex(2); } toolbar.addFixed(limitTypeCombo); toolbar.addSeparator(); limitTypeCombo.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { Object item = e.getItem(); if (WsdlLoadTestDesktopPanel.RUNS_LIMIT.equals(item)) { getModelItem().setLimitType(LoadTestLimitTypesConfig.COUNT); } else if (WsdlLoadTestDesktopPanel.SECONDS_LIMIT.equals(item)) { getModelItem().setLimitType(LoadTestLimitTypesConfig.TIME); } else if (WsdlLoadTestDesktopPanel.RUNS_PER_THREAD_LIMIT.equals(item)) { getModelItem().setLimitType(LoadTestLimitTypesConfig.COUNT_PER_THREAD); } } }); } public boolean onClose(boolean canCancel) { if (runner != null && runner.getStatus() == Status.RUNNING) { if (!UISupport.confirm("Running test will be canceled when closing window. Close anyway?", "Close LoadTest")) { return false; } } getModelItem().removeLoadTestRunListener(internalLoadTestListener); getModelItem().removePropertyChangeListener(this); getModelItem().getStatisticsModel().reset(); if (runner != null && runner.getStatus() == Status.RUNNING) { runner.cancel("closing window"); } if (statisticsDesktopPanel != null) { SoapUI.getDesktop().closeDesktopPanel(statisticsDesktopPanel); } if (statisticsHistoryDesktopPanel != null) { SoapUI.getDesktop().closeDesktopPanel(statisticsHistoryDesktopPanel); } assertionsTable.release(); loadStrategyConfigurationPanel.removeAll(); SoapUI.getDesktop().removeDesktopListener(desktopListener); statisticsTable.release(); inspectorPanel.release(); setupGroovyEditor.release(); tearDownGroovyEditor.release(); return release(); } private final class InternalDesktopListener extends DesktopListenerAdapter { public void desktopPanelClosed(DesktopPanel desktopPanel) { if (desktopPanel == statisticsDesktopPanel) { statisticsDesktopPanel = null; } else if (desktopPanel == statisticsHistoryDesktopPanel) { statisticsHistoryDesktopPanel = null; } } } public class RunLoadTestAction extends AbstractAction { public RunLoadTestAction() { putValue(Action.SMALL_ICON, UISupport.createImageIcon("/run.png")); putValue(Action.SHORT_DESCRIPTION, "Runs this LoadTest"); } public void actionPerformed(ActionEvent e) { WsdlLoadTest loadtest = getModelItem(); if (loadtest.getTestCase().getTestStepCount() == 0) { UISupport.showErrorMessage("Missing TestSteps for testing!"); return; } if (loadtest.getLimitType() == LoadTestLimitTypesConfig.COUNT && loadtest.getTestLimit() < loadtest.getThreadCount()) { if (!UISupport.confirm("The run limit is set to a lower count than number of threads\nRun Anyway?", "Run LoadTest")) { return; } } runButton.setEnabled(false); runner = loadtest.run(); Analytics.trackAction(SoapUIActions.RUN_LOAD_TEST.getActionName()); } } public class ResetAction extends AbstractAction { public ResetAction() { putValue(Action.SMALL_ICON, UISupport.createImageIcon("/reset_loadtest_statistics.gif")); putValue(Action.SHORT_DESCRIPTION, "Resets statistics for this LoadTest"); } public void actionPerformed(ActionEvent e) { getModelItem().getStatisticsModel().reset(); } } public class ShowStatisticsGraphAction extends AbstractAction { public ShowStatisticsGraphAction() { putValue(Action.SMALL_ICON, UISupport.createImageIcon("/stats_graph.gif")); putValue(Action.SHORT_DESCRIPTION, "Shows the statistics graph"); } public void actionPerformed(ActionEvent e) { if (statisticsDesktopPanel == null) { statisticsDesktopPanel = new StatisticsDesktopPanel(getModelItem()); } UISupport.showDesktopPanel(statisticsDesktopPanel); } } public class ShowTestTimesGraphAction extends AbstractAction { public ShowTestTimesGraphAction() { putValue(Action.SMALL_ICON, UISupport.createImageIcon("/samples_graph.gif")); putValue(Action.SHORT_DESCRIPTION, "Shows the Statistics History graph"); } public void actionPerformed(ActionEvent e) { if (statisticsHistoryDesktopPanel == null) { statisticsHistoryDesktopPanel = new StatisticsHistoryDesktopPanel(getModelItem()); } UISupport.showDesktopPanel(statisticsHistoryDesktopPanel); } } public class CancelRunTestCaseAction extends AbstractAction { public CancelRunTestCaseAction() { putValue(Action.SMALL_ICON, UISupport.createImageIcon("/stop.png")); putValue(Action.SHORT_DESCRIPTION, "Stops running this LoadTest"); } public void actionPerformed(ActionEvent e) { if (runner != null) { runner.cancel("Canceled"); cancelButton.setEnabled(false); } } } public boolean dependsOn(ModelItem modelItem) { WsdlLoadTest loadTest = getModelItem(); return modelItem == loadTest || modelItem == loadTest.getTestCase() || modelItem == loadTest.getTestCase().getTestSuite() || modelItem == loadTest.getTestCase().getTestSuite().getProject(); } public void setLoadStrategy(String type) { LoadStrategyFactory factory = LoadStrategyRegistry.getInstance().getFactory(type); LoadStrategy loadStrategy = factory.create(getModelItem()); getModelItem().setLoadStrategy(loadStrategy); loadStrategyConfigurationPanel.removeAll(); loadStrategyConfigurationPanel.add(loadStrategy.getConfigurationPanel(), BorderLayout.CENTER); loadStrategyConfigurationPanel.revalidate(); } private class InternalLoadTestListener extends LoadTestRunListenerAdapter { public void beforeLoadTest(LoadTestRunner testRunner, LoadTestRunContext context) { loadTestLog.clear(); loadTestIsRunning = true; if (getModelItem().getTestLimit() > 0) { progressBar.setValue(0); progressBar.setString(null); } else { progressBar.setString("..."); } progressBar.setStringPainted(true); runButton.setEnabled(false); cancelButton.setEnabled(true); strategyCombo.setEnabled(false); limitTypeCombo.setEnabled(false); optionsButton.setEnabled(false); threadsSpinner.setEnabled(getModelItem().getLoadStrategy().allowThreadCountChangeDuringRun()); new Thread(new ProgressBarUpdater(), getModelItem().getName() + " ProgressBarUpdater").start(); } public void afterLoadTest(LoadTestRunner testRunner, LoadTestRunContext context) { runButton.setEnabled(true); cancelButton.setEnabled(false); strategyCombo.setEnabled(true); limitTypeCombo.setEnabled(true); threadsSpinner.setEnabled(true); optionsButton.setEnabled(true); runner = null; loadTestIsRunning = false; if (progressBar.isIndeterminate()) { progressBar.setIndeterminate(false); progressBar.setValue(0); } else if (testRunner.getStatus() == Status.FINISHED) { progressBar.setValue(100); } if (testRunner.getStatus() == Status.FAILED) { UISupport.showErrorMessage("LoadTest failed; " + testRunner.getReason()); } } } private class ProgressBarUpdater implements Runnable { public void run() { while (true) { if (!loadTestIsRunning) { break; } if (getModelItem().getTestLimit() == 0) { if (loadTestIsRunning && !progressBar.isIndeterminate()) { progressBar.setIndeterminate(true); progressBar.setString("..."); } } else { if (loadTestIsRunning && progressBar.isIndeterminate()) { progressBar.setIndeterminate(false); progressBar.setString(null); } progressBar.setValue(runner == null ? 0 : (int) (runner.getProgress() * 100)); } try { Thread.sleep(500); } catch (InterruptedException e) { SoapUI.logError(e); } } } } public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(WsdlLoadTest.THREADCOUNT_PROPERTY)) { threadsSpinner.setValue(evt.getNewValue()); } else if (evt.getPropertyName().equals(WsdlLoadTest.HISTORYLIMIT_PROPERTY)) { long lng = (Long) evt.getNewValue(); statisticsGraphButton.setEnabled(lng != 0); testTimesGraphButton.setEnabled(lng != 0); } super.propertyChange(evt); } private class SetupScriptGroovyEditorModel extends AbstractGroovyEditorModel { @Override public Action createRunAction() { return new AbstractAction() { public void actionPerformed(ActionEvent e) { try { MockLoadTestRunner mockTestRunner = new MockLoadTestRunner( WsdlLoadTestDesktopPanel.this.getModelItem(), SoapUI.ensureGroovyLog()); WsdlLoadTestDesktopPanel.this.getModelItem().runSetupScript( new MockLoadTestRunContext(mockTestRunner), mockTestRunner); } catch (Exception e1) { UISupport.showErrorMessage(e1); } } }; } public SetupScriptGroovyEditorModel() { super(new String[]{"log", "context", "loadTestRunner"}, WsdlLoadTestDesktopPanel.this.getModelItem(), "Setup"); } public String getScript() { return WsdlLoadTestDesktopPanel.this.getModelItem().getSetupScript(); } public void setScript(String text) { WsdlLoadTestDesktopPanel.this.getModelItem().setSetupScript(text); } } private class TearDownScriptGroovyEditorModel extends AbstractGroovyEditorModel { @Override public Action createRunAction() { return new AbstractAction() { public void actionPerformed(ActionEvent e) { try { MockLoadTestRunner mockTestRunner = new MockLoadTestRunner( WsdlLoadTestDesktopPanel.this.getModelItem(), SoapUI.ensureGroovyLog()); WsdlLoadTestDesktopPanel.this.getModelItem().runTearDownScript( new MockLoadTestRunContext(mockTestRunner), mockTestRunner); } catch (Exception e1) { UISupport.showErrorMessage(e1); } } }; } public TearDownScriptGroovyEditorModel() { super(new String[]{"log", "context", "loadTestRunner"}, WsdlLoadTestDesktopPanel.this.getModelItem(), "TearDown"); } public String getScript() { return WsdlLoadTestDesktopPanel.this.getModelItem().getTearDownScript(); } public void setScript(String text) { WsdlLoadTestDesktopPanel.this.getModelItem().setTearDownScript(text); } } @Override protected void renameModelItem() { SoapUI.getActionRegistry().performAction("RenameLoadTestAction", getModelItem(), null); } @Override protected void cloneModelItem() { SoapUI.getActionRegistry().performAction("CloneLoadTestAction", getModelItem(), null); } }