1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2002, 2016, 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 com.sun.javatest.exec.Session.Event;
  30 import java.awt.BorderLayout;
  31 import java.awt.EventQueue;
  32 import java.awt.event.ActionEvent;
  33 import java.awt.event.ActionListener;
  34 import java.io.File;
  35 
  36 import javax.swing.Action;
  37 import javax.swing.DefaultListModel;
  38 import javax.swing.JComponent;
  39 import javax.swing.JList;
  40 import javax.swing.JMenu;
  41 import javax.swing.JMenuItem;
  42 import javax.swing.JOptionPane;
  43 import javax.swing.JPanel;
  44 import javax.swing.JTextArea;
  45 import javax.swing.ScrollPaneConstants;
  46 
  47 import com.sun.javatest.Harness;
  48 import com.sun.javatest.InterviewParameters;
  49 import com.sun.javatest.Parameters;
  50 import com.sun.javatest.TestResult;
  51 import com.sun.javatest.TestSuite;
  52 import com.sun.javatest.WorkDirectory;
  53 import com.sun.javatest.tool.Preferences;
  54 import com.sun.javatest.tool.ToolAction;
  55 import com.sun.javatest.tool.UIFactory;
  56 import com.sun.javatest.util.BackupPolicy;
  57 import com.sun.javatest.util.I18NResourceBundle;
  58 import com.sun.javatest.util.StringArray;
  59 import java.util.ArrayList;
  60 import java.util.Arrays;
  61 import java.util.List;
  62 import java.util.Map;
  63 
  64 class RunTestsHandler implements ET_RunTestControl, Session.Observer {
  65     RunTestsHandler(JComponent parent, ExecModel model, UIFactory uif) {
  66         this.parent = parent;
  67         this.model = model;
  68         this.uif = uif;
  69 
  70         initActions();
  71         initHarness();
  72     }
  73 
  74     /**
  75      * This method is normally invoked after QSW...
  76      */
  77     public void runTests() {
  78         start();
  79     }
  80     public void setConfig(Session config) {
  81         this.config = config;
  82     }
  83 
  84     public void setTreePanelModel(TreePanelModel tpm) {
  85         this.tpm = tpm;
  86 
  87         if (progMonitor != null)
  88             progMonitor.setTreePanelModel(tpm);
  89     }
  90 
  91     public JMenu getMenu() {
  92         JMenu menu = uif.createMenu("rh");
  93         menu.add(uif.createMenuItem(startAction));
  94 
  95         /* not ready yet, save for 3.1
  96         pauseCheckBox = uif.createCheckBoxMenuItem("rh", "pause", false);
  97         pauseCheckBox.setEnabled(false);
  98         runMenu.add(pauseCheckBox);
  99         */
 100 
 101         menu.add(uif.createMenuItem(stopAction));
 102 
 103         // custom menu items
 104         ContextManager cm = model.getContextManager();
 105         JavaTestMenuManager mm = null;
 106         if (cm != null) {
 107             mm = cm.getMenuManager();
 108             if (mm != null) {
 109                 JMenuItem[] items =
 110                     mm.getMenuItems(JavaTestMenuManager.RUN_PRIMARY);
 111                 if (items != null)
 112                     for (int i = 0; i < items.length; i++)
 113                         menu.add(items[i]);
 114             }
 115         }
 116 
 117         menu.addSeparator();
 118         menu.add(uif.createMenuItem(showProgressAction));
 119 
 120         // custom menu items, at the end
 121         if (mm != null) {
 122             JMenuItem[] items =
 123                 mm.getMenuItems(JavaTestMenuManager.RUN_OTHER);
 124             if (items != null) {
 125                 menu.addSeparator();
 126                 for (int i = 0; i < items.length; i++)
 127                     menu.add(items[i]);
 128             }
 129         }
 130 
 131         return menu;
 132     }
 133 
 134     Action[] getToolBarActions() {
 135         return new Action[] {
 136             startAction,
 137             stopAction
 138         };
 139     }
 140 
 141     public Harness getHarness() {
 142         return harness;
 143     }
 144 
 145     MessageStrip getMessageStrip() {
 146         if (messageStrip == null) {
 147             Monitor[] monitors = new Monitor[2];
 148             monitors[0] = new ElapsedTimeMonitor(mState, uif);
 149             monitors[1] = new RunProgressMonitor(mState, uif);
 150 
 151             ActionListener zoom = (new ActionListener() {
 152                     public void actionPerformed(ActionEvent e) {
 153                         setProgressMonitorVisible(!isProgressMonitorVisible());
 154                     }
 155                 });
 156             messageStrip = new MessageStrip(uif, monitors, mState, zoom);
 157             messageStrip.setRunningMonitor(monitors[1]);
 158             messageStrip.setIdleMonitor(monitors[0]);
 159             harness.addObserver(messageStrip);
 160         }
 161 
 162         return messageStrip;
 163     }
 164 
 165     public JComponent getViewComponent() {
 166         return getMessageStrip();
 167     }
 168 
 169 
 170     public List<Action> getToolBarActionList() {
 171         return Arrays.asList(getToolBarActions());
 172     }
 173 
 174     public void save(Map<String, String> m) {
 175     }
 176     public void restore(Map<String, String> m) {
 177     }
 178 
 179     public synchronized void dispose() {
 180         if (harness != null) {
 181             harness.stop();
 182             harness = null;
 183         }
 184 
 185         parent = null;
 186         model = null;
 187         config = null;
 188         tpm = null;
 189 
 190         if (progMonitor != null) {
 191             progMonitor.dispose();
 192         }
 193     }
 194 
 195     // should really be observing ExecModel
 196     public void updateGUI() {
 197         testSuite = model.getTestSuite();
 198         workDir = model.getWorkDirectory();
 199         //interviewParams = model.getInterviewParameters();
 200         // initialize the start button once the test suite is set
 201         if (testSuite != null && !model.isConfiguring()) {
 202             if (!startAction.isEnabled() && !stopAction.isEnabled())
 203                 startAction.setEnabled(true);
 204         } else {
 205             startAction.setEnabled(false);
 206         }
 207 
 208         // if is user press cancel at some moment while configuring
 209         waitForConfig = waitForConfig && model.isConfiguring();
 210     }
 211 
 212     /**
 213      * Starts test run. If configuration is not ready suggests configuring
 214      * first.
 215      */
 216     void start() {
 217         // UPDATE executeImmediate() if you change code here
 218 
 219         if (!interviewReady()) {
 220             model.configure();
 221             waitForConfig = true;
 222             whatToRun = null;
 223         }
 224         startIfReady();
 225 
 226     }
 227 
 228     /**
 229      * Starts test run if configuration is ready.
 230      */
 231     private void startIfReady() {
 232         startAction.setEnabled(false);
 233         if (interviewReady()) {
 234             waitForConfig = false;
 235             startHarness(config.getParameters());
 236         } else {
 237             startAction.setEnabled(!model.isConfiguring());
 238         }
 239 
 240     }
 241 
 242     /**
 243      * Handles special reconfiguration required for quick-pick test execution.
 244      * If not ready, suggests configuring first.
 245      * @param tests Null or zero length indicates all tests.  Otherwise,
 246      *        the strings must be root relative locations in the testsuite.
 247      */
 248     public void executeImmediate(String[] paths) {
 249 
 250         if (!interviewReady()) {
 251             model.configure();
 252             waitForConfig = true;
 253             whatToRun = paths;
 254         }
 255         executeImmediateIfReady(paths);
 256     }
 257 
 258 
 259     /**
 260      * Starts test run if configuration is ready.
 261      */
 262     private void executeImmediateIfReady(String[] paths) {
 263         if (!interviewReady()) {
 264             startAction.setEnabled(!model.isConfiguring());
 265             return;
 266         }
 267         waitForConfig = false;
 268         startAction.setEnabled(false);
 269         Parameters params = config.getParameters();
 270         // if we reach this point, we have a usable interview which
 271         // we can now alter it
 272         Object[] items = {params.getEnv().getName(),
 273                 TestTreePanel.createNodeListString(TestTreePanel.createNodeList(paths))};
 274         int option = 0;
 275         if (paths[0].equals(""))
 276             option = uif.showYesNoDialog("rh.confirmQuickAll",
 277                         new Object[] {params.getEnv().getName()});
 278         else {
 279             JPanel p = uif.createPanel("rh.confirmPanel", false);
 280             JTextArea msg = uif.createMessageArea("rh.confirmQuick",
 281                         new Object[] {params.getEnv().getName()});
 282             p.setLayout(new BorderLayout());
 283             p.add(msg, BorderLayout.NORTH);
 284             DefaultListModel<String> model = new DefaultListModel<>();
 285             for (int i = paths.length; i > 0; i--)
 286                 model.add(model.getSize(), paths[model.getSize()]);
 287 
 288             JList<?> list = uif.createList("rh.confirmList", model);
 289             p.add(uif.createScrollPane(list,
 290                 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
 291                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);
 292 
 293             option = uif.showCustomYesNoDialog("rh.confirmQuick", p);
 294         }
 295 
 296         if (option != JOptionPane.YES_OPTION) {
 297             startAction.setEnabled(true);
 298             return;
 299         }
 300 
 301         // copy interview
 302         if (localParams != null && (localParams instanceof InterviewParameters)) {
 303             ((InterviewParameters)localParams).dispose();
 304         }
 305 
 306         try {
 307             localParams = BasicSessionControl.clone(params);
 308         } catch (Session.Fault e) {
 309             throw new RuntimeException("TBD: i18n cannot clone parameters");
 310         }
 311 
 312         final Preferences p = Preferences.access();
 313         boolean useTests2Run = p.getPreference(ExecTool.TESTS2RUN_PREF, "true").equals("true");
 314 
 315         if (!useTests2Run) {
 316             // alter tests in interview
 317             // (should verify that TestsParameters is mutable)
 318             Parameters.TestsParameters tps = localParams.getTestsParameters();
 319             Parameters.MutableTestsParameters mtps = (Parameters.MutableTestsParameters)tps;
 320 
 321             if (paths == null || paths.length == 0 || paths[0].equals("")) {
 322                 mtps.setTestsMode(Parameters.MutableTestsParameters.ALL_TESTS);
 323             }
 324             else {
 325                 mtps.setTestsMode(Parameters.MutableTestsParameters.SPECIFIED_TESTS);
 326                 // validate them?
 327                 mtps.setTests(paths);
 328             }
 329         }
 330         else {
 331             if (paths == null || paths.length == 0 || paths[0].equals("")) {
 332                 // execute whatever is selected in configuration
 333             }
 334             else {
 335                 Parameters.TestsParameters tps = localParams.getTestsParameters();
 336                 Parameters.MutableTestsParameters mtps = (Parameters.MutableTestsParameters)tps;
 337                 if (mtps.getTestsMode() == Parameters.MutableTestsParameters.ALL_TESTS)
 338                     mtps.setTests(paths);
 339                 else {
 340                     // NOTE: the combined set of paths isn't optimized after being
 341                     // combined, code elsewhere takes care of this for us
 342                     mtps.setTestsMode(Parameters.MutableTestsParameters.SPECIFIED_TESTS);
 343                     String[] origTests = mtps.getSpecifiedTests();
 344 
 345                     if (origTests == null || origTests.length == 0) {
 346                         mtps.setTests(paths);
 347                     }
 348                     else {
 349                         String[] combined = reprocessTests2Run(paths, origTests);
 350                         if (combined == null || combined.length == 0) {
 351                             uif.showInformationDialog("rh.nointersection", paths);
 352                             return;
 353                         }
 354                         mtps.setTests(combined);
 355                     }
 356                 }
 357             }
 358         }
 359 
 360         // start harness
 361         startHarness(localParams);
 362     }
 363 
 364     /**
 365      * Merge the session tests to run against the tests selected to run in the
 366      * configuration.
 367      * @param requested paths requested by the user to run from the tree. May not be null.
 368      * @param iTests paths of tests to run specified in the interview. May not be null.
 369      * @return Resulting set of test paths that should be run.
 370      * @see com.sun.javatest.exec.ExecTool#TESTS2RUN_PREF
 371      * @since 4.2.1
 372      */
 373     static String[] reprocessTests2Run(final String[] requested, final String[] iTests) {
 374         ArrayList<String> result = new ArrayList<>();
 375     outer:
 376         for (int i = 0; i < requested.length; i++) {
 377             String curr = requested[i];
 378 
 379             for (int j = 0; j < iTests.length; j++) {
 380                 int slash = curr.lastIndexOf('/');
 381                 int pound = (slash == -1 ? curr.lastIndexOf('#') : curr.lastIndexOf(slash, '#'));
 382 
 383                 if (curr.startsWith(iTests[j]) &&
 384                     (curr.length() == iTests[j].length() || curr.charAt(iTests[j].length()) == '#' ||
 385                      curr.charAt(iTests[j].length()) == '/')) {
 386                     result.add(curr);
 387                     continue outer;
 388                 }
 389             }   // for j
 390         }   // for i
 391 
 392      outer2:
 393         for (int i = 0; i < iTests.length; i++) {
 394             String curr = iTests[i];
 395 
 396             for (int j = 0; j < requested.length; j++) {
 397                 int slash = curr.lastIndexOf('/');
 398                 int pound = (slash == -1 ? curr.lastIndexOf('#') : curr.lastIndexOf(slash, '#'));
 399 
 400                 if (curr.startsWith(requested[j]) &&
 401                     (curr.length() == requested[j].length() || curr.charAt(requested[j].length()) == '#' ||
 402                      curr.charAt(requested[j].length()) == '/')) {
 403                     result.add(curr);
 404                     // don't terminate search as in above case
 405                 }
 406             }   // for j
 407         }   // for i
 408 
 409         return result.toArray(new String[result.size()]);
 410     }
 411 
 412     /*
 413     private boolean wdReady() {
 414         if (workDir == null) {
 415             model.showWorkDirDialog(true);
 416             if (workDir == null) {
 417                 return false;
 418             }
 419         }
 420 
 421         return true;
 422     }
 423      */
 424 
 425     private boolean interviewReady() {
 426 //      sessionControl.ensureInterviewUpToDate();
 427 
 428         return workDir != null && config.isReady();
 429 
 430 /*
 431         interviewParams = model.getInterviewParameters();
 432 
 433         if (!interviewParams.isTemplate() && interviewParams.isFinishable())  {
 434             return true;
 435         } else {
 436             return false;
 437         }
 438   */
 439     }
 440 
 441     private void startHarness(Parameters ips) {
 442 //        sessionControl.beforeExecution();
 443         notifyStarted(ips); // things like update EL, propagation, etc.
 444         try {
 445             if (!config.getParameters().getWorkDirectory().getTestResultTable().isReady()) {
 446                 // get bundle not done inline to avoid i18n check problems
 447                 I18NResourceBundle i18n = uif.getI18NResourceBundle();
 448                 messageStrip.showMessage(i18n, "rh.waitToStart.txt");
 449             }
 450 
 451             harness.start(ips);
 452 //            sessionControl.afterExecution();
 453             notifyFinished(ips);
 454         }
 455         catch (Harness.Fault e) {
 456             uif.showError("rh", e.toString());
 457         }
 458     }
 459 
 460     /**
 461      * Initialize the harness object used by the tool to run tests.
 462      */
 463     private void initHarness() {
 464         harness = new Harness();
 465 
 466         // FIX
 467         // Set backup parameters; in time this might become more versatile:
 468         // for example, using preferences
 469         // Should also probably be static or at least shared between instances
 470         // Should be moved down into harness land? or at least reused by
 471         // batch mode and regtest; note: Preferences are currently a GUI feature.
 472         BackupPolicy backupPolicy = new BackupPolicy() {
 473             public int getNumBackupsToKeep(File file) {
 474                 return numBackupsToKeep;
 475             }
 476             public boolean isBackupRequired(File file) {
 477                 if (ignoreExtns != null) {
 478                     for (int i = 0; i < ignoreExtns.length; i++) {
 479                         if (file.getPath().endsWith(ignoreExtns[i]))
 480                             return false;
 481                     }
 482                 }
 483                 return true;
 484             }
 485             private int numBackupsToKeep = Integer.getInteger("javatest.backup.count", 5).intValue();
 486             private String[] ignoreExtns = StringArray.split(System.getProperty("javatest.backup.ignore", ".jtr"));
 487         };
 488 
 489         harness.setBackupPolicy(backupPolicy);
 490         observer = new HarnessObserver();
 491         harness.addObserver(observer);
 492 
 493         mState = new MonitorState(harness);
 494     }
 495 
 496     private boolean isProgressMonitorVisible() {
 497         if (progMonitor == null || !progMonitor.isVisible())
 498             return false;
 499         else
 500             return true;
 501     }
 502 
 503     private void setProgressMonitorVisible(boolean state) {
 504         if (progMonitor == null) {
 505             progMonitor = new ProgressMonitor(parent, uif, mState);
 506             progMonitor.setTreePanelModel(tpm);
 507         }
 508 
 509         progMonitor.setVisible(state);
 510     }
 511 
 512     private void initActions() {
 513         showProgressAction = new ToolAction(uif, "rh.progress") {
 514                 public void actionPerformed(ActionEvent e) {
 515                     setProgressMonitorVisible(true);
 516                 }
 517             };
 518 
 519         startAction = new ToolAction(uif, "rh.start", true) {
 520                 public void actionPerformed(ActionEvent e) {
 521                     start();
 522                 }
 523             };
 524 
 525         stopAction = new ToolAction(uif, "rh.stop", true) {
 526             {
 527                 // this action is initially disabled, and only enabled when
 528                 // tests are actually being run
 529                 setEnabled(false);
 530             }
 531 
 532             public void actionPerformed(ActionEvent e) {
 533                 harness.stop();
 534             }
 535         };
 536 
 537     }
 538 
 539     private JComponent parent;
 540     private ExecModel model;
 541     private Session config;
 542     //private BasicSessionControl sessionControl;
 543     private UIFactory uif;
 544     private TreePanelModel tpm;
 545 
 546     private TestSuite testSuite;
 547     private WorkDirectory workDir;
 548     //private InterviewParameters interviewParams;
 549 
 550     // to track copy of interviewParams in executeImmediate()
 551     private Parameters localParams;
 552 
 553     private Action showProgressAction;
 554     private Action startAction;
 555     private Action stopAction;
 556 
 557     private Harness harness;
 558     private HarnessObserver observer;
 559     private MonitorState mState;
 560 
 561     private MessageStrip messageStrip;
 562     private ProgressMonitor progMonitor;
 563 
 564     // flag indicating that user pressed "run"
 565     // button while config wasn't ready. And as soon as the config is
 566     // ready tests will be run
 567     private boolean waitForConfig;
 568 
 569     // contains paths of tests to run. used together with waitForConfig.
 570     // to remember paths passed via executeImmediate(paths).
 571     private String[] whatToRun;
 572 
 573     private final ArrayList<Observer> observers = new ArrayList<Observer>();
 574 
 575 
 576     public void addObserver(Observer obs) {
 577         if (obs != null && !observers.contains(obs)) {
 578             observers.add(obs);
 579         }
 580     }
 581 
 582     public void removeObserver(Observer obs) {
 583         if (obs != null && observers.contains(obs)) {
 584             observers.remove(obs);
 585         }
 586     }
 587 
 588     public void notifyStarted(Parameters p) {
 589         for (Observer obs: observers) {
 590             obs.startTests(p);
 591         }
 592     }
 593     public void notifyFinished(Parameters p) {
 594         for (Observer obs: observers) {
 595             obs.finishTests(p);
 596         }
 597     }
 598 
 599     public void updated(Event ev) {
 600         if (ev instanceof BasicSession.E_NewConfig) {
 601             if (waitForConfig) {
 602                 waitForConfig = false;
 603                 if (whatToRun == null) {
 604                     startIfReady();
 605                 } else {
 606                     executeImmediateIfReady(whatToRun);
 607                 }
 608             }
 609         } else if (ev instanceof BasicSessionControl.E_EditorVisibility) {
 610             updateGUI();
 611         }
 612     }
 613 
 614 
 615     private class HarnessObserver implements Harness.Observer {
 616         public void startingTestRun(final Parameters params) {
 617             EventQueue.invokeLater(new Runnable() {
 618                 public void run() {
 619                     startAction.setEnabled(false);
 620                     //pauseCheckBox.setEnabled(true);
 621                     stopAction.setEnabled(true);
 622 
 623                     // get bundle not done inline to avoid i18n check problems
 624                     I18NResourceBundle i18n = uif.getI18NResourceBundle();
 625                     messageStrip.showMessage(i18n, "rh.starting.txt");
 626                 }
 627             });
 628         }
 629 
 630         public void startingTest(TestResult tr) {
 631         }
 632 
 633         public void finishedTest(TestResult tr) {
 634         }
 635 
 636         public void stoppingTestRun() {
 637         }
 638 
 639         public void finishedTesting() {
 640         }
 641 
 642         public void finishedTestRun(boolean allOK) {
 643             EventQueue.invokeLater(new Runnable() {
 644                 public void run() {
 645                     startAction.setEnabled(true);
 646                     //pauseCheckBox.setEnabled(false);
 647                     stopAction.setEnabled(false);
 648                 }
 649             });
 650 
 651             if (localParams != null && (localParams instanceof InterviewParameters)) {
 652                 ((InterviewParameters)localParams).dispose();
 653                 localParams = null;
 654             }
 655         }
 656 
 657         public void error(final String msg) {
 658             EventQueue.invokeLater(new Runnable() {
 659                 public void run() {
 660                     uif.showError("rh.error", msg);
 661                 }
 662             });
 663         }
 664     }
 665 }