--- /dev/null 2015-02-03 15:10:25.283260976 +0300 +++ new/test/java/awt/Window/AlwaysOnTop/AutoTestOnTop.java 2015-03-16 17:07:35.000000000 +0300 @@ -0,0 +1,795 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 4632143 + @summary Unit test for the RFE window/frame/dialog always on top + @author dom@sparc.spb.su: area=awt.toplevel + @run main AutoTestOnTop +*/ + +import java.awt.*; +import java.awt.event.*; +import java.lang.reflect.*; +import javax.swing.*; +import java.util.Vector; + +/** + * @author tav@sparc.spb.su + * @author dom@sparc.spb.su + * Tests that always-on-top windows combine correctly with different kinds of window in different styles and conditions. + * + * !!! WARNING !!! + * The test fails sometimes because the toFront() method doesn't guarantee + * that after its invocation the frame will be placed above all other windows. + */ +public class AutoTestOnTop { + static Window topw; + static Frame parentw = new Frame(); + static Window f; + static Frame parentf = new Frame(); + + static Object uncheckedSrc = new Object(); // used when no need to check event source + static Object eventSrc = uncheckedSrc; + static boolean dispatchedCond; + + static Semaphore STATE_SEMA = new Semaphore(); + static Semaphore VIS_SEMA = new Semaphore(); + static Vector errors = new Vector(); + + static boolean isUnix = false; + + static StringBuffer msgError = new StringBuffer(); + static StringBuffer msgCase = new StringBuffer(); + static StringBuffer msgAction = new StringBuffer(); + static StringBuffer msgFunc = new StringBuffer(); + static StringBuffer msgVisibility = new StringBuffer(); + + static volatile int stageNum; + static volatile int actNum; + static volatile int testResult = 0; + + static volatile boolean doCheckEvents; + static volatile boolean eventsCheckPassed; + static boolean[] eventsCheckInitVals = new boolean[] { // Whether events are checked for abcence or precence + true, true, true, true, true, false, false, false, false + }; + static String[] msgEventsChecks = new String[] { + null, null, null, null, null, + "expected WindowEvent.WINDOW_STATE_CHANGED hasn't been generated", + "expected WindowEvent.WINDOW_STATE_CHANGED hasn't been generated", + "expected WindowEvent.WINDOW_STATE_CHANGED hasn't been generated", + "expected WindowEvent.WINDOW_STATE_CHANGED hasn't been generated", + }; + + static final int stagesCount = 7; + static final int actionsCount = 9; + + static Method[] preActions = new Method[actionsCount]; + static Method[] postActions = new Method[actionsCount]; + static Method[] isActionsAllowed = new Method[actionsCount]; + static Method[] checksActionEvents = new Method[actionsCount]; + + static Robot robot; + + static boolean doStartTest; + static String osName = System.getProperty("os.name"); + + + public static void main(String[] args) { + checkTesting(); + + } + + public static void performTesting() { + isUnix = osName.equals("Linux") || osName.equals("SunOS"); + + Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { + public void eventDispatched(AWTEvent e) { + if (e.getID() == MouseEvent.MOUSE_CLICKED) { + if (eventSrc != null & eventSrc != uncheckedSrc && e.getSource() != eventSrc) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": " + msgError); + testResult = -1; + } + synchronized (eventSrc) { + dispatchedCond = true; + eventSrc.notify(); + } + } + + if (doCheckEvents && (e.getSource() == topw || e.getSource() == f)) { + + //System.err.println("AWTEventListener: catched the event " + e); + + try { + checksActionEvents[actNum].invoke(null, new Object[] {e}); + } catch (InvocationTargetException ite) { + ite.printStackTrace(); + } catch (IllegalAccessException iae) { + iae.printStackTrace(); + } + return; + } + } + }, 0xffffffffffffffffL); + + Method[] allMethods; + + try { + allMethods = AutoTestOnTop.class.getDeclaredMethods(); + } catch (SecurityException se) { + throw new RuntimeException(se); + } + + for (int i = 0; i < allMethods.length; i++) { + String name = allMethods[i].getName(); + if (name.startsWith("preAction")) { + preActions[name.charAt(name.length() - 1) - '0'] = allMethods[i]; + } else if (name.startsWith("postAction")) { + postActions[name.charAt(name.length() - 1) - '0'] = allMethods[i]; + } else if (name.startsWith("isActionAllowed")) { + isActionsAllowed[name.charAt(name.length() - 1) - '0'] = allMethods[i]; + } else if (name.startsWith("checkActionEvents")) { + checksActionEvents[name.charAt(name.length() - 1) - '0'] = allMethods[i]; + } + } + + f = new Frame("Auxiliary Frame"); + f.setBounds(50, 0, 400, 50); + f.setVisible(true); + waitTillShown(f); + + try { + robot = new Robot(); + } catch (AWTException e) { + throw new RuntimeException("Error: unable to create robot", e); + } + + mainTest(); + + if (testResult != 0) { + System.err.println("The following errors were encountered: "); + for (int i = 0; i < errors.size(); i++) { + System.err.println(errors.get(i).toString()); + } + throw new RuntimeException("Test failed."); + } else { + System.err.println("Test PASSED."); + } + } + + public static void mainTest() { +// stageNum = 0; +// for (int i = 0; i < 5; i++) { +// actNum = 2; +// System.err.println("************************* A C T I O N " + actNum + " *************************"); +// doStage(stageNum, actNum); +// // pause(500); +// actNum = 3; +// System.err.println("************************* A C T I O N " + actNum + " *************************"); +// doStage(stageNum, actNum); +// // pause(500); +// } + for (stageNum = 0; stageNum < stagesCount; stageNum++) { + System.err.println("************************* S T A G E " + stageNum + " *************************"); + for (actNum = 0; actNum < actionsCount; actNum++) { + System.err.println("************************* A C T I O N " + actNum + " *************************"); + doStage(stageNum, actNum); + } // for thru actNum + } // fow thru stageNum + + eventSrc = null; + } + + private static void doStage(int stageNum, int actNum) { + try { + + if (!((Boolean)isActionsAllowed[actNum].invoke(null, new Object[0])).booleanValue()) { + System.err.println("Action skipped due to a platform limitations"); + return; + } + + STATE_SEMA.reset(); + createWindow(stageNum); + + //************************* + // Set window always-on-top + //************************* + + preActions[actNum].invoke(null, new Object[0]); + setAlwaysOnTop(topw, true); + waitForIdle(true); + + if (!topw.isAlwaysOnTopSupported()) return; + + postActions[actNum].invoke(null, new Object[0]); + waitForIdle(false); + + STATE_SEMA.reset(); + + testForAlwaysOnTop(); + + //***************************** + // Set window not always-on-top + //***************************** + + preActions[actNum].invoke(null, new Object[0]); + setAlwaysOnTop(topw, false); + waitForIdle(true); + postActions[actNum].invoke(null, new Object[0]); + waitForIdle(false); + STATE_SEMA.reset(); + + testForNotAlwaysOnTop(); + + } catch (InvocationTargetException ite) { + ite.printStackTrace(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + private static void checkTesting() { + if (Toolkit.getDefaultToolkit().isAlwaysOnTopSupported()) { + performTesting(); + } + } + + public static void testForAlwaysOnTop() { + System.err.println("Checking for always-on-top " + topw); + + ensureInitialWinPosition(topw); + + // Check that always-on-top window is topmost. + // - Click on always-on-top window on the windows cross area. + clickOn(topw, f, 10, 30, "setting " + msgVisibility + + " window (1) always-on-top didn't make it topmost"); + + // Check that we can't change z-order of always-on-top window. + // - a) Try to put the other window on the top. + f.toFront(); + clickOn(uncheckedSrc, f, 190, 30, ""); // coz toFront() works not always + pause(300); + + // - b) Click on always-on-top window on the windows cross area. + clickOn(topw, f, 10, 30, "setting " + msgVisibility + + " window (1) always-on-top didn't make it such"); + + // Ask for always-on-top property + if (isAlwaysOnTop(topw) != true) + error("Test failed: stage #" + stageNum + ", action #" + actNum + ": " + msgCase + ": " + msgAction + + ": isAlwaysOnTop() returned 'false' for window (1) set always-on-top at state " + + msgVisibility); + } + + public static void testForNotAlwaysOnTop() { + System.err.println("Checking for non always-on-top of " + topw); + ensureInitialWinPosition(topw); + + if (msgVisibility.equals("visible") && actNum != 2) { + // Check that the window remains topmost. + // - click on the window on the windows cross area. + clickOn(topw, f, 10, 30, "setting " + msgVisibility + + " window (1) not always-on-top didn't keep it topmost"); + } + + // Check that we can change z-order of not always-on-top window. + // - a) try to put the other window on the top. + f.toFront(); + clickOn(uncheckedSrc, f, 190, 30, ""); // coz toFront() works not always + pause(300); + + // - b) click on not always-on-top window on the windows cross area. + clickOn(f, f, 10, 30, "setting " + msgVisibility + + " window (1) not always-on-top didn't make it such"); + + // Ask for always-on-top property + if (isAlwaysOnTop(topw) != false) + error("Test failed: stage #" + stageNum + ", action #" + actNum + ": " + msgCase + ": " + msgAction + + ": isAlwaysOnTop() returned 'true' for window (1) set not always-on-top at state " + + msgVisibility); + } + + + private static void createWindow(int stageNum) { + // Free native resourses + if (topw != null && topw.isVisible()) { + topw.dispose(); + } + + switch (stageNum) { + case 0: + topw = new Frame("Top Frame"); + msgCase.replace(0, msgCase.length(), "Frame (1) over Frame (2)"); + break; + case 1: + topw = new JFrame("Top JFrame"); + msgCase.replace(0, msgCase.length(), "JFrame (1) over Frame (2)"); + break; + case 2: + topw = new Dialog(parentw, "Top Dialog"); + msgCase.replace(0, msgCase.length(), "Dialog (1) over Frame (2)"); + break; + case 3: + topw = new JDialog(parentw, "Top JDialog"); + msgCase.replace(0, msgCase.length(), "JDialog (1) over Frame (2)"); + break; + case 4: + topw = new Frame("Top Frame"); + f.dispose(); + f = new Dialog(parentf, "Auxiliary Dialog"); + f.setBounds(50, 0, 250, 50); + f.setVisible(true); + waitTillShown(f); + msgCase.replace(0, msgCase.length(), "Frame (1) over Dialog (2)"); + break; + case 5: + topw = new Window(parentw); + msgCase.replace(0, msgCase.length(), "Window (1) over Frame (2)"); + break; + case 6: + topw = new JWindow(parentw); + msgCase.replace(0, msgCase.length(), "JWindow (1) over Frame (2)"); + break; + } + topw.addWindowStateListener(new WindowAdapter() { + public void windowStateChanged(WindowEvent e) { + System.err.println("* " + e); + STATE_SEMA.raise(); + } + }); + topw.setSize(200, 50); + } + + /** + * 0: setting always-on-top to invisible window + * 1: setting always-on-top to visible window + * 2: always-on-top on visible non-focusable window + * 3: always-on-top on visible, dragging topw after that + * 4: always-on-top on visible, dragging f after that + * 5: always-on-top on (visible, maximized), make normal after that + * 6: always-on-top on (visible, iconified), make normal after that + * 7: always-on-top on visible, iconify/deiconify after that + * 8: always-on-top on visible, maximize/restore after that + */ + public static void preAction_0() { + topw.setVisible(false); + } + public static void postAction_0() { + if (topw.isShowing()) { + error("Test failed: stage #" + stageNum + ", action #" + actNum + ": " + msgCase + + ": no actions with windows: changing always-on-top property at window (1) state 'invisible' makes window (1) visible"); + } + setWindowVisible("no actions with windows", "invisible"); + } + public static boolean isActionAllowed_0() { + // Window on Linux is always always-on-top! + return !((stageNum == 5 || stageNum == 6) && isUnix) && (stageNum < stagesCount); + } + public static void checkActionEvents_0(AWTEvent e) { + System.err.println(e.toString()); + } + + public static void preAction_1() { + setWindowVisible("no actions with windows", "visible"); + } + public static void postAction_1() {} + public static boolean isActionAllowed_1() { + return !((stageNum == 5 || stageNum == 6) && isUnix) && (stageNum < stagesCount ); + } + public static void checkActionEvents_1(AWTEvent e) { + System.err.println(e.toString()); + if (e instanceof PaintEvent) { + return; + } + eventsCheckPassed = false; + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": unexpected event " + e + " was generated"); + } + + public static void preAction_2() { + setWindowVisible("when window (1) set not focusable", "visible"); + topw.setFocusableWindowState(false); + f.toFront(); + pause(300); + } + public static void postAction_2() {} + public static boolean isActionAllowed_2() { + return !((stageNum == 5 || stageNum == 6) && isUnix) && (stageNum < stagesCount); + } + public static void checkActionEvents_2(AWTEvent e) { + System.err.println(e.toString()); + if ( (e.getID() >= FocusEvent.FOCUS_FIRST && e.getID() <= FocusEvent.FOCUS_LAST) || + (e.getID() == WindowEvent.WINDOW_LOST_FOCUS && e.getID() == WindowEvent.WINDOW_GAINED_FOCUS)) { + eventsCheckPassed = false; + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + + msgAction + ": after call " + msgFunc + + ": unexpected event " + e + " was generated"); + } + } + + public static void preAction_3() { + setWindowVisible("after dragging", "visible"); + } + public static void postAction_3() { + Point p = topw.getLocationOnScreen(); + int x = p.x + 40, y = p.y + 5; + + try { // Take a pause to avoid double click + Thread.sleep(500); // when called one after another. + } catch (InterruptedException ie) { + ie.printStackTrace(); + } catch (IllegalComponentStateException e) { + e.printStackTrace(); + } + + // Drag the window. + robot.mouseMove(x, y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseMove(200, 50); + robot.mouseMove(x, y); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + } + public static boolean isActionAllowed_3() { + return (stageNum < 5); + } + public static void checkActionEvents_3(AWTEvent e) { + System.err.println(e.toString()); + } + + public static void preAction_4() { + setWindowVisible("after dragging window (2)", "visible"); + } + public static void postAction_4() { + Point p = f.getLocationOnScreen(); + int x = p.x + 150, y = p.y + 5; + + try { // Take a pause to avoid double click + Thread.sleep(500); // when called one after another. + } catch (InterruptedException ie) { + ie.printStackTrace(); + } catch (IllegalComponentStateException e) { + e.printStackTrace(); + } + + // Drag the window. + robot.mouseMove(x, y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseMove(200, 50); + robot.mouseMove(x, y); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + + ensureInitialWinPosition(f); + } + public static boolean isActionAllowed_4() { + return !((stageNum == 5 || stageNum == 6) && isUnix); + } + public static void checkActionEvents_4(AWTEvent e) { + System.err.println(e.toString()); + } + + // Metacity has a bug not allowing to set a window to NORMAL state!!! + + public static void preAction_5() { + setWindowVisible("at state 'maximized'", "visible"); + ((Frame)topw).setExtendedState(Frame.MAXIMIZED_BOTH); + waitForStateChange(); + } + public static void postAction_5() { + ((Frame)topw).setExtendedState(Frame.NORMAL); + waitForStateChange(); + } + public static boolean isActionAllowed_5() { + return (stageNum < 2); + } + public static void checkActionEvents_5(AWTEvent e) { + System.err.println("=" + e.toString()); + if (e.getID() == WindowEvent.WINDOW_STATE_CHANGED) { + eventsCheckPassed = true; + } + } + + public static void preAction_6() { + setWindowVisible("at state 'iconified'", "visible"); + System.err.println("Iconifying " + topw); + ((Frame)topw).setExtendedState(Frame.ICONIFIED); + if (!waitForStateChange()) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": state change to ICONIFIED hasn't been generated"); + } + } + public static void postAction_6() { + System.err.println("Restoring " + topw); + ((Frame)topw).setExtendedState(Frame.NORMAL); + if (!waitForStateChange()) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": state change to NORMAL hasn't been generated"); + } + } + public static boolean isActionAllowed_6() { + return (stageNum < 2 ); + } + public static void checkActionEvents_6(AWTEvent e) { + System.err.println("+" + e.toString()); + if (e.getID() == WindowEvent.WINDOW_STATE_CHANGED) { + eventsCheckPassed = true; + } + } + + public static void preAction_7() { + setWindowVisible("before state 'iconified'", "visible"); + } + public static void postAction_7() { + System.err.println("Setting iconified"); + ((Frame)topw).setExtendedState(Frame.ICONIFIED); + if (!waitForStateChange()) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": state change to ICONIFIED hasn't been generated"); + } + System.err.println("Setting normal"); + ((Frame)topw).setExtendedState(Frame.NORMAL); + if (!waitForStateChange()) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": state change to NORMAL hasn't been generated"); + } + } + public static boolean isActionAllowed_7() { + return (stageNum < 2); + } + public static void checkActionEvents_7(AWTEvent e) { + System.err.println(e.toString()); + if (e.getID() == WindowEvent.WINDOW_STATE_CHANGED) { + eventsCheckPassed = true; + } + } + + public static void preAction_8() { + setWindowVisible("before state 'maximized'", "visible"); + } + public static void postAction_8() { + ((Frame)topw).setExtendedState(Frame.MAXIMIZED_BOTH); + if (!waitForStateChange()) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": state change to MAXIMIZED hasn't been generated"); + } + ((Frame)topw).setExtendedState(Frame.NORMAL); + if (!waitForStateChange()) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + msgFunc + + ": state change to NORMAL hasn't been generated"); + } + } + public static boolean isActionAllowed_8() { + return (stageNum < 2); + } + public static void checkActionEvents_8(AWTEvent e) { + System.err.println(e.toString()); + if (e.getID() == WindowEvent.WINDOW_STATE_CHANGED) { + eventsCheckPassed = true; + } + } + + //*************************************************************************** + + private static void setWindowVisible(String mAction, String mVisibility) { + msgAction.replace(0, msgAction.length(), mAction); + msgVisibility.replace(0, msgVisibility.length(), mVisibility); + + topw.setVisible(true); + pause(100); // Needs for Sawfish + topw.setLocation(0, 0); + waitTillShown(topw); + f.toFront(); + pause(300); + } + + private static void clickOn(Object src, Window relwin, int x, int y, String errorStr) { + Point p = relwin.getLocationOnScreen(); + int counter = 10; + while (--counter > 0) { + eventSrc = src; + msgError.replace(0, msgError.length(), errorStr); + + robot.mouseMove(p.x + x, p.y + y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + + synchronized (eventSrc) { + if (!dispatchedCond) { + try { + eventSrc.wait(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (!dispatchedCond) { + //System.err.println("clickOn: MOUSE_CLICKED event losed, trying to generate it again..."); + continue; + } + dispatchedCond = false; + } + break; + } // end while + if (counter <= 0) { + eventSrc = uncheckedSrc; + error("Test: internal error: could't catch MOUSE_CLICKED event. Skip testing this stage"); + } + } + + private static void setAlwaysOnTop(Window w, boolean value) { + System.err.println("Setting always on top on " + w + " to " + value); + robot.mouseMove(0, 100); // Move out of the window + msgFunc.replace(0, msgCase.length(), "setAlwaysOnTop()"); + try { + w.setAlwaysOnTop(value); + } catch (Exception e) { + error("Test failed: stage#" + stageNum + "action #" + actNum + ": " + msgCase + ": " + msgAction + + ": setAlwaysOnTop(" + value + ") called at state " + msgVisibility + + " threw exeption " + e); + } + } + + private static boolean isAlwaysOnTop(Window w) { + robot.mouseMove(0, 100); // Move out of the window + msgFunc.replace(0, msgCase.length(), "isAlwaysOnTop()"); + boolean result = false; + try { + result = w.isAlwaysOnTop(); + } catch (Exception e) { + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + + ": isAlwaysOnTop() called at state " + msgVisibility + + " threw exeption " + e); + } + return result; + } + + private static void waitTillShown(Component c) { + while (true) { + try { + Thread.sleep(100); + c.getLocationOnScreen(); + break; + } catch (InterruptedException e) { + e.printStackTrace(); + break; + } + } + } + + private static void waitForIdle(boolean doCheck) { + try { + robot.waitForIdle(); + EventQueue.invokeAndWait( new Runnable() { + public void run() {} // Dummy implementation + } ); + } catch(InterruptedException ite) { + System.err.println("waitForIdle, non-fatal exception caught:"); + ite.printStackTrace(); + } catch(InvocationTargetException ine) { + System.err.println("waitForIdle, non-fatal exception caught:"); + ine.printStackTrace(); + } + doCheckEvents = doCheck; + + if (doCheck) { + eventsCheckPassed = eventsCheckInitVals[actNum]; // Initialize + } else if (!eventsCheckPassed && + msgEventsChecks[actNum] != null) { + + + // Some expected event hasn't been catched, + // so give it one more chance... + doCheckEvents = true; + pause(500); + doCheckEvents = false; + + if (!eventsCheckPassed) { + testResult = -1; + error("Test failed: stage #" + stageNum + ", action # " + actNum + ": " + msgCase + ": " + msgAction + ": after call " + + msgFunc + ": " + msgEventsChecks[actNum]); + } + } + } + + private static boolean waitForStateChange() { + System.err.println("------- Waiting for state change"); + try { + STATE_SEMA.doWait(3000); + } catch (InterruptedException ie) { + System.err.println("Wait interrupted: " + ie); + } + boolean state = STATE_SEMA.getState(); + STATE_SEMA.reset(); + return state; + } + + private static void ensureInitialWinPosition(Window w) { + int counter = 30; + while (w.getLocationOnScreen().y != 0 && --counter > 0) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + break; + } + } + if (counter <= 0) { + w.setLocation(0, 0); + pause(100); + System.err.println("Test: window set to initial position forcedly"); + } + } + + private static void pause(int mls) { + try { + Thread.sleep(mls); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static void error(String msg) { + errors.add(msg); + System.err.println(msg); + } +} + +class Semaphore { + boolean state = false; + int waiting = 0; + public Semaphore() { + } + public synchronized void doWait() throws InterruptedException { + if (state) { + return; + } + waiting++; + wait(); + waiting--; + } + public synchronized void doWait(int timeout) throws InterruptedException { + if (state) { + return; + } + waiting++; + wait(timeout); + waiting--; + } + public synchronized void raise() { + state = true; + if (waiting > 0) { + notifyAll(); + } + } + + public synchronized void doNotify() { + notifyAll(); + } + public synchronized boolean getState() { + return state; + } + + public synchronized void reset() { + state = false; + } +}