View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 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.ERRORSONLY, settings.getBoolean(  
325 						OptionsForm.class.getName() + "@errors_only" ));
326 			optionsDialog.setBooleanValue( OptionsForm.FOLLOW, follow );
327 			
328 			if( optionsDialog.show() )
329 			{
330 				int maxRows = optionsDialog.getIntValue( OptionsForm.MAXROWS, 1000 );
331 				logListModel.setMaxSize( maxRows );
332 				settings.setLong( OptionsForm.class.getName() + "@max_rows", maxRows );
333 				errorsOnly = optionsDialog.getBooleanValue( OptionsForm.ERRORSONLY );
334 				settings.setBoolean( OptionsForm.class.getName() + "@errors_only", 
335 							errorsOnly );
336 				
337 				follow = optionsDialog.getBooleanValue( OptionsForm.FOLLOW );
338 			}
339 		}
340 	}
341 	
342 	@AForm( name="Log Options", description="Set options for the run log below" )
343 	private static interface OptionsForm 
344 	{
345 		@AField( name="Max Rows", description="Sets the maximum number of rows to keep in the log", type=AFieldType.INT)
346 		public static final String MAXROWS = "Max Rows";
347 
348 		@AField( name="Errors Only", description="Logs only TestStep errors in the log", type=AFieldType.BOOLEAN)
349 		public static final String ERRORSONLY = "Errors Only";
350 		
351 		@AField( name="Follow", description="Follow log content", type=AFieldType.BOOLEAN)
352 		public static final String FOLLOW = "Follow";
353 	}
354 	
355 	private class ClearLogAction extends AbstractAction
356 	{
357 		public ClearLogAction()
358 		{
359 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_loadtest.gif" ));
360 			putValue( Action.SHORT_DESCRIPTION, "Clears the log");
361 		}
362 		
363 		public void actionPerformed( ActionEvent e )
364 		{
365 			logListModel.clear();
366 		}
367 	}
368 	
369 	private class ExportLogAction extends AbstractAction
370 	{
371 		public ExportLogAction()
372 		{
373 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/export.gif" ));
374 			putValue( Action.SHORT_DESCRIPTION, "Exports this log to a file");
375 		}
376 		
377 		public void actionPerformed( ActionEvent e )
378 		{
379 			File file = UISupport.getFileDialogs().saveAs( this, "Save Log" );
380 			if( file != null )
381 			{
382 				try
383 				{
384 					PrintWriter out = new PrintWriter( file );
385 					for( int c = 0; c < logListModel.getSize(); c++ )
386 					{
387 						Object value = logListModel.getElementAt( c );
388 						if (value instanceof String)
389 						{
390 							out.println(value.toString());
391 						}
392 						else if (value instanceof TestCaseLogItem)
393 						{
394 							TestCaseLogItem logItem = (TestCaseLogItem) value;
395 							String msg = logItem.getMsg();
396 							if( StringUtils.hasContent( msg ))
397 								out.println( msg);
398 						}
399 					}
400 					
401 					out.close();
402 				}
403 				catch( FileNotFoundException e1 )
404 				{
405 					UISupport.showErrorMessage( e1 );
406 				}
407 			}
408 		}
409 	}
410 	
411 	public static class TestRunLogTestRunListener extends TestRunListenerAdapter
412 	{
413 		private SimpleDateFormat dateFormat;
414 		private final TestRunLog runLog;
415 		private final boolean clearOnRun;
416 
417 		public TestRunLogTestRunListener( TestRunLog runLog, boolean clearOnRun )
418 		{
419 			this.runLog = runLog;
420 			this.clearOnRun = clearOnRun;
421 			dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" );
422 		}
423 
424 		public void beforeRun( TestRunner testRunner, TestRunContext runContext )
425 		{
426 			if( SoapUI.getTestMonitor().hasRunningLoadTest( testRunner.getTestCase() ) )
427 				return;
428 
429 			if( clearOnRun )
430 				runLog.clear();
431 
432 			String testCaseName = testRunner.getTestCase().getName();
433 			runLog.addBoldText( "TestCase [" + testCaseName + "] started at " + dateFormat.format( new Date() ) );
434 			runLog.setStepIndex( 0 );
435 		}
436 
437 		public void afterRun( TestRunner testRunner, TestRunContext runContext )
438 		{
439 			if( SoapUI.getTestMonitor().hasRunningLoadTest( testRunner.getTestCase() ) )
440 				return;
441 
442 			WsdlTestCaseRunner wsdlRunner = ( WsdlTestCaseRunner ) testRunner;
443 
444 			String testCaseName = testRunner.getTestCase().getName();
445 			if( testRunner.getStatus() == TestRunner.Status.CANCELED )
446 				runLog.addText( "TestCase [" + testCaseName + "] canceled [" + testRunner.getReason() + "], time taken = "
447 							+ wsdlRunner.getTimeTaken() );
448 			else if( testRunner.getStatus() == TestRunner.Status.FAILED )
449 			{
450 				String msg = wsdlRunner.getReason();
451 				if(  wsdlRunner.getError() != null )
452 				{
453 					if( msg != null )
454 						msg += ":";
455 				
456 					msg += wsdlRunner.getError();
457 				}
458 				
459 				runLog.addText( "TestCase [" + testCaseName + "] failed [" + msg + "], time taken = "
460 							+ wsdlRunner.getTimeTaken() );
461 			}
462 			else 
463 				runLog.addText( "TestCase [" + testCaseName + "] finished with status [" + testRunner.getStatus() + "], time taken = " + wsdlRunner.getTimeTaken() );
464 		}
465 
466 		public void afterStep( TestRunner testRunner, TestRunContext runContext, TestStepResult stepResult )
467 		{
468 			if( SoapUI.getTestMonitor().hasRunningLoadTest( testRunner.getTestCase() ) )
469 				return;
470 
471 			runLog.addTestStepResult( stepResult );
472 		}
473 	}
474 
475 	public void setStepIndex( int i )
476 	{
477 		logListModel.setStepIndex( i );
478 	}
479 
480 	public synchronized void addBoldText( String string )
481 	{
482 		boldTexts.add( string );
483 		addText( string );
484 	}
485 
486 	public void release()
487 	{
488 		if( optionsDialog != null )
489 		{
490 			optionsDialog.release();
491 			optionsDialog = null;
492 		}
493 	}
494 }