001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.util.newjvm;
038:
039: import edu.rice.cs.drjava.DrJavaTestCase;
040: import edu.rice.cs.drjava.config.FileOption;
041:
042: import edu.rice.cs.util.Log;
043: import edu.rice.cs.util.UnexpectedException;
044: import edu.rice.cs.util.swing.Utilities;
045:
046: import java.rmi.RemoteException;
047:
048: /** Test cases for the master/slave jvm control framework.
049: * @version $Id: IntegratedMasterSlaveTest.java 4255 2007-08-28 19:17:37Z mgricken $
050: */
051: public class IntegratedMasterSlaveTest extends DrJavaTestCase {
052:
053: private static Log _log = new Log("MasterSlave.txt", false);
054:
055: volatile TestMasterJVM _testMaster = _newTestMasterJVM(); // JUnit ensures separate copy for each test
056:
057: private TestMasterJVM _newTestMasterJVM() {
058: try {
059: return new TestMasterJVM();
060: } catch (RemoteException e) {
061: throw new UnexpectedException(e);
062: }
063: }
064:
065: public void tearDown() throws Exception {
066: _testMaster.dispose();
067: super .tearDown();
068: }
069:
070: public void testItAll() throws Exception {
071: // run a couple of times. each one forks its own jvm so not
072: // too many! we run multiple times to prove that the master
073: // can invoke multiple slaves (only one active at a time though)
074: for (int i = 0; i < 2; i++)
075: _testMaster.runTestSequence();
076: _log.log("testItAll completed");
077: }
078:
079: public void testImmediateQuit() throws Exception {
080: for (int i = 0; i < 5; i++)
081: _testMaster.runImmediateQuitTest();
082: _log.log("testImmediateQuit completed");
083: }
084:
085: private class TestMasterJVM extends AbstractMasterJVM implements
086: TestMasterRemote {
087:
088: /** Field and lock used to signal slave quit events. */
089: private volatile boolean _justQuit; // true after slave quits
090: private final Object _quitLock = new Object();
091:
092: /** Field and lock used to signal slave connected events. */
093: private volatile boolean _connected; // true when slave is connected
094: private final Object _connectedLock = new Object();
095:
096: /** Field and lock used to signal letter change events. */
097: private volatile char _letter;
098: private final Object _letterLock = new Object();
099:
100: private volatile String _currentTest = "";
101:
102: public TestMasterJVM() throws RemoteException {
103: super (CounterSlave.class.getName());
104: }
105:
106: /** In util-20020414-0647, if quitSlave were called between the time the slave was invoked and the time it
107: * registered, an IllegalStateException was thrown. The correct behavior, which we test for here, is for the
108: * slave to quit as soon as it is started up.
109: */
110: public void runImmediateQuitTest() throws Exception {
111:
112: // Utilities.show("ImmediateQuitTest started");
113:
114: _currentTest = "runImmediateQuitTest";
115: _justQuit = false;
116: _connected = false;
117: _letter = 'a'; // this needs to be reset because the slave is going to check it!
118:
119: invokeSlave(new String[0], FileOption.NULL_FILE);
120:
121: // Utilities.show("slave invoked");
122:
123: // Immediately call quit, which should not throw an exception. It should return without waiting.
124: quitSlave();
125:
126: // Utilities.show("slave quit");
127:
128: // now we just wait for the quit to process
129: synchronized (_quitLock) {
130: while (!_justQuit)
131: _quitLock.wait();
132: }
133:
134: _currentTest = ""; // If we get here, it worked as expected.
135:
136: // Utilities.show("ImmediateQuitTest finished");
137: _log.log("Ran immediateQuitTest");
138:
139: // (All of the post-quit invariants are checked in handleSlaveQuit.
140: }
141:
142: public void runTestSequence() throws Exception {
143:
144: _currentTest = "runTestSequence";
145: _justQuit = false;
146: _connected = false;
147: _letter = 'a';
148:
149: invokeSlave(new String[0], FileOption.NULL_FILE);
150:
151: synchronized (_connectedLock) {
152: while (!_connected)
153: _connectedLock.wait();
154: }
155:
156: ((TestSlaveRemote) getSlave()).startLetterTest();
157:
158: _log.log("letter test started");
159:
160: // now, wait until five getletter calls passed; after fifth call letter is 'f' due to the ++
161: synchronized (_letterLock) {
162: while (_letter != 'f') {
163: _letterLock.wait();
164: }
165: }
166:
167: _log.log("letter test finished");
168:
169: for (int i = 0; i < 7; i++) {
170: int value = ((TestSlaveRemote) getSlave()).getNumber();
171: assertEquals("value returned by slave", i, value);
172: }
173:
174: _log.log("number test finished");
175:
176: quitSlave();
177: synchronized (_quitLock) {
178: while (!_justQuit)
179: _quitLock.wait();
180: } // for quit to finish
181: _currentTest = "";
182: _log.log("Ran runTestSequence");
183: }
184:
185: public char getLetter() {
186: synchronized (_letterLock) {
187: char ret = _letter;
188: _letter++;
189: _letterLock.notify();
190: return ret;
191: }
192: }
193:
194: protected void handleSlaveConnected() {
195: TestSlaveRemote slave = (TestSlaveRemote) getSlave();
196: assertTrue("slave is set", slave != null);
197: assertTrue("startUp not in progress",
198: !isStartupInProgress());
199: // getLetter should have never been called.
200: assertEquals("letter value", 'a', _letter);
201: synchronized (_connectedLock) {
202: _connected = true;
203: _connectedLock.notify();
204: }
205: _log.log("_handleSlaveConnected() finished");
206: }
207:
208: protected void handleSlaveQuit(int status) {
209: assertEquals("slave result code", 0, status);
210: if (_currentTest.equals("runTestSequence")) {
211: // 5 letter calls must have occurred, so 'f' should be next
212: assertEquals("last letter returned", 'f', _letter);
213: }
214: assertTrue("slave is not set", getSlave() == null);
215: assertTrue("startUp not in progress",
216: !isStartupInProgress());
217:
218: // alert test method that quit occurred.
219: synchronized (_quitLock) {
220: _justQuit = true;
221: _quitLock.notify();
222: }
223: }
224:
225: /** Called if the slave JVM dies before it is able to register.
226: * @param cause The Throwable which caused the slave to die.
227: */
228: public void errorStartingSlave(Throwable cause)
229: throws RemoteException {
230: fail("There was an error starting the slave JVM: " + cause);
231: }
232: }
233:
234: /** The slave will exit with error codes in the case of problems, since there is no other thing it can do!
235: * <DL>
236: * <DT>1</DT><DD>MasterRemote class cast exception.</DD>
237: * <DT>2</DT><DD>Incorect value from getLetter</DD>
238: * <DT>3</DT><DD>RemoteException caught</DD>
239: * <DT>4</DT><DD>Timeout waiting for master JVM to call</DD>
240: * <DT>5</DT><DD>Interrupted while waiting for master JVM to call</DD>
241: * </DL>
242: */
243: public static class CounterSlave extends AbstractSlaveJVM implements
244: TestSlaveRemote {
245:
246: public static final CounterSlave ONLY = newCounterSlave();
247:
248: private volatile int _counter = 0;
249: private volatile TestMasterRemote _master = null;
250:
251: private CounterSlave() throws RemoteException {
252: }
253:
254: private static CounterSlave newCounterSlave() {
255: try {
256: return new CounterSlave();
257: } catch (RemoteException e) {
258: throw new UnexpectedException(e);
259: }
260: }
261:
262: public synchronized int getNumber() {
263: return _counter++;
264: }
265:
266: protected void handleStart(MasterRemote m) {
267: _master = (TestMasterRemote) m;
268: }
269:
270: public void startLetterTest() throws RemoteException {
271: // Run this part of the test in a new thread, so this call will immediately return
272: Thread thread = new Thread() {
273: public void run() {
274: try {
275: for (char c = 'a'; c <= 'e'; c++) {
276: char got = _master.getLetter();
277: if (c != got)
278: System.exit(2);
279: }
280:
281: // OK, now wait up till 15 seconds for master jvm to call
282: Thread.sleep(15000);
283: System.exit(4);
284: } catch (InterruptedException e) {
285: System.exit(5);
286: } catch (RemoteException re) {
287: javax.swing.JOptionPane.showMessageDialog(null,
288: re.toString());
289: System.exit(3);
290: } catch (ClassCastException cce) {
291: System.exit(1);
292: }
293: }
294: };
295: thread.start();
296: }
297: }
298:
299: public interface TestSlaveRemote extends SlaveRemote {
300: public int getNumber() throws RemoteException;
301:
302: public void startLetterTest() throws RemoteException;
303: }
304:
305: public interface TestMasterRemote
306: /*<SlaveType extends SlaveRemote>*/extends MasterRemote/*<SlaveType>*/{
307: public char getLetter() throws RemoteException;
308: }
309: }
|