1 /* 2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.awt.*; 25 import java.awt.event.*; 26 import java.awt.peer.ComponentPeer; 27 import java.lang.reflect.Constructor; 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 import java.util.ArrayList; 31 import javax.swing.*; 32 33 import sun.awt.AWTAccessor; 34 import sun.awt.EmbeddedFrame; 35 import java.io.*; 36 import test.java.awt.regtesthelpers.Util; 37 38 /** 39 * <p>This class provides basis for AWT Mixing testing. 40 * <p>It provides all standard test machinery and should be used by 41 * extending and overriding next methods: 42 * <li> {@link OverlappingTestBase#prepareControls()} - setup UI components 43 * <li> {@link OverlappingTestBase#performTest()} - run particular test 44 * Those methods would be run in the loop for each AWT component. 45 * <p>Current AWT component should be added to the tested UI by {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) ()}. 46 * There AWT components are prepared to be tested as being overlayed by other (e.g. Swing) components - they are colored to 47 * {@link OverlappingTestBase#AWT_BACKGROUND_COLOR} and throws failure on catching mouse event. 48 * <p> Validation of component being overlayed should be tested by {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) } 49 * See each method javadoc for more details. 50 * 51 * <p>Due to test machinery limitations all test should be run from their own main() by calling next coe 52 * <code> 53 * public static void main(String args[]) throws InterruptedException { 54 * instance = new YourTestInstance(); 55 * OverlappingTestBase.doMain(args); 56 * } 57 * </code> 58 * 59 * @author Sergey Grinev 60 */ 61 public abstract class OverlappingTestBase { 62 // working variables 63 private static volatile boolean wasHWClicked = false; 64 private static volatile boolean passed = true; 65 // constants 66 /** 67 * Default color for AWT component used for validate correct drawing of overlapping. <b>Never</b> use it for lightweight components. 68 */ 69 protected static final Color AWT_BACKGROUND_COLOR = new Color(21, 244, 54); 70 protected static Color AWT_VERIFY_COLOR = AWT_BACKGROUND_COLOR; 71 protected static final int ROBOT_DELAY = 500; 72 private static final String[] simpleAwtControls = {"Button", "Checkbox", "Label", "TextArea"}; 73 /** 74 * Generic strings array. To be used for population of List based controls. 75 */ 76 protected static final String[] petStrings = {"Bird", "Cat", "Dog", "Rabbit", "Rhynocephalia Granda", "Bear", "Tiger", "Mustang"}; 77 // "properties" 78 /** 79 * Tests customization. Set this variable to test only control from java.awt 80 * <p>Usage of this variable should be marked with CR being the reason. 81 * <p>Do not use this variable simultaneously with {@link OverlappingTestBase#skipClassNames} 82 */ 83 protected String onlyClassName = null; 84 /** 85 * For customizing tests. List classes' simple names to skip them from testings. 86 * <p>Usage of this variable should be marked with CR being the reason. 87 */ 88 protected String[] skipClassNames = null; 89 /** 90 * Set to false to avoid event delivery validation 91 * @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean) 92 */ 93 protected boolean useClickValidation = true; 94 /** 95 * Set to false if test doesn't supposed to verify EmbeddedFrame 96 */ 97 protected boolean testEmbeddedFrame = false; 98 /** 99 * Set this variable to true if testing embedded frame is impossible (on Mac, for instance, for now). 100 * The testEmbeddedFrame is explicitly set to true in dozen places. 101 */ 102 protected boolean skipTestingEmbeddedFrame = false; 103 104 public static final boolean isMac = System.getProperty("os.name").toLowerCase().contains("os x"); 105 private boolean isFrameBorderCalculated; 106 private int borderShift; 107 108 { if (Toolkit.getDefaultToolkit().getClass().getName().matches(".*L.*Toolkit")) { 109 // No EmbeddedFrame in LWToolkit/LWCToolkit, yet 110 // And it should be programmed some other way, too, in any case 111 //System.err.println("skipTestingEmbeddedFrame"); 112 //skipTestingEmbeddedFrame = true; 113 }else { 114 System.err.println("do not skipTestingEmbeddedFrame"); 115 } 116 } 117 118 protected int frameBorderCounter() { 119 if (!isFrameBorderCalculated) { 120 try { 121 new FrameBorderCounter(); // force compilation by jtreg 122 String JAVA_HOME = System.getProperty("java.home"); 123 Process p = Runtime.getRuntime().exec(JAVA_HOME + "/bin/java FrameBorderCounter"); 124 try { 125 p.waitFor(); 126 } catch (InterruptedException e) { 127 e.printStackTrace(); 128 throw new RuntimeException(e); 129 } 130 if (p.exitValue() != 0) { 131 throw new RuntimeException("FrameBorderCounter exited with not null code!\n" + readInputStream(p.getErrorStream())); 132 } 133 borderShift = Integer.parseInt(readInputStream(p.getInputStream()).trim()); 134 isFrameBorderCalculated = true; 135 } catch (IOException e) { 136 e.printStackTrace(); 137 throw new RuntimeException("Problem calculating a native border size"); 138 } 139 } 140 return borderShift; 141 } 142 143 public void getVerifyColor() { 144 try { 145 final int size = 200; 146 final Point[] p = new Point[1]; 147 SwingUtilities.invokeAndWait(new Runnable() { 148 public void run(){ 149 JFrame frame = new JFrame("set back"); 150 frame.getContentPane().setBackground(AWT_BACKGROUND_COLOR); 151 frame.setSize(size, size); 152 frame.setUndecorated(true); 153 frame.setVisible(true); 154 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 155 p[0] = frame.getLocation(); 156 } 157 }); 158 Robot robot = new Robot(); 159 robot.waitForIdle(); 160 Thread.sleep(ROBOT_DELAY); 161 AWT_VERIFY_COLOR = robot.getPixelColor(p[0].x+size/2, p[0].y+size/2); 162 System.out.println("Color will be compared with " + AWT_VERIFY_COLOR + " instead of " + AWT_BACKGROUND_COLOR); 163 } catch (Exception e) { 164 System.err.println("Cannot get verify color: "+e.getMessage()); 165 } 166 } 167 168 private String readInputStream(InputStream is) throws IOException { 169 byte[] buffer = new byte[4096]; 170 int len = 0; 171 StringBuilder sb = new StringBuilder(); 172 try (InputStreamReader isr = new InputStreamReader(is)) { 173 while ((len = is.read(buffer)) > 0) { 174 sb.append(new String(buffer, 0, len)); 175 } 176 } 177 return sb.toString(); 178 } 179 180 private void setupControl(final Component control) { 181 if (useClickValidation) { 182 control.addMouseListener(new MouseAdapter() { 183 @Override 184 public void mouseClicked(MouseEvent e) { 185 System.err.println("ERROR: " + control.getClass() + " received mouse click."); 186 wasHWClicked = true; 187 } 188 }); 189 } 190 control.setBackground(AWT_BACKGROUND_COLOR); 191 control.setForeground(AWT_BACKGROUND_COLOR); 192 control.setPreferredSize(new Dimension(150, 150)); 193 control.setFocusable(false); 194 } 195 196 private void addAwtControl(java.util.List<Component> container, final Component control) { 197 String simpleName = control.getClass().getSimpleName(); 198 if (onlyClassName != null && !simpleName.equals(onlyClassName)) { 199 return; 200 } 201 if (skipClassNames != null) { 202 for (String skipMe : skipClassNames) { 203 if (simpleName.equals(skipMe)) { 204 return; 205 } 206 } 207 } 208 setupControl(control); 209 container.add(control); 210 } 211 212 private void addSimpleAwtControl(java.util.List<Component> container, String className) { 213 try { 214 Class definition = Class.forName("java.awt." + className); 215 Constructor constructor = definition.getConstructor(new Class[]{String.class}); 216 java.awt.Component component = (java.awt.Component) constructor.newInstance(new Object[]{"AWT Component " + className}); 217 addAwtControl(container, component); 218 } catch (Exception ex) { 219 System.err.println(ex.getMessage()); 220 fail("Setup error, this jdk doesn't have awt conrol " + className); 221 } 222 } 223 224 /** 225 * Adds current AWT control to container 226 * <p>N.B.: if testEmbeddedFrame == true this method will also add EmbeddedFrame over Canvas 227 * and it should be called <b>after</b> Frame.setVisible(true) call 228 * @param container container to hold AWT component 229 */ 230 protected final void propagateAWTControls(Container container) { 231 if (currentAwtControl != null) { 232 container.add(currentAwtControl); 233 } else { // embedded frame 234 try { 235 236 //create embedder 237 Canvas embedder = new Canvas(); 238 embedder.setBackground(Color.RED); 239 embedder.setPreferredSize(new Dimension(150, 150)); 240 container.add(embedder); 241 container.setVisible(true); // create peer 242 243 long frameWindow = 0; 244 String getWindowMethodName = null; 245 String eframeClassName = null; 246 if (Toolkit.getDefaultToolkit().getClass().getName().contains("XToolkit")) { 247 java.awt.Helper.addExports("sun.awt.X11", OverlappingTestBase.class.getModule()); 248 getWindowMethodName = "getWindow"; 249 eframeClassName = "sun.awt.X11.XEmbeddedFrame"; 250 }else if (Toolkit.getDefaultToolkit().getClass().getName().contains(".WToolkit")) { 251 java.awt.Helper.addExports("sun.awt.windows", OverlappingTestBase.class.getModule()); 252 getWindowMethodName = "getHWnd"; 253 eframeClassName = "sun.awt.windows.WEmbeddedFrame"; 254 }else if (isMac) { 255 java.awt.Helper.addExports("sun.lwawt", OverlappingTestBase.class.getModule()); 256 java.awt.Helper.addExports("sun.lwawt.macosx", OverlappingTestBase.class.getModule()); 257 eframeClassName = "sun.lwawt.macosx.CViewEmbeddedFrame"; 258 } 259 260 ComponentPeer peer = AWTAccessor.getComponentAccessor() 261 .getPeer(embedder); 262 if (!isMac) { 263 Method getWindowMethod = peer.getClass().getMethod(getWindowMethodName); 264 frameWindow = (Long) getWindowMethod.invoke(peer); 265 } else { 266 Method m_getPlatformWindowMethod = peer.getClass().getMethod("getPlatformWindow"); 267 Object platformWindow = m_getPlatformWindowMethod.invoke(peer); 268 Class classPlatformWindow = Class.forName("sun.lwawt.macosx.CPlatformWindow"); 269 270 Method m_getContentView = classPlatformWindow.getMethod("getContentView"); 271 Object contentView = m_getContentView.invoke(platformWindow); 272 Class classContentView = Class.forName("sun.lwawt.macosx.CPlatformView"); 273 274 Method m_getAWTView = classContentView.getMethod("getAWTView"); 275 frameWindow = (Long) m_getAWTView.invoke(contentView); 276 } 277 278 Class eframeClass = Class.forName(eframeClassName); 279 Constructor eframeCtor = eframeClass.getConstructor(long.class); 280 EmbeddedFrame eframe = (EmbeddedFrame) eframeCtor.newInstance(frameWindow); 281 setupControl(eframe); 282 eframe.setSize(new Dimension(150, 150)); 283 eframe.setVisible(true); 284 // System.err.println(eframe.getSize()); 285 } catch (Exception ex) { 286 ex.printStackTrace(); 287 fail("Failed to instantiate EmbeddedFrame: " + ex.getMessage()); 288 } 289 } 290 } 291 private static final Font hugeFont = new Font("Arial", Font.BOLD, 70); 292 293 private java.util.List<Component> getAWTControls() { 294 java.util.List<Component> components = new ArrayList<Component>(); 295 296 for (String clazz : simpleAwtControls) { 297 addSimpleAwtControl(components, clazz); 298 } 299 300 TextField tf = new TextField(); 301 tf.setFont(hugeFont); 302 addAwtControl(components, tf); 303 304 // more complex controls 305 Choice c = new Choice(); 306 for (int i = 0; i < petStrings.length; i++) { 307 c.add(petStrings[i]); 308 } 309 addAwtControl(components, c); 310 c.setPreferredSize(null); 311 c.setFont(hugeFont); // to make control bigger as setPrefferedSize don't do his job here 312 313 List l = new List(petStrings.length); 314 for (int i = 0; i < petStrings.length; i++) { 315 l.add(petStrings[i]); 316 } 317 addAwtControl(components, l); 318 319 Canvas canvas = new Canvas(); 320 canvas.setSize(100, 200); 321 addAwtControl(components, canvas); 322 323 Scrollbar sb = new Scrollbar(Scrollbar.VERTICAL, 500, 1, 0, 500); 324 addAwtControl(components, sb); 325 326 Scrollbar sb2 = new Scrollbar(Scrollbar.HORIZONTAL, 500, 1, 0, 500); 327 addAwtControl(components, sb2); 328 329 return components; 330 } 331 /** 332 * Default shift for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point) } 333 */ 334 protected static Point shift = new Point(16, 16); 335 336 /** 337 * Verifies point using specified AWT Robot. Supposes <code>defaultShift == true</code> for {@link OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean) }. 338 * This method is used to verify controls by providing just their plain screen coordinates. 339 * @param robot AWT Robot. Usually created by {@link Util#createRobot() } 340 * @param lLoc point to verify 341 * @see OverlappingTestBase#clickAndBlink(java.awt.Robot, java.awt.Point, boolean) 342 */ 343 protected void clickAndBlink(Robot robot, Point lLoc) { 344 clickAndBlink(robot, lLoc, true); 345 } 346 /** 347 * Default failure message for color check 348 * @see OverlappingTestBase#performTest() 349 */ 350 protected String failMessageColorCheck = "The LW component did not pass pixel color check and is overlapped"; 351 /** 352 * Default failure message event check 353 * @see OverlappingTestBase#performTest() 354 */ 355 protected String failMessage = "The LW component did not received the click."; 356 357 private static boolean isValidForPixelCheck(Component component) { 358 if ((component instanceof java.awt.Scrollbar) || isMac && (component instanceof java.awt.Button)) { 359 return false; 360 } 361 return true; 362 } 363 364 /** 365 * Preliminary validation - should be run <b>before</b> overlapping happens to ensure test is correct. 366 * @param robot AWT Robot. Usually created by {@link Util#createRobot() } 367 * @param lLoc point to validate to be <b>of</b> {@link OverlappingTestBase#AWT_BACKGROUND_COLOR} 368 * @param component tested component, should be pointed out as not all components are valid for pixel check. 369 */ 370 protected void pixelPreCheck(Robot robot, Point lLoc, Component component) { 371 if (isValidForPixelCheck(component)) { 372 int tries = 10; 373 Color c = null; 374 while (tries-- > 0) { 375 c = robot.getPixelColor(lLoc.x, lLoc.y); 376 System.out.println("Precheck. color: "+c+" compare with "+AWT_VERIFY_COLOR); 377 if (c.equals(AWT_VERIFY_COLOR)) { 378 return; 379 } 380 try { 381 Thread.sleep(100); 382 } catch (InterruptedException e) { 383 } 384 } 385 System.err.println(lLoc + ": " + c); 386 fail("Dropdown test setup failure, colored part of AWT component is not located at click area"); 387 } 388 } 389 390 /** 391 * Verifies point using specified AWT Robot. 392 * <p>Firstly, verifies point by color pixel check 393 * <p>Secondly, verifies event delivery by mouse click 394 * @param robot AWT Robot. Usually created by {@link Util#createRobot() } 395 * @param lLoc point to verify 396 * @param defaultShift if true verified position will be shifted by {@link OverlappingTestBase#shift }. 397 */ 398 protected void clickAndBlink(Robot robot, Point lLoc, boolean defaultShift) { 399 Point loc = lLoc.getLocation(); 400 //check color 401 Util.waitForIdle(robot); 402 try{ 403 Thread.sleep(500); 404 }catch(Exception exx){ 405 exx.printStackTrace(); 406 } 407 408 if (defaultShift) { 409 loc.translate(shift.x, shift.y); 410 } 411 if (!(System.getProperty("os.name").toLowerCase().contains("os x"))) { 412 Color c = robot.getPixelColor(loc.x, loc.y); 413 System.out.println("C&B. color: "+c+" compare with "+AWT_VERIFY_COLOR); 414 if (c.equals(AWT_VERIFY_COLOR)) { 415 fail(failMessageColorCheck); 416 passed = false; 417 } 418 419 // perform click 420 Util.waitForIdle(robot); 421 } 422 423 robot.mouseMove(loc.x, loc.y); 424 425 robot.mousePress(InputEvent.BUTTON1_MASK); 426 robot.mouseRelease(InputEvent.BUTTON1_MASK); 427 Util.waitForIdle(robot); 428 } 429 430 /** 431 * This method should be overriden with code which setups UI for testing. 432 * Code in this method <b>will</b> be called only from AWT thread so Swing operations can be called directly. 433 * 434 * @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI 435 */ 436 protected abstract void prepareControls(); 437 438 /** 439 * This method should be overriden with test execution. It will <b>not</b> be called from AWT thread so all Swing operations should be treated accordingly. 440 * @return true if test passed. Otherwise fail with default fail message. 441 * @see {@link OverlappingTestBase#failMessage} default fail message 442 */ 443 protected abstract boolean performTest(); 444 /** 445 * This method can be overriden with cleanup routines. It will be called from AWT thread so all Swing operations should be treated accordingly. 446 */ 447 protected void cleanup() { 448 // intentionally do nothing 449 } 450 /** 451 * Currect tested AWT Control. Usually shouldn't be accessed directly. 452 * 453 * @see {@link OverlappingTestBase#propagateAWTControls(java.awt.Container) } for instructions about adding tested AWT control to UI 454 */ 455 protected Component currentAwtControl; 456 457 private void testComponent(Component component) throws InterruptedException, InvocationTargetException { 458 Robot robot = null; 459 try { 460 robot = new Robot(); 461 }catch(Exception ignorex) { 462 } 463 currentAwtControl = component; 464 System.out.println("Testing " + currentAwtControl.getClass().getSimpleName()); 465 SwingUtilities.invokeAndWait(new Runnable() { 466 public void run() { 467 prepareControls(); 468 } 469 }); 470 if (component != null) { 471 Util.waitTillShown(component); 472 } 473 Util.waitForIdle(robot); 474 try { 475 Thread.sleep(500); // wait for graphic effects on systems like Win7 476 } catch (InterruptedException ex) { 477 } 478 if (!instance.performTest()) { 479 fail(failMessage); 480 passed = false; 481 } 482 SwingUtilities.invokeAndWait(new Runnable() { 483 public void run() { 484 cleanup(); 485 } 486 }); 487 } 488 489 private void testEmbeddedFrame() throws InvocationTargetException, InterruptedException { 490 Robot robot = null; 491 try { 492 robot = new Robot(); 493 }catch(Exception ignorex) { 494 } 495 System.out.println("Testing EmbeddedFrame"); 496 currentAwtControl = null; 497 SwingUtilities.invokeAndWait(new Runnable() { 498 public void run() { 499 prepareControls(); 500 } 501 }); 502 Util.waitForIdle(robot); 503 try { 504 Thread.sleep(500); // wait for graphic effects on systems like Win7 505 } catch (InterruptedException ex) { 506 } 507 if (!instance.performTest()) { 508 fail(failMessage); 509 passed = false; 510 } 511 SwingUtilities.invokeAndWait(new Runnable() { 512 public void run() { 513 cleanup(); 514 } 515 }); 516 } 517 518 private void testAwtControls() throws InterruptedException { 519 try { 520 for (Component component : getAWTControls()) { 521 testComponent(component); 522 } 523 if (testEmbeddedFrame && !skipTestingEmbeddedFrame) { 524 testEmbeddedFrame(); 525 } 526 } catch (InvocationTargetException ex) { 527 ex.printStackTrace(); 528 fail(ex.getMessage()); 529 } 530 } 531 /** 532 * Used by standard test machinery. See usage at {@link OverlappingTestBase } 533 */ 534 protected static OverlappingTestBase instance; 535 536 protected OverlappingTestBase() { 537 getVerifyColor(); 538 } 539 540 /***************************************************** 541 * Standard Test Machinery Section 542 * DO NOT modify anything in this section -- it's a 543 * standard chunk of code which has all of the 544 * synchronisation necessary for the test harness. 545 * By keeping it the same in all tests, it is easier 546 * to read and understand someone else's test, as 547 * well as insuring that all tests behave correctly 548 * with the test harness. 549 * There is a section following this for test- 550 * classes 551 ******************************************************/ 552 private static void init() throws InterruptedException { 553 //System.setProperty("sun.awt.disableMixing", "true"); 554 555 instance.testAwtControls(); 556 557 if (wasHWClicked) { 558 fail("HW component received the click."); 559 passed = false; 560 } 561 if (passed) { 562 pass(); 563 } 564 }//End init() 565 private static boolean theTestPassed = false; 566 private static boolean testGeneratedInterrupt = false; 567 private static String failureMessage = ""; 568 private static Thread mainThread = null; 569 private static int sleepTime = 300000; 570 571 // Not sure about what happens if multiple of this test are 572 // instantiated in the same VM. Being static (and using 573 // static vars), it aint gonna work. Not worrying about 574 // it for now. 575 /** 576 * Starting point for test runs. See usage at {@link OverlappingTestBase } 577 * @param args regular main args, not used. 578 * @throws InterruptedException 579 */ 580 public static void doMain(String args[]) throws InterruptedException { 581 mainThread = Thread.currentThread(); 582 try { 583 init(); 584 } catch (TestPassedException e) { 585 //The test passed, so just return from main and harness will 586 // interepret this return as a pass 587 return; 588 } 589 //At this point, neither test pass nor test fail has been 590 // called -- either would have thrown an exception and ended the 591 // test, so we know we have multiple threads. 592 593 //Test involves other threads, so sleep and wait for them to 594 // called pass() or fail() 595 try { 596 Thread.sleep(sleepTime); 597 //Timed out, so fail the test 598 throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds"); 599 } catch (InterruptedException e) { 600 //The test harness may have interrupted the test. If so, rethrow the exception 601 // so that the harness gets it and deals with it. 602 if (!testGeneratedInterrupt) { 603 throw e; 604 } 605 606 //reset flag in case hit this code more than once for some reason (just safety) 607 testGeneratedInterrupt = false; 608 609 if (theTestPassed == false) { 610 throw new RuntimeException(failureMessage); 611 } 612 } 613 614 }//main 615 616 /** 617 * Test will fail if not passed after this timeout. Default timeout is 300 seconds. 618 * @param seconds timeout in seconds 619 */ 620 public static synchronized void setTimeoutTo(int seconds) { 621 sleepTime = seconds * 1000; 622 } 623 624 /** 625 * Set test as passed. Usually shoudn't be called directly. 626 */ 627 public static synchronized void pass() { 628 System.out.println("The test passed."); 629 System.out.println("The test is over, hit Ctl-C to stop Java VM"); 630 //first check if this is executing in main thread 631 if (mainThread == Thread.currentThread()) { 632 //Still in the main thread, so set the flag just for kicks, 633 // and throw a test passed exception which will be caught 634 // and end the test. 635 theTestPassed = true; 636 throw new TestPassedException(); 637 } 638 theTestPassed = true; 639 testGeneratedInterrupt = true; 640 mainThread.interrupt(); 641 }//pass() 642 643 /** 644 * Fail test generic message. 645 */ 646 public static synchronized void fail() { 647 //test writer didn't specify why test failed, so give generic 648 fail("it just plain failed! :-)"); 649 } 650 651 /** 652 * Fail test providing specific reason. 653 * @param whyFailed reason 654 */ 655 public static synchronized void fail(String whyFailed) { 656 System.out.println("The test failed: " + whyFailed); 657 System.out.println("The test is over, hit Ctl-C to stop Java VM"); 658 //check if this called from main thread 659 if (mainThread == Thread.currentThread()) { 660 //If main thread, fail now 'cause not sleeping 661 throw new RuntimeException(whyFailed); 662 } 663 theTestPassed = false; 664 testGeneratedInterrupt = true; 665 failureMessage = whyFailed; 666 mainThread.interrupt(); 667 }//fail() 668 }// class LWComboBox 669 class TestPassedException extends RuntimeException { 670 }