1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.exec;
  28 
  29 import java.awt.Component;
  30 import java.awt.CardLayout;
  31 import java.awt.Color;
  32 import java.awt.EventQueue;
  33 import java.awt.GridBagConstraints;
  34 import java.awt.GridBagLayout;
  35 import java.awt.event.ActionEvent;
  36 import java.awt.event.ActionListener;
  37 import java.awt.event.MouseAdapter;
  38 import java.awt.event.MouseEvent;
  39 import java.lang.reflect.InvocationTargetException;
  40 
  41 import javax.swing.BorderFactory;
  42 import javax.swing.Box;
  43 import javax.swing.DefaultListModel;
  44 import javax.swing.JButton;
  45 import javax.swing.JComponent;
  46 import javax.swing.JLabel;
  47 import javax.swing.JList;
  48 import javax.swing.JPanel;
  49 import javax.swing.JScrollPane;
  50 import javax.swing.JTextField;
  51 import javax.swing.Timer;
  52 import javax.swing.border.Border;
  53 
  54 import com.sun.javatest.Status;
  55 import com.sun.javatest.TestResult;
  56 import com.sun.javatest.tool.I18NUtils;
  57 import com.sun.javatest.tool.ToolDialog;
  58 import com.sun.javatest.tool.UIFactory;
  59 
  60 class ProgressMonitor extends ToolDialog {
  61 
  62     ProgressMonitor(Component parent, UIFactory uif, MonitorState state) {
  63         super(parent, uif, "pm", FRAME);
  64 
  65         /*ToolDialog
  66         super(parent, uif.getI18NString("pm.title"), false);
  67         */
  68 
  69         // ToolDialog this.uif = uif;
  70         this.state = state;
  71     }
  72 
  73     protected void initGUI() {
  74         setI18NTitle("pm.title");
  75         setHelp("browse.testMonitor.csh");
  76 
  77         subpanels = new StatusSubpanel[4];
  78 
  79         /*ToolDialog
  80         Container content = getContentPane();
  81         content.setName("progressD");
  82         content.setLayout(new BorderLayout());
  83         */
  84 
  85         JPanel body = new JPanel(new GridBagLayout());
  86         GridBagConstraints c = new GridBagConstraints();
  87         c.fill = GridBagConstraints.BOTH;
  88 
  89         // TOP
  90         c.gridwidth = 2;
  91         c.gridx = 0;
  92         c.gridy = 0;
  93         c.insets.left = 8;
  94         c.insets.right = 8;
  95         c.insets.top = 5;
  96         c.weightx = 1;
  97         c.weighty = 0;
  98         progressSubpanel = new ProgressSubpanel();
  99         subpanels[0] = progressSubpanel;
 100         body.add(progressSubpanel, c);
 101 
 102         // LEFT SIDE
 103         c.gridheight = 1;
 104         c.gridwidth = 1;
 105         c.gridx = 0;
 106         c.gridy = 1;
 107         c.insets.right = 0;
 108         c.weightx = 1;
 109         c.weighty = 2;
 110 
 111         timeSubpanel = new TimeSubpanel();
 112         subpanels[1] = timeSubpanel;
 113         body.add(timeSubpanel, c);
 114 
 115         c.gridy = 2;
 116         memorySubpanel = new MemorySubpanel();
 117         subpanels[2] = memorySubpanel;
 118         body.add(memorySubpanel, c);
 119 
 120         // RIGHT SIDE
 121         // list of tests running
 122         c.fill = GridBagConstraints.BOTH;
 123         c.gridheight = 2;
 124         c.gridwidth = 1;
 125         c.gridx = 1;
 126         c.gridy = 1;
 127         c.insets.right = 8;
 128         c.weightx = 3;
 129         c.weighty = 1;
 130         activitySubpanel = new ActivitySubpanel();
 131         subpanels[3] = activitySubpanel;
 132         body.add(activitySubpanel, c);
 133 
 134         setBody(body);
 135         /*ToolDialog
 136         content.add(body, BorderLayout.CENTER);
 137         */
 138 
 139         /*ToolDialog
 140         JPanel btnPanel = new JPanel(new GridBagLayout());
 141         GridBagConstraints bc = new GridBagConstraints();
 142         bc.anchor = GridBagConstraints.EAST;
 143         bc.insets.top = 5;
 144         bc.insets.bottom = 11;  // value from JL&F Guidelines
 145         bc.insets.right = 5;   // value from JL&F Guidelines
 146         bc.weightx = 1;
 147 
 148         JButton helpBtn = uif.createHelpButton("pm.help", "browse.testMonitor.csh");
 149         btnPanel.add(helpBtn, bc);
 150 
 151         JButton closeBtn = uif.createCloseButton("pm.close");
 152         bc.insets.right = 11;   // value from JL&F Guidelines
 153         bc.weightx = 0;
 154         btnPanel.add(closeBtn, bc);
 155 
 156         content.add(btnPanel, BorderLayout.SOUTH);
 157         */
 158         JButton helpBtn = uif.createHelpButton("pm.help", "browse.testMonitor.csh");
 159         JButton closeBtn = uif.createCloseButton("pm.close");
 160         setButtons(new JButton[] { helpBtn, closeBtn }, closeBtn);
 161 
 162         // other stuff
 163         state.addObserver(listener);
 164 
 165         if (state.isRunning())
 166             listener.starting();
 167 
 168         /*ToolDialog
 169         HelpBroker b = uif.getHelpBroker();
 170         //Desktop.addHelpDebugListener(progDialog);
 171         b.enableHelpKey(getRootPane(), "browse.testMonitor.csh", null);
 172         */
 173 
 174         //ToolDialog pack();
 175 
 176         for (int i = 0; i < subpanels.length; i++) {
 177             // make sure the panels are in any initial state
 178             // important for getting the "last" run info, less important if
 179             // there's a run in progress or a future runs
 180             subpanels[i].update();
 181         }
 182 
 183         /*ToolDialog
 184         getAccessibleContext().setAccessibleDescription(
 185                             uif.getI18NString("pm.desc"));
 186         */
 187     }
 188 
 189     void setTreePanelModel(TreePanelModel tpm) {
 190         this.tpm = tpm;
 191     }
 192 
 193     //----------member variables-----------------------------------------------------
 194 
 195     private MonitorState state;
 196     private TreePanelModel tpm;
 197 
 198     private ProgressSubpanel progressSubpanel;
 199     private ActivitySubpanel activitySubpanel;
 200     private TimeSubpanel timeSubpanel;
 201     private MemorySubpanel memorySubpanel;
 202 
 203     private StatusSubpanel[] subpanels;
 204 
 205     private volatile boolean running;
 206     private Listener listener = new Listener();
 207 
 208     private static final int UPDATE_FREQUENCY = 1000;
 209     private static int componentCount; // for generating names
 210 
 211 
 212     //----------nested classes-------------------------------------------------------
 213 
 214     class Listener implements MonitorState.Observer, ActionListener {
 215         Listener() {
 216             timer = new Timer(UPDATE_FREQUENCY, this);
 217         }
 218 
 219         public void actionPerformed(ActionEvent e) {
 220             if (e.getSource() == timer) {
 221                 for (int i = 0; i < subpanels.length; i++)
 222                     subpanels[i].update();
 223             }
 224         }
 225 
 226         // these are probably on whichever thread the harness is running on
 227         // be sure to switch them onto the event thread
 228         public void starting() {
 229             running = true;
 230 
 231             for (int i = 0; i < subpanels.length; i++)
 232                 subpanels[i].starting();
 233 
 234             timer.start();
 235         }
 236 
 237         public void postProcessing() {
 238             for (int i = 0; i < subpanels.length; i++) {
 239                     // make sure the panels update one last time
 240                 subpanels[i].update();
 241                 subpanels[i].postProcessing();
 242             }   // for
 243         }
 244 
 245         public void stopping() {
 246             // it is assumed that finished() will be called right away
 247         }
 248 
 249         public void finished(final boolean allOk) {
 250             running = false;
 251             for (int i = 0; i < subpanels.length; i++) {
 252                 // make sure the panels update one last time
 253                 subpanels[i].update();
 254                 subpanels[i].stopping();
 255             }   // for
 256 
 257             timer.stop();
 258         }
 259 
 260         private Timer timer;
 261     }
 262 
 263     private abstract class StatusSubpanel extends JPanel {
 264         /**
 265          * Refresh any data in this subpanel.
 266          * This method is always called on the event dispatch thread.
 267          */
 268         abstract void update();
 269 
 270         /**
 271          * Test run is starting.  This method can be ignored if it does not apply.
 272          */
 273         void starting() {
 274         }
 275 
 276         void postProcessing() {
 277         }
 278 
 279         /**
 280          * Test run is finishing.  This method can be ignored if it does not apply.
 281          */
 282         void stopping() {
 283         }
 284     }
 285 
 286     private class ProgressSubpanel extends StatusSubpanel
 287     {
 288         ProgressSubpanel() {
 289             setBorder(uif.createTitledBorder("pm.prog"));
 290             setLayout(new GridBagLayout());
 291 
 292             // Line label
 293             GridBagConstraints lnc = new GridBagConstraints();
 294             lnc.gridx = 0;
 295             lnc.anchor = GridBagConstraints.EAST;
 296             lnc.insets.right = 10;
 297 
 298             // Field label
 299             GridBagConstraints lc = new GridBagConstraints();
 300             lc.insets.right = 5;
 301 
 302             // Field
 303             GridBagConstraints fc = new GridBagConstraints();
 304             fc.weightx = 1;
 305             fc.fill = GridBagConstraints.HORIZONTAL;
 306             fc.insets.right = 15;
 307 
 308             // Remaining field
 309             GridBagConstraints rc = new GridBagConstraints();
 310             rc.weightx = 1;
 311             rc.fill = GridBagConstraints.BOTH;
 312             rc.gridwidth = GridBagConstraints.REMAINDER;
 313 
 314             JTextField tf = uif.createHeading("pm.tests");
 315             tf.setBackground(UIFactory.Colors.TRANSPARENT.getValue());
 316             uif.setAccessibleInfo(tf, "pm.tests");
 317             add(tf, lnc);
 318             JLabel lab = uif.createLabel("pm.tests.pass");
 319             add(lab, lc);
 320             passTf = uif.createOutputField("pm.tests.pass", 6);
 321             passTf.setHorizontalAlignment(JTextField.RIGHT);
 322             lab.setLabelFor(passTf);
 323             lab.setDisplayedMnemonic(uif.getI18NString("pm.tests.pass.mne").charAt(0));
 324             add(passTf, fc);
 325 
 326             lab = uif.createLabel("pm.tests.fail");
 327             lab.setDisplayedMnemonic(uif.getI18NString("pm.tests.fail.mne").charAt(0));
 328             add(lab, lc);
 329             failTf = uif.createOutputField("pm.tests.fail", 6);
 330             failTf.setHorizontalAlignment(JTextField.RIGHT);
 331             lab.setLabelFor(failTf);
 332             add(failTf, fc);
 333 
 334             lab = uif.createLabel("pm.tests.err");
 335             add(lab, lc);
 336             errorTf = uif.createOutputField("pm.tests.err", 6);
 337             lab.setDisplayedMnemonic(uif.getI18NString("pm.tests.err.mne").charAt(0));
 338             errorTf.setHorizontalAlignment(JTextField.RIGHT);
 339             lab.setLabelFor(errorTf);
 340             add(errorTf, fc);
 341 
 342             lab = uif.createLabel("pm.tests.nr");
 343             lab.setDisplayedMnemonic(uif.getI18NString("pm.tests.nr.mne").charAt(0));
 344             add(lab, lc);
 345             notRunTf = uif.createOutputField("pm.tests.nr", 6);
 346             notRunTf.setHorizontalAlignment(JTextField.RIGHT);
 347             lab.setLabelFor(notRunTf);
 348             add(notRunTf, rc);
 349             add(Box.createVerticalStrut(10), rc);
 350 
 351             Color[] colors = new Color[Status.NUM_STATES];
 352             colors[Status.PASSED] = I18NUtils.getStatusBarColor(Status.PASSED);
 353             colors[Status.FAILED] = I18NUtils.getStatusBarColor(Status.FAILED);
 354             colors[Status.ERROR] = I18NUtils.getStatusBarColor(Status.ERROR);
 355             colors[Status.NOT_RUN] = I18NUtils.getStatusBarColor(Status.NOT_RUN);
 356 
 357             /*
 358             String[] actions = new String[5 + 1];
 359             actions[Status.PASSED] = Integer.toString(Status.PASSED);
 360             actions[Status.FAILED] = Integer.toString(Status.FAILED);
 361             actions[Status.ERROR] = Integer.toString(Status.ERROR);
 362             actions[Status.NOT_RUN] = Integer.toString(Status.NOT_RUN);
 363             actions[actions.length - 1] = "-1";
 364             */
 365 
 366             tf = uif.createHeading("pm.tests.mtr");
 367             uif.setAccessibleInfo(tf, "pm.tests.mtr");
 368             add(tf, lnc);
 369             add(meter = new ProgressMeter(colors, state), rc);
 370             uif.setAccessibleInfo(meter, "pm.tests.bar");
 371             /*
 372             meter.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
 373 
 374             ActionListener al = new ActionListener() {
 375                 public void actionPerformed(ActionEvent e) {
 376                     model.showSummary(Integer.parseInt(e.getActionCommand()));
 377                 }
 378             };
 379 
 380             meter.addActionListener(al);
 381             */
 382             uif.setToolTip(this, "pm.tests");
 383         }
 384 
 385         public void update() {
 386             updateAll();
 387         }
 388 
 389         //--------------------------------------------------------------------------------
 390 
 391         private void updateAll() {
 392             meter.update();
 393 
 394             int[] stats = state.getStats();
 395 
 396             setCount(passTf, stats[Status.PASSED]);
 397             setCount(failTf, stats[Status.FAILED]);
 398             setCount(errorTf, stats[Status.ERROR]);
 399             setCount(notRunTf,state.getTestsRemainingCount());
 400         }
 401 
 402         private final void setCount(JTextField tf, int value) {
 403             if (EventQueue.isDispatchThread())
 404                 tf.setText(Integer.toString(value));
 405             else
 406                 try {
 407                     EventQueue.invokeAndWait(new BranchPanel.TextUpdater(tf,
 408                                              Integer.toString(value), uif));
 409                 }
 410                 catch (InterruptedException e) {
 411                 }
 412                 catch (InvocationTargetException e) {
 413                 }
 414         }
 415 
 416         private JTextField passTf;
 417         private JTextField failTf;
 418         private JTextField errorTf;
 419         private JTextField notRunTf;
 420 
 421         private ProgressMeter meter;
 422         private int[] meterStats;
 423     }
 424 
 425     private class ActivitySubpanel extends StatusSubpanel {
 426         ActivitySubpanel() {
 427             setBorder(uif.createTitledBorder("pm.activity"));
 428             setLayout(new CardLayout());
 429 
 430             idleCard = createSimpleCard("pm.idle");
 431             addCard(idleCard);
 432             initRunningCard();
 433             showCard(idleCard);
 434         }
 435 
 436         void starting() {
 437             showCard(runningCard);
 438             //StatusDialog.this.validate();
 439         }
 440 
 441         void postProcessing() {
 442             showCard(idleCard);
 443         }
 444 
 445         void stopping() {
 446         }
 447 
 448         void update() {
 449             testListData.removeAllElements();
 450             TestResult[] rt = state.getRunningTests();
 451             for (int i = 0; i < rt.length; i++)
 452                 testListData.addElement(rt[i]);
 453         }
 454 
 455         //------------------------------------------------------------------
 456 
 457         private JComponent createSimpleCard(String uiKey) {
 458             JPanel card = new JPanel();
 459             card.setLayout(new GridBagLayout());
 460             GridBagConstraints c = new GridBagConstraints();
 461             c.anchor = GridBagConstraints.CENTER;
 462             c.gridwidth = GridBagConstraints.REMAINDER;
 463             c.weightx = 1;
 464             c.weighty = 1;
 465             JTextField tf = uif.createHeading(uiKey);
 466             uif.setAccessibleInfo(tf, uiKey);
 467             card.add(tf, c);
 468 
 469             JLabel icon = uif.createIconLabel(uiKey);
 470             c.anchor = GridBagConstraints.WEST;
 471             c.weighty = 0;
 472             card.add(icon, c);
 473 
 474             return card;
 475         }
 476 
 477         private void initRunningCard() {
 478             testListData = new DefaultListModel<>();
 479             final JList<?> list = uif.createList("pm.runlist", testListData);
 480             list.setBorder(BorderFactory.createEtchedBorder());
 481             list.setCellRenderer(RenderingUtilities.createTestListRenderer());
 482             list.addMouseListener(new MouseAdapter() {
 483                     public void mouseClicked(MouseEvent e) {
 484                         int index = list.locationToIndex(e.getPoint());
 485                         if (index < 0)
 486                             return;
 487                         Object target = list.getModel().getElementAt(index);
 488                         if (target instanceof TestResult && tpm != null) {
 489                             tpm.showTest(((TestResult)target).getTestName());
 490                         }
 491                     }
 492                 }
 493             );
 494             runningCard =
 495                 uif.createScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
 496                                 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 497             addCard(runningCard);
 498         }
 499 
 500         private void addCard(JComponent comp) {
 501             String s = comp.getName();
 502             if (s == null)
 503                 comp.setName("StatusDialog.component" + componentCount++);
 504             add(comp, comp.getName());
 505         }
 506 
 507         private void showCard(JComponent comp) {
 508             //setVisible(false);
 509             ((CardLayout)(getLayout())).show(this, comp.getName());
 510             //setVisible(true);
 511         }
 512 
 513         private JComponent idleCard;
 514         private JComponent runningCard;
 515 
 516         private JTextField fileField;
 517         private DefaultListModel<TestResult> testListData;
 518         private String rootDir;
 519     }
 520 
 521     private class TimeSubpanel extends StatusSubpanel {
 522         TimeSubpanel() {
 523             setLayout(new GridBagLayout());
 524             Border b1 = uif.createTitledBorder("pm.time");
 525             Border b2 = BorderFactory.createEmptyBorder(10, 5, 10, 5);
 526             setBorder(BorderFactory.createCompoundBorder(b1, b2));
 527 
 528             // Line label
 529             GridBagConstraints lnc = new GridBagConstraints();
 530             lnc.gridx = 0;
 531             lnc.anchor = GridBagConstraints.EAST;
 532             lnc.insets.right = 10;
 533 
 534             // Remaining field
 535             GridBagConstraints rc = new GridBagConstraints();
 536             rc.fill = GridBagConstraints.BOTH;
 537             rc.gridwidth = GridBagConstraints.REMAINDER;
 538             rc.weightx = 1;
 539 
 540             JLabel lab = uif.createLabel("pm.time.sofar");
 541             lab.setDisplayedMnemonic(uif.getI18NString("pm.time.sofar.mne").charAt(0));
 542             add(lab, lnc);
 543             add(elapsedField = uif.createOutputField("pm.time.sofar", 8), rc);
 544             elapsedField.setText("00:00:00");
 545             lab.setLabelFor(elapsedField);
 546 
 547             lab = uif.createLabel("pm.time.remain");
 548             lab.setDisplayedMnemonic(uif.getI18NString("pm.time.remain.mne").charAt(0));
 549             add(lab, lnc);
 550             add(estimatedRemainingField = uif.createOutputField("pm.time.remain", 8), rc);
 551             estimatedRemainingField.setText("00:00:00");
 552             lab.setLabelFor(estimatedRemainingField);
 553 
 554             // workaround unknown GridBagLayout problem
 555             add(Box.createVerticalStrut(10), rc);
 556 
 557             uif.setToolTip(this, "pm.time");
 558         }
 559 
 560         void update() {
 561             long elapsed = state.getElapsedTime();
 562             elapsedField.setText(ElapsedTimeMonitor.millisToString(elapsed));
 563 
 564             long remain = state.getEstimatedTime();
 565             estimatedRemainingField.setText(ElapsedTimeMonitor.millisToString(remain));
 566         }
 567 
 568         private JTextField elapsedField;
 569         private JTextField estimatedRemainingField;
 570         //private ProgressMeter meter;
 571         private int[] meterStats = new int[2];
 572     }
 573 
 574     private class MemorySubpanel extends StatusSubpanel {
 575         MemorySubpanel() {
 576             setLayout(new GridBagLayout());
 577             Border b1 = uif.createTitledBorder("pm.memory");
 578             Border b2 = BorderFactory.createEmptyBorder(10, 5, 10, 5);
 579             setBorder(BorderFactory.createCompoundBorder(b1, b2));
 580 
 581             // Line label
 582             GridBagConstraints lnc = new GridBagConstraints();
 583             lnc.gridx = 0;
 584             lnc.anchor = GridBagConstraints.EAST;
 585             lnc.insets.right = 10;
 586             lnc.weightx = 0;
 587             lnc.fill = GridBagConstraints.NONE;
 588 
 589             // Remaining field
 590             GridBagConstraints rc = new GridBagConstraints();
 591             rc.fill = GridBagConstraints.BOTH;
 592             rc.gridwidth = GridBagConstraints.REMAINDER;
 593             rc.weightx = 1;
 594 
 595             JLabel lab = uif.createLabel("pm.memory.used");
 596             lab.setDisplayedMnemonic(uif.getI18NString("pm.memory.used.mne").charAt(0));
 597             add(lab, lnc);
 598             add(usedField = uif.createOutputField("pm.memory.used", 10), rc);
 599             lab.setLabelFor(usedField);
 600 
 601             lab = uif.createLabel("pm.memory.ttl");
 602             lab.setDisplayedMnemonic(uif.getI18NString("pm.memory.ttl.mne").charAt(0));
 603             add(lab, lnc);
 604             add(totalField = uif.createOutputField("pm.memory.ttl", 10), rc);
 605             lab.setLabelFor(totalField);
 606 
 607             // workaround unknown GridBagLayout problem
 608             add(Box.createVerticalStrut(10), rc);
 609 
 610             /*
 611             rc.fill = GridBagConstraints.VERTICAL;
 612             Color[] mc = {
 613                 new Color(128, 0, 0),
 614                 new Color(0, 128, 0)
 615             };
 616             add(meter = new ProgressMeter(mc, state), rc);
 617             */
 618             uif.setToolTip(this, "pm.memory");
 619         }
 620 
 621         void update() {
 622             int freeMem = (int)(runtime.freeMemory() / 1024);
 623             int totalMem = (int)(runtime.totalMemory() / 1024);
 624             int usedMem = totalMem - freeMem;
 625             usedField.setText(usedMem + "K");
 626             totalField.setText(totalMem + "K");
 627 
 628         }
 629 
 630         private JTextField usedField;
 631         private JTextField totalField;
 632         private ProgressMeter meter;
 633         private int[] meterStats = new int[2];
 634         private Runtime runtime = Runtime.getRuntime();
 635     }
 636 }