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.testcase;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Component;
18  import java.awt.Font;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.MouseAdapter;
21  import java.awt.event.MouseEvent;
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.PrintWriter;
25  import java.text.SimpleDateFormat;
26  import java.util.Date;
27  import java.util.HashSet;
28  import java.util.Set;
29  
30  import javax.swing.AbstractAction;
31  import javax.swing.Action;
32  import javax.swing.BorderFactory;
33  import javax.swing.JLabel;
34  import javax.swing.JList;
35  import javax.swing.JPanel;
36  import javax.swing.JPopupMenu;
37  import javax.swing.JScrollPane;
38  import javax.swing.ListCellRenderer;
39  
40  import com.eviware.soapui.SoapUI;
41  import com.eviware.soapui.impl.wsdl.testcase.TestCaseLogItem;
42  import com.eviware.soapui.impl.wsdl.testcase.TestCaseLogModel;
43  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCaseRunner;
44  import com.eviware.soapui.model.settings.Settings;
45  import com.eviware.soapui.model.support.TestRunListenerAdapter;
46  import com.eviware.soapui.model.testsuite.TestRunContext;
47  import com.eviware.soapui.model.testsuite.TestRunner;
48  import com.eviware.soapui.model.testsuite.TestStepResult;
49  import com.eviware.soapui.model.testsuite.TestStepResult.TestStepStatus;
50  import com.eviware.soapui.support.StringUtils;
51  import com.eviware.soapui.support.UISupport;
52  import com.eviware.soapui.support.action.swing.ActionList;
53  import com.eviware.soapui.support.action.swing.ActionSupport;
54  import com.eviware.soapui.support.components.JHyperlinkLabel;
55  import com.eviware.soapui.support.components.JXToolBar;
56  import com.eviware.x.form.XFormDialog;
57  import com.eviware.x.form.support.ADialogBuilder;
58  import com.eviware.x.form.support.AField;
59  import com.eviware.x.form.support.AForm;
60  import com.eviware.x.form.support.AField.AFieldType;
61  
62  /***
63   * Panel for displaying TestStepResults
64   *  
65   * @author Ole.Matzura
66   */
67  
68  public class TestRunLog extends JPanel
69  {
70  	private TestCaseLogModel logListModel;
71  	private JList testLogList;
72  	private boolean errorsOnly = false;
73  	private final Settings settings;
74  	private Set<String> boldTexts = new HashSet<String>();
75  	private boolean follow = true;
76  	protected int selectedIndex; 
77  	private XFormDialog optionsDialog;
78  	
79  	public TestRunLog( Settings settings )
80  	{
81  		super(new BorderLayout());
82  		this.settings = settings;
83  
84  		errorsOnly = settings.getBoolean( OptionsForm.class.getName() + "@errors_only" );
85  		
86  		buildUI();
87  	}
88  
89  	private void buildUI()
90  	{
91  		logListModel = new TestCaseLogModel();
92  		logListModel.setMaxSize( ( int ) settings.getLong(OptionsForm.class.getName() + "@max_rows", 1000 ) );
93  		
94  		testLogList = new JList(logListModel);
95  		testLogList.setCellRenderer(new TestLogCellRenderer());
96  		testLogList.setPrototypeCellValue( "Testing 123" );
97  		testLogList.setFixedCellWidth( -1 );
98  		testLogList.addMouseListener(new LogListMouseListener());
99  
100 		JScrollPane scrollPane = new JScrollPane(testLogList);
101 		add(scrollPane, BorderLayout.CENTER);
102 		add( buildToolbar(), BorderLayout.NORTH );
103 	}
104 
105 	private Component buildToolbar( )
106 	{
107 		JXToolBar toolbar = UISupport.createSmallToolbar();
108 		
109 		addToolbarButtons( toolbar );
110 		
111 		return toolbar;
112 	}
113 	
114 	protected JList getTestLogList()
115 	{
116 		return testLogList;
117 	}
118 
119 	protected void addToolbarButtons( JXToolBar toolbar )
120 	{
121 		toolbar.addFixed( UISupport.createToolbarButton( new ClearLogAction() ) );
122 		toolbar.addFixed( UISupport.createToolbarButton( new SetLogOptionsAction() ) );
123 		toolbar.addFixed( UISupport.createToolbarButton( new ExportLogAction() ) );
124 	}
125 
126 	private final class TestLogCellRenderer extends JLabel implements ListCellRenderer
127 	{
128 		private Font boldFont;
129 		private Font normalFont;
130 		private JHyperlinkLabel hyperlinkLabel = new JHyperlinkLabel("");
131 		
132 		public TestLogCellRenderer()
133 		{
134 			setOpaque(true);
135 			setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
136 			setIcon(null);
137 			boldFont = getFont().deriveFont( Font.BOLD );
138 			normalFont = getFont();
139 			
140 			hyperlinkLabel.setOpaque( true );
141 			hyperlinkLabel.setForeground( Color.BLUE.darker().darker().darker() );
142 			hyperlinkLabel.setUnderlineColor( Color.GRAY );
143 			hyperlinkLabel.setBorder( BorderFactory.createEmptyBorder( 0, 4, 3, 3 ) );
144 		}
145 		
146 		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
147 				boolean cellHasFocus)
148 		{
149 			if (isSelected)
150 			{
151 				setBackground(list.getSelectionBackground());
152 				setForeground(list.getSelectionForeground());
153 			}
154 			else
155 			{
156 				setBackground(list.getBackground());
157 				setForeground(list.getForeground());
158 			}
159 			
160 			if (value instanceof String)
161 			{
162 				setText(value.toString());
163 			}
164 			else if (value instanceof TestCaseLogItem)
165 			{
166 				TestCaseLogItem logItem = (TestCaseLogItem) value;
167 				String msg = logItem.getMsg();
168 				setText(msg == null ? "" : msg);
169 			}
170 			
171 			TestStepResult result = logListModel.getResultAt(index);
172 			if( result != null && !result.isDiscarded() && result.getActions() != null && !getText().startsWith( " ->" ))
173 			{
174 				hyperlinkLabel.setText( getText() );
175 				hyperlinkLabel.setBackground( getBackground() );
176 				hyperlinkLabel.setEnabled( list.isEnabled() );
177 				
178 				if( result.getStatus() == TestStepStatus.OK )
179 				{
180 					hyperlinkLabel.setIcon( UISupport.createImageIcon( "/valid_assertion.gif" ) );
181 				}
182 				else if( result.getStatus() == TestStepStatus.FAILED )
183 				{
184 					hyperlinkLabel.setIcon( UISupport.createImageIcon( "/failed_assertion.gif" ) );
185 				}
186 				else 
187 				{
188 					hyperlinkLabel.setIcon( UISupport.createImageIcon( "/unknown_assertion.gif" ) );
189 				}
190 				
191 				return hyperlinkLabel;
192 			}
193 
194 			setEnabled(list.isEnabled());
195 			
196 			if( boldTexts.contains( getText() ))
197 			{
198 				setFont( boldFont);
199 			}
200 			else
201 			{
202 				setFont( normalFont );
203 			}
204 
205 			return this;
206 		}
207 	}
208 
209 	/***
210 	 * Mouse Listener for triggering default action and showing popup for log list items
211 	 * 
212 	 * @author Ole.Matzura
213 	 */
214 
215 	private final class LogListMouseListener extends MouseAdapter
216 	{
217 		public void mouseClicked(MouseEvent e)
218 		{
219 			int index = testLogList.getSelectedIndex();
220 			if ( index != -1 && (index == selectedIndex || e.getClickCount() > 1))
221 			{
222 				TestStepResult result = logListModel.getResultAt(index);
223 				if (result != null && result.getActions() != null)
224 					result.getActions().performDefaultAction(new ActionEvent(this, 0, null));
225 			}
226 			
227 			selectedIndex = index;
228 		}
229 
230 		public void mousePressed(MouseEvent e)
231 		{
232 			if (e.isPopupTrigger())
233 				showPopup(e);
234 		}
235 
236 		public void mouseReleased(MouseEvent e)
237 		{
238 			if (e.isPopupTrigger())
239 				showPopup(e);
240 		}
241 
242 		public void showPopup(MouseEvent e)
243 		{
244 			int row = testLogList.locationToIndex(e.getPoint());
245 			if (row == -1)
246 				return;
247 
248 			if (testLogList.getSelectedIndex() != row)
249 			{
250 				testLogList.setSelectedIndex(row);
251 			}
252 
253 			TestStepResult result = logListModel.getResultAt(row);
254 			if (result == null)
255 				return;
256 
257 			ActionList actions = result.getActions();
258 
259 			if (actions == null || actions.getActionCount() == 0)
260 				return;
261 
262 			JPopupMenu popup = ActionSupport.buildPopup(actions);
263 			UISupport.showPopup(popup, testLogList, e.getPoint());
264 		}
265 	}
266 
267 	public synchronized void clear()
268 	{
269 		logListModel.clear();
270 		boldTexts.clear();
271 	}
272 
273 	public synchronized void addText(String string)
274 	{
275 		logListModel.addText( string );
276 		if( follow  )
277 			testLogList.ensureIndexIsVisible( logListModel.getSize()-1 );
278 	}
279 
280 	public synchronized void addTestStepResult(TestStepResult stepResult)
281 	{
282 		if( errorsOnly && stepResult.getStatus() != TestStepResult.TestStepStatus.FAILED )
283 			return;
284 		
285 		logListModel.addTestStepResult( stepResult );
286 		if( follow  )
287 		{
288 			try
289 			{
290 				testLogList.ensureIndexIsVisible( logListModel.getSize() - 1 );
291 			}
292 			catch( RuntimeException e )
293 			{
294 			}
295 		}
296 	}
297 
298 	public TestCaseLogModel getLogListModel()
299 	{
300 		return logListModel;
301 	}
302 
303 	public void setLogListModel(TestCaseLogModel logListModel)
304 	{
305 		this.logListModel = logListModel;
306 		testLogList.setModel( logListModel );
307 	}
308 	
309 	private class SetLogOptionsAction extends AbstractAction
310 	{
311 		public SetLogOptionsAction()
312 		{
313 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/options.gif" ));
314 			putValue( Action.SHORT_DESCRIPTION, "Sets TestCase Log Options");
315 		}
316 		
317 		public void actionPerformed( ActionEvent e )
318 		{
319 			if( optionsDialog == null )
320 				optionsDialog = ADialogBuilder.buildDialog( OptionsForm.class );
321 			
322 			optionsDialog.setIntValue( OptionsForm.MAXROWS, ( int ) settings.getLong( 
323 						OptionsForm.class.getName() + "@max_rows", 1000 ));
324 			optionsDialog.setBooleanValue( OptionsForm.DISCARDREMOVED, settings.getBoolean(  
325 						OptionsForm.class.getName() + "@discard_removed" ));
326 			optionsDialog.setBooleanValue( OptionsForm.ERRORSONLY, settings.getBoolean(  
327 						OptionsForm.class.getName() + "@errors_only" ));
328 			optionsDialog.setBooleanValue( OptionsForm.FOLLOW, follow );
329 			
330 			if( optionsDialog.show() )
331 			{
332 				int maxRows = optionsDialog.getIntValue( OptionsForm.MAXROWS, 1000 );
333 				logListModel.setMaxSize( maxRows );
334 				settings.setLong( OptionsForm.class.getName() + "@max_rows", maxRows );
335 				errorsOnly = optionsDialog.getBooleanValue( OptionsForm.ERRORSONLY );
336 				settings.setBoolean( OptionsForm.class.getName() + "@errors_only", 
337 							errorsOnly );
338 				
339 				boolean discardRemoved = optionsDialog.getBooleanValue( OptionsForm.DISCARDREMOVED );
340 				settings.setBoolean( OptionsForm.class.getName() + "@discard_removed", discardRemoved );
341 				logListModel.setDiscardRemoved( discardRemoved );
342 				
343 				follow = optionsDialog.getBooleanValue( OptionsForm.FOLLOW );
344 			}
345 		}
346 	}
347 	
348 	@AForm( name="Log Options", description="Set options for the run log below" )
349 	private static interface OptionsForm 
350 	{
351 		@AField( name="Max Rows", description="Sets the maximum number of rows to keep in the log", type=AFieldType.INT)
352 		public static final String MAXROWS = "Max Rows";
353 
354 		@AField( name="Discard Removed", description="Discards TestStep results rolled out from log", type=AFieldType.BOOLEAN)
355 		public static final String DISCARDREMOVED = "Discard Removed";
356 		
357 		@AField( name="Errors Only", description="Logs only TestStep errors in the log", type=AFieldType.BOOLEAN)
358 		public static final String ERRORSONLY = "Errors Only";
359 		
360 		@AField( name="Follow", description="Follow log content", type=AFieldType.BOOLEAN)
361 		public static final String FOLLOW = "Follow";
362 	}
363 	
364 	private class ClearLogAction extends AbstractAction
365 	{
366 		public ClearLogAction()
367 		{
368 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_loadtest.gif" ));
369 			putValue( Action.SHORT_DESCRIPTION, "Clears the log");
370 		}
371 		
372 		public void actionPerformed( ActionEvent e )
373 		{
374 			logListModel.clear();
375 		}
376 	}
377 	
378 	private class ExportLogAction extends AbstractAction
379 	{
380 		public ExportLogAction()
381 		{
382 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/export.gif" ));
383 			putValue( Action.SHORT_DESCRIPTION, "Exports this log to a file");
384 		}
385 		
386 		public void actionPerformed( ActionEvent e )
387 		{
388 			File file = UISupport.getFileDialogs().saveAs( this, "Save Log" );
389 			if( file != null )
390 			{
391 				try
392 				{
393 					PrintWriter out = new PrintWriter( file );
394 					for( int c = 0; c < logListModel.getSize(); c++ )
395 					{
396 						Object value = logListModel.getElementAt( c );
397 						if (value instanceof String)
398 						{
399 							out.println(value.toString());
400 						}
401 						else if (value instanceof TestCaseLogItem)
402 						{
403 							TestCaseLogItem logItem = (TestCaseLogItem) value;
404 							String msg = logItem.getMsg();
405 							if( StringUtils.hasContent( msg ))
406 								out.println( msg);
407 						}
408 					}
409 					
410 					out.close();
411 				}
412 				catch( FileNotFoundException e1 )
413 				{
414 					UISupport.showErrorMessage( e1 );
415 				}
416 			}
417 		}
418 	}
419 	
420 	public static class TestRunLogTestRunListener extends TestRunListenerAdapter
421 	{
422 		private SimpleDateFormat dateFormat;
423 		private final TestRunLog runLog;
424 		private final boolean clearOnRun;
425 
426 		public TestRunLogTestRunListener( TestRunLog runLog, boolean clearOnRun )
427 		{
428 			this.runLog = runLog;
429 			this.clearOnRun = clearOnRun;
430 			dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" );
431 		}
432 
433 		public void beforeRun( TestRunner testRunner, TestRunContext runContext )
434 		{
435 			if( SoapUI.getTestMonitor().hasRunningLoadTest( testRunner.getTestCase() ) )
436 				return;
437 
438 			if( clearOnRun )
439 				runLog.clear();
440 
441 			String testCaseName = testRunner.getTestCase().getName();
442 			runLog.addBoldText( "TestCase [" + testCaseName + "] started at " + dateFormat.format( new Date() ) );
443 			runLog.setStepIndex( 0 );
444 		}
445 
446 		public void afterRun( TestRunner testRunner, TestRunContext runContext )
447 		{
448 			if( SoapUI.getTestMonitor().hasRunningLoadTest( testRunner.getTestCase() ) )
449 				return;
450 
451 			WsdlTestCaseRunner wsdlRunner = ( WsdlTestCaseRunner ) testRunner;
452 
453 			String testCaseName = testRunner.getTestCase().getName();
454 			if( testRunner.getStatus() == TestRunner.Status.CANCELED )
455 				runLog.addText( "TestCase [" + testCaseName + "] canceled [" + testRunner.getReason() + "], time taken = "
456 							+ wsdlRunner.getTimeTaken() );
457 			else if( testRunner.getStatus() == TestRunner.Status.FAILED )
458 			{
459 				String msg = wsdlRunner.getReason();
460 				if(  wsdlRunner.getError() != null )
461 				{
462 					if( msg != null )
463 						msg += ":";
464 				
465 					msg += wsdlRunner.getError();
466 				}
467 				
468 				runLog.addText( "TestCase [" + testCaseName + "] failed [" + msg + "], time taken = "
469 							+ wsdlRunner.getTimeTaken() );
470 			}
471 			else 
472 				runLog.addText( "TestCase [" + testCaseName + "] finished with status [" + testRunner.getStatus() + "], time taken = " + wsdlRunner.getTimeTaken() );
473 		}
474 
475 		public void afterStep( TestRunner testRunner, TestRunContext runContext, TestStepResult stepResult )
476 		{
477 			if( SoapUI.getTestMonitor().hasRunningLoadTest( testRunner.getTestCase() ) )
478 				return;
479 
480 			runLog.addTestStepResult( stepResult );
481 		}
482 	}
483 
484 	public void setStepIndex( int i )
485 	{
486 		logListModel.setStepIndex( i );
487 	}
488 
489 	public synchronized void addBoldText( String string )
490 	{
491 		boldTexts.add( string );
492 		addText( string );
493 	}
494 
495 	public void release()
496 	{
497 		if( optionsDialog != null )
498 		{
499 			optionsDialog.release();
500 			optionsDialog = null;
501 		}
502 	}
503 }