1
2
3
4
5
6
7
8
9
10
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 }