View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui.impl.wsdl.panels.loadtest;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Dimension;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.ItemEvent;
20  import java.awt.event.ItemListener;
21  import java.beans.PropertyChangeEvent;
22  import java.beans.PropertyChangeListener;
23  
24  import javax.swing.AbstractAction;
25  import javax.swing.Action;
26  import javax.swing.BorderFactory;
27  import javax.swing.Box;
28  import javax.swing.JButton;
29  import javax.swing.JComboBox;
30  import javax.swing.JComponent;
31  import javax.swing.JLabel;
32  import javax.swing.JPanel;
33  import javax.swing.JProgressBar;
34  import javax.swing.JSpinner;
35  import javax.swing.JSplitPane;
36  import javax.swing.JTabbedPane;
37  import javax.swing.SpinnerNumberModel;
38  import javax.swing.event.ChangeEvent;
39  import javax.swing.event.ChangeListener;
40  
41  import com.eviware.soapui.SoapUI;
42  import com.eviware.soapui.config.LoadTestLimitTypesConfig;
43  import com.eviware.soapui.impl.wsdl.actions.loadtest.LoadTestOptionsAction;
44  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
45  import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
46  import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTestRunner;
47  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
48  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
49  import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction;
50  import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
51  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategy;
52  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyFactory;
53  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyRegistry;
54  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
55  import com.eviware.soapui.model.ModelItem;
56  import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
57  import com.eviware.soapui.model.testsuite.LoadTestRunContext;
58  import com.eviware.soapui.model.testsuite.LoadTestRunListener;
59  import com.eviware.soapui.model.testsuite.LoadTestRunner;
60  import com.eviware.soapui.model.testsuite.LoadTestRunner.Status;
61  import com.eviware.soapui.support.UISupport;
62  import com.eviware.soapui.support.components.JXToolBar;
63  import com.eviware.soapui.ui.desktop.DesktopPanel;
64  import com.eviware.soapui.ui.support.DesktopListenerAdapter;
65  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
66  import com.jgoodies.forms.builder.ButtonBarBuilder;
67  
68  /***
69   * Desktop panel for LoadTests
70   * 
71   * @author Ole.Matzura
72   */
73  
74  public class WsdlLoadTestDesktopPanel extends ModelItemDesktopPanel<WsdlLoadTest> implements PropertyChangeListener
75  {
76  	private static final String SECONDS_LIMIT = "Seconds";
77  	private static final String RUNS_LIMIT = "Total Runs";
78  	private JPanel contentPanel;
79  	@SuppressWarnings("unused")
80  	private JSplitPane mainSplit;
81  	@SuppressWarnings("unused")
82  	private JTabbedPane mainTabs;
83  	@SuppressWarnings("unused")
84  	private JPanel graphPanel;
85  	private JButton runButton;
86  	private JButton cancelButton;
87  	private JButton statisticsGraphButton;
88  	private WsdlLoadTestRunner runner;
89  	private JSpinner threadsSpinner;
90  	private LoadTestRunListener internalLoadTestListener = new InternalLoadTestListener();
91  	private JComboBox strategyCombo;
92  	private JPanel loadStrategyConfigurationPanel;
93  	private JButton resetButton;
94  	private LoadTestLog loadTestLog;
95  	private JButton optionsButton;
96  	private JButton testTimesGraphButton;
97  	@SuppressWarnings("unused")
98  	private Object limit;
99  	private JSpinner limitSpinner;
100 	private JComboBox limitTypeCombo;
101 	private SpinnerNumberModel limitSpinnerModel;
102 	private JProgressBar progressBar;
103 	private long loadTestStartTime;
104 	private StatisticsDesktopPanel statisticsDesktopPanel;
105 	private StatisticsHistoryDesktopPanel statisticsHistoryDesktopPanel;
106 	
107 	public boolean loadTestIsRunning;
108 	private InternalDesktopListener desktopListener;
109 	private JButton exportButton;
110 	private JLoadTestAssertionsTable assertionsTable;
111 	private JStatisticsTable statisticsTable;
112 	
113 	public WsdlLoadTestDesktopPanel(WsdlLoadTest loadTest)
114 	{
115 		super( loadTest );
116 		
117 		loadTestLog = loadTest.getLoadTestLog();
118 		loadTest.addPropertyChangeListener( this );
119 		loadTest.addLoadTestRunListener( internalLoadTestListener );
120 		
121 		desktopListener = new InternalDesktopListener();
122 	   SoapUI.getDesktop().addDesktopListener( desktopListener );
123 		
124 		buildUI();
125 	}
126 
127 	private void buildUI()
128 	{
129 		contentPanel = new JPanel( new BorderLayout());
130 		
131 		contentPanel.add( buildToolbar(), BorderLayout.NORTH );
132 		contentPanel.add( buildContent(), BorderLayout.CENTER );
133 		
134 		contentPanel.setPreferredSize( new Dimension( 600, 500 ));
135 	}
136 
137 	private JComponent buildContent()
138 	{
139 		JTabbedPane tabbedPane = new JTabbedPane( JTabbedPane.BOTTOM );
140 		tabbedPane.addTab( "LoadTest Log", buildLog() );
141 		tabbedPane.addTab( "LoadTest Assertions", buildAssertions() );
142 		JPanel p = new JPanel( new BorderLayout() );
143 		p.add( tabbedPane, BorderLayout.CENTER );
144 		p.setBackground( Color.LIGHT_GRAY );
145 		
146 		JSplitPane mainSplit = UISupport.createVerticalSplit( buildStatistics(), p );
147 		mainSplit.setDividerLocation( 150 );
148 		return mainSplit;
149 	}
150 
151 	private JComponent buildStatistics(	)
152 	{
153 		statisticsTable = new JStatisticsTable( getModelItem() );
154 		return statisticsTable;
155 	}
156 
157 	private JComponent buildLog()
158 	{
159 		JLoadTestLogTable loadTestLogTable = new JLoadTestLogTable( loadTestLog );
160 		return loadTestLogTable;
161 	}
162 
163 	private JComponent buildAssertions()
164 	{
165 		assertionsTable = new JLoadTestAssertionsTable( getModelItem() );
166 		return assertionsTable;
167 	}
168 
169 	private JComponent buildToolbar()
170 	{
171 		WsdlLoadTest loadTest = getModelItem();
172 		
173 		JXToolBar toolbar = UISupport.createToolbar();
174 		
175 		//ButtonBarBuilder builder = new ButtonBarBuilder();
176       runButton = UISupport.createToolbarButton( new RunLoadTestAction());
177       cancelButton = UISupport.createToolbarButton( new CancelRunTestCaseAction(), false );
178       resetButton = UISupport.createToolbarButton( new ResetAction() );
179       exportButton = UISupport.createToolbarButton( new ExportStatisticsAction( loadTest.getStatisticsModel() ) );
180       
181       statisticsGraphButton = UISupport.createToolbarButton( new ShowStatisticsGraphAction() );
182       testTimesGraphButton = UISupport.createToolbarButton( new ShowTestTimesGraphAction() );
183       
184       statisticsGraphButton.setEnabled( getModelItem().getHistoryLimit() != 0 );
185 		testTimesGraphButton.setEnabled( getModelItem().getHistoryLimit() != 0  );
186       
187       optionsButton = UISupport.createToolbarButton( new LoadTestOptionsAction( getModelItem() ) );
188       
189       strategyCombo = new JComboBox( LoadStrategyRegistry.getInstance().getStrategies() );
190       strategyCombo.setToolTipText( "Selects which LoadTest Strategy to use" );
191       UISupport.setPreferredHeight( strategyCombo, 18 );
192 		strategyCombo.setSelectedItem( loadTest.getLoadStrategy().getType());
193 		strategyCombo.addItemListener( new ItemListener() 
194 				{
195 					public void itemStateChanged(ItemEvent e)
196 					{
197 						Object item = e.getItem();
198 						if( item == null ) 
199 							return;
200 						
201 						setLoadStrategy(item.toString());
202 					}} );
203       
204 		toolbar.add( runButton );
205 		toolbar.add( cancelButton );
206 		toolbar.add( statisticsGraphButton );
207 		toolbar.add( testTimesGraphButton );
208 		toolbar.add( resetButton );
209 		toolbar.add( exportButton );
210      
211 		toolbar.add( optionsButton );
212 		toolbar.add( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.LOADTESTEDITOR_HELP_URL )));
213 		toolbar.add( Box.createHorizontalGlue() );
214 		buildLimitBar( toolbar );
215       toolbar.addSeparator();
216 		
217 		progressBar = new JProgressBar( 0, 100 );
218 		progressBar.setPreferredSize( new Dimension( 70, 20 ));
219 
220 		toolbar.addFixed( progressBar );
221 		
222 		ButtonBarBuilder builder = new ButtonBarBuilder();
223 		
224 		builder.addFixed( new JLabel( "Threads:" ));
225       builder.addRelatedGap();
226 		
227 		threadsSpinner = new JSpinner( new SpinnerNumberModel(getModelItem().getThreadCount(), 1, 9999, 1) );
228 		threadsSpinner.setToolTipText( "Sets the number of threads (\"Virtual Users\") to run this TestCase" );
229 		UISupport.setPreferredHeight( threadsSpinner, 18 );
230       threadsSpinner.getModel().addChangeListener( new ChangeListener() {
231 
232 			public void stateChanged(ChangeEvent e)
233 			{
234 				getModelItem().setThreadCount( ((SpinnerNumberModel) threadsSpinner.getModel()).getNumber().intValue() );
235 			}} );
236       
237 		builder.addFixed( threadsSpinner);
238 		builder.addUnrelatedGap();
239 		
240 		LoadStrategy loadStrategy = loadTest.getLoadStrategy();
241 		
242 		builder.addFixed( new JLabel( "Strategy" ));
243       builder.addRelatedGap();
244       builder.addFixed( strategyCombo );
245       builder.addUnrelatedGap();
246 		
247       loadStrategyConfigurationPanel = new JPanel( new BorderLayout() );
248 		loadStrategyConfigurationPanel.add( loadStrategy.getConfigurationPanel(), BorderLayout.CENTER );
249       
250       builder.addFixed( loadStrategyConfigurationPanel );
251       builder.setBorder( BorderFactory.createEmptyBorder( 2, 3, 3, 3 ) );
252       
253 		return UISupport.buildPanelWithToolbar( toolbar, builder.getPanel() );
254 	}
255 	
256 	public void buildLimitBar( JXToolBar toolbar )
257 	{
258 		limitSpinnerModel = new SpinnerNumberModel(getModelItem().getTestLimit(), 0, Long.MAX_VALUE, 100 );
259 		
260 		limitSpinner = new JSpinner( limitSpinnerModel );
261 		limitSpinner.setPreferredSize( new Dimension( 70, 20 ));
262 		limitSpinner.setToolTipText( "Sets the limit for this test; total number of requests or seconds to run" );
263 		limitSpinner.getModel().addChangeListener( new ChangeListener() {
264 
265 			public void stateChanged(ChangeEvent e)
266 			{
267 				int intValue = ((SpinnerNumberModel) limitSpinner.getModel()).getNumber().intValue();
268 				getModelItem().setTestLimit( intValue );
269 			}} );
270 		
271 		toolbar.addSeparator();
272 		toolbar.addFixed( new JLabel( "Limit:" ));
273 		toolbar.addSeparator();
274 		toolbar.addFixed(limitSpinner );
275 		toolbar.addSeparator();
276 		
277 		limitTypeCombo = new JComboBox( new String[] { WsdlLoadTestDesktopPanel.RUNS_LIMIT, WsdlLoadTestDesktopPanel.SECONDS_LIMIT});
278 		
279 		if( getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME )
280 			limitTypeCombo.setSelectedIndex( 1 );
281 		
282 		toolbar.addFixed( limitTypeCombo);
283 		toolbar.addSeparator();
284 		
285 		limitTypeCombo.addItemListener( new ItemListener() 
286 		{
287 			public void itemStateChanged(ItemEvent e)
288 			{
289 				Object item = e.getItem();
290 				if( WsdlLoadTestDesktopPanel.RUNS_LIMIT.equals( item ))
291 				{
292 					getModelItem().setLimitType( LoadTestLimitTypesConfig.COUNT );
293 				}
294 				else if( WsdlLoadTestDesktopPanel.SECONDS_LIMIT.equals( item ))
295 				{
296 					getModelItem().setLimitType( LoadTestLimitTypesConfig.TIME );
297 				}
298 			}} );
299 	}
300 
301 	public boolean onClose( boolean canCancel )
302 	{
303 		if( runner != null && runner.getStatus() == Status.RUNNING )
304 		{
305 			if( !UISupport.confirm( "Running test will be canceled when closing window. Close anyway?", "Close LoadTest" ))
306 				return false;
307 		}
308 		
309 		getModelItem().removeLoadTestRunListener( internalLoadTestListener );
310 		getModelItem().removePropertyChangeListener( this );
311 		getModelItem().getStatisticsModel().reset();
312 		
313 		if( runner != null && runner.getStatus() == Status.RUNNING )
314 			runner.cancel( "closing window" );
315 		
316 		if( statisticsDesktopPanel != null )
317 			SoapUI.getDesktop().closeDesktopPanel( statisticsDesktopPanel );
318 		
319 		if( statisticsHistoryDesktopPanel != null )
320 			SoapUI.getDesktop().closeDesktopPanel( statisticsHistoryDesktopPanel );
321 		
322 		assertionsTable.release();
323 		loadStrategyConfigurationPanel.removeAll();
324 		SoapUI.getDesktop().removeDesktopListener( desktopListener );
325 		
326 		statisticsTable.release();
327 		
328 		return release();
329 	}
330 
331 	public JComponent getComponent()
332 	{
333 		return contentPanel;
334 	}
335 
336    private final class InternalDesktopListener extends DesktopListenerAdapter
337 	{
338 		public void desktopPanelClosed(DesktopPanel desktopPanel)
339 		{
340 			if( desktopPanel == statisticsDesktopPanel )
341 				statisticsDesktopPanel = null;
342 			else if( desktopPanel == statisticsHistoryDesktopPanel )
343 				statisticsHistoryDesktopPanel = null;
344 		}
345 	}
346 
347 	public class RunLoadTestAction extends AbstractAction
348    {
349 		public RunLoadTestAction()
350       {
351          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run_testcase.gif"));
352          putValue( Action.SHORT_DESCRIPTION, "Runs this LoadTest" );
353       }
354 
355 		public void actionPerformed(ActionEvent e)
356 		{
357 			WsdlLoadTest loadtest = getModelItem();
358 			if( loadtest.getTestCase().getTestStepCount() == 0 )
359 			{
360 				UISupport.showErrorMessage( "Missing TestSteps for testing!");
361 				return;
362 			}
363 			
364 			if( loadtest.getLimitType() == LoadTestLimitTypesConfig.COUNT && 
365 				 loadtest.getTestLimit() < loadtest.getThreadCount() )
366 			{
367 				if( !UISupport.confirm( "The run limit is set to a lower count than number of threads\nRun Anyway?", 
368 							"Run LoadTest" ))
369 				{
370 					return;
371 				}
372 			}
373 			
374 			runButton.setEnabled( false );
375          runner = loadtest.run();
376 		}
377    }
378 
379    public class ResetAction extends AbstractAction
380    {
381 		public ResetAction()
382       {
383          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/reset_loadtest_statistics.gif"));
384          putValue( Action.SHORT_DESCRIPTION, "Resets statistics for this LoadTest" );
385       }
386 
387 		public void actionPerformed(ActionEvent e)
388 		{
389 			getModelItem().getStatisticsModel().reset();
390 		}
391    }
392    
393    public class ShowStatisticsGraphAction extends AbstractAction
394    {
395 		public ShowStatisticsGraphAction()
396       {
397          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/stats_graph.gif"));
398          putValue( Action.SHORT_DESCRIPTION, "Shows the statistics graph" );
399       }
400 
401 		public void actionPerformed(ActionEvent e)
402 		{
403 			if( statisticsDesktopPanel == null )
404 				statisticsDesktopPanel = new StatisticsDesktopPanel( getModelItem() );
405 			
406          UISupport.showDesktopPanel( statisticsDesktopPanel );
407 		}
408    }
409 
410    public class ShowTestTimesGraphAction extends AbstractAction
411    {
412 		public ShowTestTimesGraphAction()
413       {
414          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/samples_graph.gif"));
415          putValue( Action.SHORT_DESCRIPTION, "Shows the Statistics History graph" );
416       }
417 
418 		public void actionPerformed(ActionEvent e)
419 		{
420 			if( statisticsHistoryDesktopPanel == null )
421 				statisticsHistoryDesktopPanel = new StatisticsHistoryDesktopPanel( getModelItem() );
422 			
423          UISupport.showDesktopPanel( statisticsHistoryDesktopPanel );
424 		}
425    }
426    
427    public class CancelRunTestCaseAction extends AbstractAction
428    {
429 
430 		public CancelRunTestCaseAction()
431       {
432          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/stop_testcase.gif"));
433          putValue( Action.SHORT_DESCRIPTION, "Stops running this LoadTest" );
434       }
435       
436 		public void actionPerformed(ActionEvent e)
437 		{
438       	if( runner != null )
439       	{
440       		runner.cancel( "Canceled" );
441       		cancelButton.setEnabled( false );
442       	}
443       }
444    }
445 
446    public boolean dependsOn(ModelItem modelItem)
447 	{
448    	WsdlLoadTest loadTest = getModelItem();
449    	
450 		return modelItem == loadTest || modelItem == loadTest.getTestCase() || 
451 			modelItem == loadTest.getTestCase().getTestSuite() || 
452 			modelItem == loadTest.getTestCase().getTestSuite().getProject();
453 	}
454 	
455 	public void setLoadStrategy(String type)
456 	{
457 		LoadStrategyFactory factory = LoadStrategyRegistry.getInstance().getFactory( type );
458 		LoadStrategy loadStrategy = factory.create();
459 		getModelItem().setLoadStrategy( loadStrategy );
460 		loadStrategyConfigurationPanel.removeAll();
461 		loadStrategyConfigurationPanel.add( loadStrategy.getConfigurationPanel(), BorderLayout.CENTER );
462 		loadStrategyConfigurationPanel.revalidate();
463 	}
464 
465 	private class InternalLoadTestListener extends LoadTestRunListenerAdapter
466 	{
467 		public void beforeLoadTest( LoadTestRunner testRunner, LoadTestRunContext context )
468 		{
469          loadTestLog.clear();
470 			
471 			loadTestStartTime = System.currentTimeMillis();
472 			loadTestIsRunning = true;
473 			if( getModelItem().getTestLimit() > 0 )
474 			{
475 				progressBar.setValue(0);
476 				progressBar.setString( null );
477 			}
478 			else
479 			{
480 				progressBar.setString("...");
481 			}
482 			
483 			progressBar.setStringPainted(true);
484 
485 			runButton.setEnabled( false );
486 			cancelButton.setEnabled( true );
487 			strategyCombo.setEnabled( false );
488 			limitTypeCombo.setEnabled( false );
489 			threadsSpinner.setEnabled( getModelItem().getLoadStrategy().allowThreadCountChangeDuringRun() );
490 			
491 			new Thread( new ProgressBarUpdater(), getModelItem().getName() + " ProgressBarUpdater" ).start();
492 		}
493 
494 		public void afterLoadTest( LoadTestRunner testRunner, LoadTestRunContext context )
495 		{
496 			runButton.setEnabled( true );
497 			
498 			cancelButton.setEnabled( false );
499 			strategyCombo.setEnabled( true );
500 			limitTypeCombo.setEnabled( true );
501 			threadsSpinner.setEnabled( true );
502 			
503 			runner = null;
504 			loadTestIsRunning = false;
505 			
506 			if( progressBar.isIndeterminate() )
507 			{
508 				progressBar.setIndeterminate( false );
509 				progressBar.setValue(0);
510 			}
511 			else if( testRunner.getStatus() == Status.FINISHED )
512 			{
513 				progressBar.setValue( 100 );
514 			}
515 			
516 			if( testRunner.getStatus() == Status.FAILED )
517 			{
518 				UISupport.showErrorMessage( "LoadTest failed; " + testRunner.getReason() );
519 			}
520 		}
521 		
522 		
523 	}
524 
525 	public String getDescription()
526 	{
527 		return "LoadTest: [" + getModelItem().getName() + "] for TestCase [" + getModelItem().getTestCase().getName() + 
528 					"] in TestSuite [" + getModelItem().getTestCase().getTestSuite().getName() + "]";
529 	}
530 	
531 	private class ProgressBarUpdater implements Runnable
532 	{
533 		public void run()
534 		{
535 			while( true )
536 			{
537 				if( !loadTestIsRunning )
538 					break;
539 
540 				if( getModelItem().getTestLimit() == 0 )
541 				{
542 					if( loadTestIsRunning && !progressBar.isIndeterminate())
543 					{
544 						progressBar.setIndeterminate( true );
545 						progressBar.setString("...");
546 					}
547 				}
548 				else if( getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME )
549 				{
550 					if( loadTestIsRunning && progressBar.isIndeterminate())
551 					{
552 						progressBar.setIndeterminate( false );
553 						progressBar.setString(null);
554 					}
555 					
556 					long timePassed = System.currentTimeMillis()-loadTestStartTime;
557 					int value = (int) ((timePassed*100)/(getModelItem().getTestLimit()*1000));
558 					progressBar.setValue( value );
559 				}
560 				else if( getModelItem().getLimitType() == LoadTestLimitTypesConfig.COUNT )
561 				{
562 					if( loadTestIsRunning && progressBar.isIndeterminate())
563 					{
564 						progressBar.setIndeterminate( false );
565 						progressBar.setString(null);
566 					}
567 					
568 					long counts = getModelItem().getStatisticsModel().getStatistic( LoadTestStatistics.TOTAL, Statistic.COUNT );
569 					if( counts > 0 )
570 						progressBar.setValue(  (int) ((counts*100)/getModelItem().getTestLimit()) );
571 				}
572 
573 				try
574 				{
575 					Thread.sleep( 500 );
576 				}
577 				catch (InterruptedException e)
578 				{
579 					SoapUI.logError( e );
580 				}
581 			}
582 		}
583 	}
584 
585 	public void propertyChange(PropertyChangeEvent evt)
586 	{
587 		if( evt.getPropertyName().equals( WsdlLoadTest.THREADCOUNT_PROPERTY ))
588 		{
589 			threadsSpinner.setValue( evt.getNewValue() );
590 		}
591 		else if( evt.getPropertyName().equals( WsdlLoadTest.HISTORYLIMIT_PROPERTY ))
592 		{
593 			long lng = (Long)evt.getNewValue();
594 			
595 			statisticsGraphButton.setEnabled( lng != 0 );
596 			testTimesGraphButton.setEnabled( lng != 0 );
597 		}
598 	}
599 	
600 }