001: /******************************************************************************
002: * Copyright (C) Lars Ivar Almli. All rights reserved. *
003: * ---------------------------------------------------------------------------*
004: * This file is part of MActor. *
005: * *
006: * MActor is free software; you can redistribute it and/or modify *
007: * it under the terms of the GNU General Public License as published by *
008: * the Free Software Foundation; either version 2 of the License, or *
009: * (at your option) any later version. *
010: * *
011: * MActor is distributed in the hope that it will be useful, *
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
014: * GNU General Public License for more details. *
015: * *
016: * You should have received a copy of the GNU General Public License *
017: * along with MActor; if not, write to the Free Software *
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
019: ******************************************************************************/package org.mactor.ui;
020:
021: import java.awt.BorderLayout;
022: import java.awt.CardLayout;
023: import java.awt.Color;
024: import java.awt.Component;
025: import java.awt.Dimension;
026: import java.awt.FlowLayout;
027: import java.awt.Font;
028: import java.awt.GridLayout;
029: import java.awt.event.ActionEvent;
030: import java.awt.event.ActionListener;
031: import java.io.File;
032: import java.util.LinkedList;
033: import java.util.List;
034: import javax.swing.BorderFactory;
035: import javax.swing.JButton;
036: import javax.swing.JFileChooser;
037: import javax.swing.JLabel;
038: import javax.swing.JOptionPane;
039: import javax.swing.JPanel;
040: import javax.swing.JScrollPane;
041: import javax.swing.JSplitPane;
042: import javax.swing.JTabbedPane;
043: import javax.swing.JTable;
044: import javax.swing.JTextPane;
045: import javax.swing.JTree;
046: import javax.swing.ListSelectionModel;
047: import javax.swing.SwingUtilities;
048: import javax.swing.ToolTipManager;
049: import javax.swing.event.ListSelectionEvent;
050: import javax.swing.event.ListSelectionListener;
051: import javax.swing.table.AbstractTableModel;
052: import javax.swing.table.TableCellRenderer;
053: import javax.swing.tree.DefaultMutableTreeNode;
054: import javax.swing.tree.DefaultTreeCellRenderer;
055: import org.mactor.framework.MactorException;
056: import org.mactor.framework.TestContext;
057: import org.mactor.framework.TestData;
058: import org.mactor.framework.TestEvent;
059: import org.mactor.framework.TestFeedbackListener;
060: import org.mactor.framework.TestSuiteRunner;
061: import org.mactor.framework.TestSuiteSummary;
062: import org.mactor.framework.TestSummary;
063: import org.mactor.framework.TestEvent.EventType;
064: import org.mactor.framework.spec.MessageReceiveSpec;
065: import org.mactor.framework.spec.SpecNode;
066: import org.mactor.framework.spec.TestSpec;
067:
068: /**
069: * Visualization of one test
070: *
071: * @author Lars Ivar Almli
072: */
073: public class TestPanel extends JPanel {
074: JTree tree;
075: JTable table;
076: TestSpec testSpec;
077: TestData data;
078: private TestResultTableModel dataModel;
079: JButton excuteTestButton;
080: JButton writeReportButton;
081: DetailsPanel detailsPanel = new DetailsPanel();
082: TestProgressInfo[] log;
083: TestSuiteRunner tsr;
084: TestSuiteSummary suiteSummary = new TestSuiteSummary();
085: JFileChooser fc;
086:
087: SummaryPanel summaryPanel = new SummaryPanel();
088: private TestFeedbackListener tfl = new TestFeedbackListener() {
089: public void onEvent(TestEvent event, TestContext contex) {
090: if (log[event.getDataIndex()] == null)
091: log[event.getDataIndex()] = new TestProgressInfo(contex);
092: if (log[event.getDataIndex()].addEvent(event)) { // if complete
093: suiteSummary.addSummary(log[event.getDataIndex()]
094: .getTestSummary());
095: summaryPanel.setInfo(log.length, suiteSummary);
096: }
097: dataModel.setLastEvent(event);
098: if (event.getDataIndex() == table.getSelectedRow())
099: detailsPanel.updateDetails(log[event.getDataIndex()]);
100:
101: }
102: };
103:
104: public TestPanel(TestSpec testSpec, TestData data)
105: throws MactorException {
106: this .testSpec = testSpec;
107: this .data = data;
108: setLayout(new GridLayout(1, 0));
109: setBorder(BorderFactory.createEtchedBorder());
110: log = new TestProgressInfo[data.getRowCount()];
111: tsr = new TestSuiteRunner(testSpec);
112: JTabbedPane tp = new JTabbedPane();
113: {
114: {
115: tree = new JTree(createTree(testSpec));
116: tree.setCellRenderer(new TestSpecRenderer());
117: JScrollPane sp = new JScrollPane(tree);
118: ToolTipManager.sharedInstance().registerComponent(tree);
119: sp.setPreferredSize(new Dimension(350, 400));
120: tp.addTab("Test Spec", sp);
121: }
122: {
123: JTextPane textPane = new JTextPane();
124: textPane.setText(testSpec.asXML());
125: JScrollPane sp = new JScrollPane(textPane);
126: sp.setPreferredSize(new Dimension(350, 400));
127: tp.addTab("Test Spec - XML", sp);
128: }
129: {
130: JTextPane textPane = new JTextPane();
131: textPane.setText(tsr.getGlobalConfig().asXML());
132: JScrollPane sp = new JScrollPane(textPane);
133: sp.setPreferredSize(new Dimension(350, 400));
134: tp.addTab("Global Config - XML", sp);
135: }
136: {
137: JTextPane textPane = new JTextPane();
138: textPane.setText(tsr.getMessageBrokersConfig().asXML());
139: JScrollPane sp = new JScrollPane(textPane);
140: sp.setPreferredSize(new Dimension(350, 400));
141: tp.addTab("Message Broker Config - XML", sp);
142: }
143: }
144: JPanel dataPanel = new JPanel(new BorderLayout());
145: {
146: dataModel = new TestResultTableModel(data);
147: table = new JTable(dataModel);
148: table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
149: // table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
150: table.setDefaultRenderer(String.class,
151: new TestResultTableRenderer());
152: table.getSelectionModel().addListSelectionListener(
153: new ListSelectionListener() {
154: public void valueChanged(
155: ListSelectionEvent event) {
156: SwingUtilities.invokeLater(new Runnable() {
157: public void run() {
158: int row = table.getSelectedRow();
159: if (row >= 0 && row < log.length)
160: detailsPanel
161: .updateDetails(log[row]);
162: }
163: });
164: }
165: });
166: JScrollPane tableSp = new JScrollPane(table);
167: JSplitPane tableDetailsSplitPane = new JSplitPane(
168: JSplitPane.HORIZONTAL_SPLIT, tableSp, detailsPanel);
169: tableDetailsSplitPane.setDividerLocation(400);
170: excuteTestButton = new JButton("Run");
171: writeReportButton = new JButton("Export report...");
172: writeReportButton.setEnabled(false);
173: JPanel buttonPannel = new JPanel(new FlowLayout());
174: buttonPannel.add(excuteTestButton);
175: buttonPannel.add(writeReportButton);
176: buttonPannel.add(summaryPanel);
177: JPanel topPanel = new JPanel(new BorderLayout());
178: topPanel.add(buttonPannel, BorderLayout.WEST);
179: dataPanel.add(topPanel, BorderLayout.NORTH);
180: dataPanel.add(tableDetailsSplitPane, BorderLayout.CENTER);
181: excuteTestButton.addActionListener(new ActionListener() {
182: public void actionPerformed(ActionEvent e) {
183: dataModel.clearResults();
184: suiteSummary = new TestSuiteSummary();
185: log = new TestProgressInfo[TestPanel.this .data
186: .getRowCount()];
187: summaryPanel.setInfo(log.length, suiteSummary);
188: new GuiTestThread(tsr, TestPanel.this .data, tfl)
189: .start();
190: excuteTestButton.setEnabled(false);
191: writeReportButton.setEnabled(true);
192: }
193: });
194: writeReportButton.addActionListener(new ActionListener() {
195: public void actionPerformed(ActionEvent e) {
196: if (fc == null) {
197: fc = new JFileChooser();
198: fc.setSelectedFile(new File("report.xml"));
199: fc
200: .setFileSelectionMode(JFileChooser.FILES_ONLY);
201: }
202: int returnVal = fc.showSaveDialog(TestPanel.this );
203: if (returnVal == JFileChooser.APPROVE_OPTION) {
204: File f = fc.getSelectedFile();
205: try {
206: ReportUtil.writeReportToFile(f,
207: suiteSummary, getSummaries());
208: } catch (Exception me) {
209: me.printStackTrace();
210: JOptionPane.showMessageDialog(
211: TestPanel.this , "Error:"
212: + me.getMessage(),
213: "Failed to wite the file",
214: JOptionPane.ERROR_MESSAGE);
215: }
216: }
217: }
218: });
219: }
220: JSplitPane splitPane = new JSplitPane(
221: JSplitPane.HORIZONTAL_SPLIT, tp, dataPanel);
222: splitPane.setDividerLocation(370);
223: add(splitPane);
224: }
225:
226: private List<TestSummary> getSummaries() {
227: List<TestSummary> summaries = new LinkedList<TestSummary>();
228: for (TestProgressInfo pi : log) {
229: if (pi != null && pi.getTestSummary() != null)
230: summaries.add(pi.getTestSummary());
231: }
232: return summaries;
233: }
234:
235: public void terminate() {
236: if (tsr != null)
237: tsr.terminate();
238: }
239:
240: private DefaultMutableTreeNode createTree(TestSpec testSpec) {
241: DefaultMutableTreeNode top = new DefaultMutableTreeNode(
242: testSpec);
243: addNodes(top, testSpec.getSpecNodes());
244: return top;
245: }
246:
247: private void addNodes(DefaultMutableTreeNode parent,
248: List<SpecNode> nodes) {
249: for (SpecNode specNode : nodes) {
250: DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(
251: specNode);
252: parent.add(treeNode);
253: if (specNode instanceof MessageReceiveSpec) {
254: addNodes(treeNode, ((MessageReceiveSpec) specNode)
255: .getSpecNodes());
256: }
257: }
258: }
259:
260: private static class TestSpecRenderer extends
261: DefaultTreeCellRenderer {
262: public Component getTreeCellRendererComponent(JTree tree,
263: Object value, boolean sel, boolean expanded,
264: boolean leaf, int row, boolean hasFocus) {
265: super .getTreeCellRendererComponent(tree, value, sel,
266: expanded, leaf, row, hasFocus);
267: SpecNode sn = (SpecNode) ((DefaultMutableTreeNode) value)
268: .getUserObject();
269: setText(sn.getShortDescription());
270: setToolTipText(sn.getDescription());
271: return this ;
272: }
273: }
274:
275: private class TestResultTableModel extends AbstractTableModel {
276: private int columnCount = 0;
277: private TestData data;
278: private TestEvent[] lastEventArray;
279:
280: public TestResultTableModel(TestData data) {
281: this .columnCount = data.getColumnCount() + 2;
282: this .data = data;
283: this .lastEventArray = new TestEvent[data.getRowCount()];
284: }
285:
286: @Override
287: public String getColumnName(int index) {
288: if (index == columnCount - 2)
289: return "Status";
290: if (index == columnCount - 1)
291: return "Last event";
292: return data.getColumn(index);
293: }
294:
295: public int getColumnCount() {
296: return columnCount;
297: }
298:
299: public int getRowCount() {
300: return data.getRowCount();
301: }
302:
303: @Override
304: public Class<?> getColumnClass(int arg0) {
305: return String.class;
306: }
307:
308: public Object getValueAt(int row, int col) {
309: if (col == columnCount - 2)
310: return getStatus(row);
311: if (col == columnCount - 1)
312: return getEventInfo(row);
313: return data.getValue(row, col);
314: }
315:
316: public void setLastEvent(TestEvent event) {
317: lastEventArray[event.getDataIndex()] = event;
318: super .fireTableRowsUpdated(event.getDataIndex(), event
319: .getDataIndex());
320: }
321:
322: private String getStatus(int row) {
323: if (lastEventArray[row] == null)
324: return "Not started";
325: SpecNode node = lastEventArray[row].getNode();
326: if ((node instanceof TestSpec)
327: && lastEventArray[row].getEventType() == EventType.End) {
328: if (lastEventArray[row].isSuccessful())
329: return "Successful";
330: return "Failed";
331: }
332: return "Running";
333: }
334:
335: private String getEventInfo(int row) {
336: if (lastEventArray[row] == null)
337: return "";
338: SpecNode node = lastEventArray[row].getNode();
339: if (lastEventArray[row].isStartEventType())
340: return "Performing:" + node.getShortDescription();
341: else if (lastEventArray[row].isSuccessful()) {
342: if (node instanceof TestSpec)
343: return "Test successfully completed";
344: return "Succesfully completed:"
345: + node.getShortDescription();
346: } else
347: return "Test completed with failure";
348: }
349:
350: public void clearResults() {
351: this .lastEventArray = new TestEvent[data.getRowCount()];
352: super .fireTableDataChanged();
353: }
354: }
355:
356: private class GuiTestThread extends Thread {
357: TestData data;
358: TestFeedbackListener listener;
359: TestSuiteRunner tsr;
360:
361: public GuiTestThread(TestSuiteRunner tsr, TestData data,
362: TestFeedbackListener listener) {
363: this .tsr = tsr;
364: this .data = data;
365: this .listener = new GuiTestFeedbackListener(listener);
366: }
367:
368: @Override
369: public void run() {
370: try {
371: tsr.run(data, listener);
372: } catch (MactorException me) {
373: me.printStackTrace();
374: }
375: SwingUtilities.invokeLater(new Runnable() {
376: public void run() {
377: excuteTestButton.setEnabled(true);
378: }
379: });
380: }
381:
382: private class GuiTestFeedbackListener implements
383: TestFeedbackListener {
384: TestFeedbackListener listener;
385:
386: public GuiTestFeedbackListener(TestFeedbackListener listener) {
387: this .listener = listener;
388: }
389:
390: public void onEvent(final TestEvent event,
391: final TestContext context) {
392: SwingUtilities.invokeLater(new Runnable() {
393: public void run() {
394: listener.onEvent(event, context);
395: }
396: });
397: }
398: }
399: }
400:
401: public class TestResultTableRenderer extends JLabel implements
402: TableCellRenderer {
403: boolean isBordered = true;
404: Font RUNNING_FONT;
405: Font NOT_STARTED_FONT;
406: Font COMPLETED_FONT;
407:
408: public TestResultTableRenderer() {
409: setOpaque(true);
410: RUNNING_FONT = getFont()
411: .deriveFont(Font.BOLD | Font.ITALIC);
412: NOT_STARTED_FONT = getFont().deriveFont(Font.PLAIN);
413: COMPLETED_FONT = getFont().deriveFont(Font.PLAIN);
414: }
415:
416: public Component getTableCellRendererComponent(JTable table,
417: Object object, boolean isSelected, boolean hasFocus,
418: int row, int column) {
419: setText(object + "");
420: setForeground(Color.BLACK);
421: TestEvent te = ((TestResultTableModel) table.getModel()).lastEventArray[row];
422: if (te == null) {
423: setFont(NOT_STARTED_FONT);
424: setBackground(Color.WHITE);
425: } else if (te.isSuccessfulTestCompleteEvent()) {
426: setFont(COMPLETED_FONT);
427: setBackground(Color.GREEN);
428: } else if (te.isFaultTestCompleteEvent()) {
429: setFont(COMPLETED_FONT);
430: setBackground(Color.RED);
431: } else {
432: setFont(RUNNING_FONT);
433: setBackground(Color.WHITE);
434: }
435: if (isSelected) {
436: setBackground(Color.BLUE);
437: setForeground(Color.WHITE);
438: }
439: return this ;
440: }
441: }
442:
443: private class DetailsPanel extends JPanel {
444: TestSummaryPanel summaryPanel = new TestSummaryPanel();
445: TestDetailsPanel detailsPanel = new TestDetailsPanel();
446: private String SUMMARY = "S";
447: private String DETAILS = "D";
448:
449: public DetailsPanel() {
450: super (new CardLayout());
451: add(summaryPanel, SUMMARY);
452: add(detailsPanel, DETAILS);
453: CardLayout cl = (CardLayout) (getLayout());
454: cl.show(this , DETAILS);
455: }
456:
457: public void updateDetails(TestProgressInfo entry) {
458: CardLayout cl = (CardLayout) (getLayout());
459: if (entry == null) {
460: detailsPanel.setTestDetails(null, null);
461: cl.show(this , DETAILS);
462: } else if (entry.hasTestSummary()) {
463: summaryPanel.setTestSummary(entry.getTestSummary());
464: cl.show(this , SUMMARY);
465: } else {
466: detailsPanel.setTestDetails(entry.getLog(), entry
467: .getContex());
468: cl.show(this , DETAILS);
469: }
470: }
471: }
472:
473: private class SummaryPanel extends JPanel {
474: JLabel label = new JLabel();
475:
476: public SummaryPanel() {
477: label.setFont(label.getFont().deriveFont(Font.BOLD, 9));
478: label.setBorder(null);
479: setBorder(null);
480: add(label);
481: }
482:
483: public void setInfo(int totalTestCount,
484: TestSuiteSummary suiteSummary) {
485: String text = "<html><table border=0 cellpadding=0>"
486: + "<tr><td>number of tests:</td><td>"
487: + totalTestCount
488: + "</td><td>successful:</td><td>"
489: + suiteSummary.getSuccessCount()
490: + "</td><td>avg time (ms):</td><td>"
491: + suiteSummary.getAverageSuccessCompleteTimeMs()
492: + "</td></tr>"
493: + "<tr><td>start time:</td><td> </td><td><font COLOR=RED>failed:</font></td><td><font COLOR=RED>"
494: + suiteSummary.getFailedCount()
495: + "</font></td><td><font COLOR=RED>avg time (ms):</font></td><td><font COLOR=RED>"
496: + suiteSummary.getAverageFailedCompleteTimeMs()
497: + "</font></td></tr>"
498: + "<tr><td>complete time:</td><td> </td><td><font COLOR=RED>failed:</font></td><td><font COLOR=RED>"
499: + suiteSummary.getFailedCount()
500: + "</font></td><td><font COLOR=RED>avg time (ms):</font></td><td><font COLOR=RED>"
501: + suiteSummary.getAverageFailedCompleteTimeMs()
502: + "</font></td></tr>" + "</table></html>";
503: //label.setText(text);
504: }
505: }
506: }
|