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 }