1/*
2 * Copyright (c) 2014, 2015, 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
24import java.awt.*;
25import java.awt.event.*;
26import java.awt.peer.ComponentPeer;
27import java.lang.reflect.Constructor;
28import java.lang.reflect.InvocationTargetException;
29import java.lang.reflect.Method;
30import java.util.ArrayList;
31import javax.swing.*;
32
33import sun.awt.AWTAccessor;
34import sun.awt.EmbeddedFrame;
35import java.io.*;
36import 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 */
61public 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        //*** Create instructions for the user here ***
554        //System.setProperty("sun.awt.disableMixing", "true");
555
556        String[] instructions = {
557            "This is an AUTOMATIC test, simply wait until it is done.",
558            "The result (passed or failed) will be shown in the",
559            "message window below."
560        };
561        Sysout.createDialog();
562        Sysout.printInstructions(instructions);
563
564        instance.testAwtControls();
565
566        if (wasHWClicked) {
567            fail("HW component received the click.");
568            passed = false;
569        }
570        if (passed) {
571            pass();
572        }
573    }//End  init()
574    private static boolean theTestPassed = false;
575    private static boolean testGeneratedInterrupt = false;
576    private static String failureMessage = "";
577    private static Thread mainThread = null;
578    private static int sleepTime = 300000;
579
580    // Not sure about what happens if multiple of this test are
581    //  instantiated in the same VM.  Being static (and using
582    //  static vars), it aint gonna work.  Not worrying about
583    //  it for now.
584    /**
585     * Starting point for test runs. See usage at {@link OverlappingTestBase }
586     * @param args regular main args, not used.
587     * @throws InterruptedException
588     */
589    public static void doMain(String args[]) throws InterruptedException {
590        mainThread = Thread.currentThread();
591        try {
592            init();
593        } catch (TestPassedException e) {
594            //The test passed, so just return from main and harness will
595            // interepret this return as a pass
596            return;
597        }
598        //At this point, neither test pass nor test fail has been
599        // called -- either would have thrown an exception and ended the
600        // test, so we know we have multiple threads.
601
602        //Test involves other threads, so sleep and wait for them to
603        // called pass() or fail()
604        try {
605            Thread.sleep(sleepTime);
606            //Timed out, so fail the test
607            throw new RuntimeException("Timed out after " + sleepTime / 1000 + " seconds");
608        } catch (InterruptedException e) {
609            //The test harness may have interrupted the test.  If so, rethrow the exception
610            // so that the harness gets it and deals with it.
611            if (!testGeneratedInterrupt) {
612                throw e;
613            }
614
615            //reset flag in case hit this code more than once for some reason (just safety)
616            testGeneratedInterrupt = false;
617
618            if (theTestPassed == false) {
619                throw new RuntimeException(failureMessage);
620            }
621        }
622
623    }//main
624
625    /**
626     * Test will fail if not passed after this timeout. Default timeout is 300 seconds.
627     * @param seconds timeout in seconds
628     */
629    public static synchronized void setTimeoutTo(int seconds) {
630        sleepTime = seconds * 1000;
631    }
632
633    /**
634     * Set test as passed. Usually shoudn't be called directly.
635     */
636    public static synchronized void pass() {
637        Sysout.println("The test passed.");
638        Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
639        //first check if this is executing in main thread
640        if (mainThread == Thread.currentThread()) {
641            //Still in the main thread, so set the flag just for kicks,
642            // and throw a test passed exception which will be caught
643            // and end the test.
644            theTestPassed = true;
645            throw new TestPassedException();
646        }
647        theTestPassed = true;
648        testGeneratedInterrupt = true;
649        mainThread.interrupt();
650    }//pass()
651
652    /**
653     * Fail test generic message.
654     */
655    public static synchronized void fail() {
656        //test writer didn't specify why test failed, so give generic
657        fail("it just plain failed! :-)");
658    }
659
660    /**
661     * Fail test providing specific reason.
662     * @param whyFailed reason
663     */
664    public static synchronized void fail(String whyFailed) {
665        Sysout.println("The test failed: " + whyFailed);
666        Sysout.println("The test is over, hit  Ctl-C to stop Java VM");
667        //check if this called from main thread
668        if (mainThread == Thread.currentThread()) {
669            //If main thread, fail now 'cause not sleeping
670            throw new RuntimeException(whyFailed);
671        }
672        theTestPassed = false;
673        testGeneratedInterrupt = true;
674        failureMessage = whyFailed;
675        mainThread.interrupt();
676    }//fail()
677}// class LWComboBox
678class TestPassedException extends RuntimeException {
679}
680
681//*********** End Standard Test Machinery Section **********
682//************ Begin classes defined for the test ****************
683// if want to make listeners, here is the recommended place for them, then instantiate
684//  them in init()
685
686/* Example of a class which may be written as part of a test
687class NewClass implements anInterface
688{
689static int newVar = 0;
690
691public void eventDispatched(AWTEvent e)
692{
693//Counting events to see if we get enough
694eventCount++;
695
696if( eventCount == 20 )
697{
698//got enough events, so pass
699
700LWComboBox.pass();
701}
702else if( tries == 20 )
703{
704//tried too many times without getting enough events so fail
705
706LWComboBox.fail();
707}
708
709}// eventDispatched()
710
711}// NewClass class
712
713 */
714//************** End classes defined for the test *******************
715/****************************************************
716Standard Test Machinery
717DO NOT modify anything below -- it's a standard
718chunk of code whose purpose is to make user
719interaction uniform, and thereby make it simpler
720to read and understand someone else's test.
721 ****************************************************/
722/**
723This is part of the standard test machinery.
724It creates a dialog (with the instructions), and is the interface
725for sending text messages to the user.
726To print the instructions, send an array of strings to Sysout.createDialog
727WithInstructions method.  Put one line of instructions per array entry.
728To display a message for the tester to see, simply call Sysout.println
729with the string to be displayed.
730This mimics System.out.println but works within the test harness as well
731as standalone.
732 */
733class Sysout {
734    private static TestDialog dialog;
735
736    public static void createDialogWithInstructions(String[] instructions) {
737        dialog = new TestDialog(new Frame(), "Instructions");
738        dialog.printInstructions(instructions);
739        //dialog.setVisible(true);
740        println("Any messages for the tester will display here.");
741    }
742
743    public static void createDialog() {
744        dialog = new TestDialog(new Frame(), "Instructions");
745        String[] defInstr = {"Instructions will appear here. ", ""};
746        dialog.printInstructions(defInstr);
747        //dialog.setVisible(true);
748        println("Any messages for the tester will display here.");
749    }
750
751    public static void printInstructions(String[] instructions) {
752        dialog.printInstructions(instructions);
753    }
754
755    public static void println(String messageIn) {
756        dialog.displayMessage(messageIn);
757        System.out.println(messageIn);
758    }
759}// Sysout  class
760
761/**
762This is part of the standard test machinery.  It provides a place for the
763test instructions to be displayed, and a place for interactive messages
764to the user to be displayed.
765To have the test instructions displayed, see Sysout.
766To have a message to the user be displayed, see Sysout.
767Do not call anything in this dialog directly.
768 */
769class TestDialog extends Dialog {
770    TextArea instructionsText;
771    TextArea messageText;
772    int maxStringLength = 80;
773
774    //DO NOT call this directly, go through Sysout
775    public TestDialog(Frame frame, String name) {
776        super(frame, name);
777        int scrollBoth = TextArea.SCROLLBARS_BOTH;
778        instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
779        add("North", instructionsText);
780
781        messageText = new TextArea("", 5, maxStringLength, scrollBoth);
782        add("Center", messageText);
783
784        pack();
785
786       //setVisible(true);
787    }// TestDialog()
788
789    //DO NOT call this directly, go through Sysout
790    public void printInstructions(String[] instructions) {
791        //Clear out any current instructions
792        instructionsText.setText("");
793
794        //Go down array of instruction strings
795
796        String printStr, remainingStr;
797        for (int i = 0; i < instructions.length; i++) {
798            //chop up each into pieces maxSringLength long
799            remainingStr = instructions[i];
800            while (remainingStr.length() > 0) {
801                //if longer than max then chop off first max chars to print
802                if (remainingStr.length() >= maxStringLength) {
803                    //Try to chop on a word boundary
804                    int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1);
805
806                    if (posOfSpace <= 0) {
807                        posOfSpace = maxStringLength - 1;
808                    }
809
810                    printStr = remainingStr.substring(0, posOfSpace + 1);
811                    remainingStr = remainingStr.substring(posOfSpace + 1);
812                } //else just print
813                else {
814                    printStr = remainingStr;
815                    remainingStr = "";
816                }
817
818                instructionsText.append(printStr + "\n");
819
820            }// while
821
822        }// for
823
824    }//printInstructions()
825
826    //DO NOT call this directly, go through Sysout
827    public void displayMessage(String messageIn) {
828        messageText.append(messageIn + "\n");
829        System.out.println(messageIn);
830    }
831}// TestDialog  class
832
833