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.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 }