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.drjava.model.repl;
038:
039: import edu.rice.cs.drjava.DrJava;
040: import edu.rice.cs.drjava.DrJavaTestCase;
041: import edu.rice.cs.drjava.config.OptionConstants;
042: import edu.rice.cs.drjava.config.OptionListener;
043: import edu.rice.cs.drjava.config.OptionEvent;
044: import edu.rice.cs.drjava.model.repl.newjvm.ClassPathManager;
045: import edu.rice.cs.util.swing.Utilities;
046:
047: import junit.framework.*;
048:
049: /** Tests the functionality of the repl interpreter.
050: * @version $Id: JavaInterpreterTest.java 4255 2007-08-28 19:17:37Z mgricken $
051: */
052: public final class JavaInterpreterTest extends DrJavaTestCase {
053: private JavaInterpreter _interpreter;
054: static public boolean testValue;
055:
056: /** The setup method run before each test. */
057: protected void setUp() throws Exception {
058: super .setUp();
059: _interpreter = new DynamicJavaAdapter(new ClassPathManager());
060: testValue = false;
061: }
062:
063: /** Asserts that the results of interpreting the first of each
064: * Pair is equal to the second.
065: * @param cases an array of Pairs
066: */
067: private void tester(Pair[] cases) throws ExceptionReturnedException {
068: for (int i = 0; i < cases.length; i++) {
069: Object out = _interpreter.interpret(cases[i].first());
070: assertEquals(cases[i].first() + " interpretation wrong!",
071: cases[i].second(), out);
072: }
073: }
074:
075: /** Make sure interpreting simple constants works.
076: * Note that strings and characters are quoted.
077: */
078: public void testConstants() throws ExceptionReturnedException {
079: Pair[] cases = new Pair[] { Pair.make("5", new Integer(5)),
080: Pair.make("1356", new Integer(1356)),
081: Pair.make("true", Boolean.TRUE),
082: Pair.make("false", Boolean.FALSE),
083: Pair.make("\'c\'", "'" + new Character('c') + "'"),
084: Pair.make("1.345", new Double(1.345)),
085: Pair.make("\"buwahahahaha!\"", "\"buwahahahaha!\""),
086: Pair.make("\"yah\\\"eh\\\"\"", "\"yah\"eh\"\""),
087: Pair.make("'\\''", "'" + new Character('\'') + "'") };
088: tester(cases);
089: }
090:
091: /** Test simple operations with Booleans */
092: public void testBooleanOps() throws ExceptionReturnedException {
093: Pair[] cases = new Pair[] {
094: //and
095: Pair.make("true && false", Boolean.FALSE),
096: Pair.make("true && true", Boolean.TRUE),
097: //or
098: Pair.make("true || true", Boolean.TRUE),
099: Pair.make("false || true", Boolean.TRUE),
100: Pair.make("false || false", Boolean.FALSE),
101: // not
102: Pair.make("!true", Boolean.FALSE),
103: Pair.make("!false", Boolean.TRUE),
104: //equals
105: Pair.make("true == true", Boolean.TRUE),
106: Pair.make("false == true", Boolean.FALSE),
107: Pair.make("false == false", Boolean.TRUE),
108: // xor
109: Pair.make("false ^ false", Boolean
110: .valueOf(false ^ false)),
111: Pair.make("false ^ true ", Boolean
112: .valueOf(false ^ true)) };
113: tester(cases);
114: }
115:
116: /** Tests short circuiting */
117: public void testShortCircuit() throws ExceptionReturnedException {
118: Pair[] cases = new Pair[] {
119: Pair.make("false && (3 == 1/0)", Boolean.FALSE),
120: Pair.make("true || (1/0 != 43)", Boolean.TRUE) };
121: tester(cases);
122: }
123:
124: /**
125: * put your documentation comment here
126: */
127: public void testIntegerOps() throws ExceptionReturnedException {
128: Pair[] cases = new Pair[] {
129: // // plus
130: // Pair.make("5+6", new Integer(5 + 6)),
131: // // minus
132: // Pair.make("6-5", new Integer(6 - 5)),
133: // // times
134: // Pair.make("6*5", new Integer(6*5)),
135: // // divide
136: // Pair.make("6/5", new Integer(6/5)),
137: // // modulo
138: // Pair.make("6%5", new Integer(6%5)),
139: // // bit and
140: // Pair.make("6&5", new Integer(6 & 5)),
141: // // bit or
142: // Pair.make("6 | 5", new Integer(6 | 5)),
143: // // bit xor
144: // Pair.make("6^5", new Integer(6 ^ 5)),
145: // // bit complement
146: // Pair.make("~6", new Integer(~6)),
147: // unary plus
148: // Pair.make("+5", new Integer(+5)),
149: // // unary minus
150: // Pair.make("-5", new Integer(-5)),
151: // left shift
152: Pair.make("400 << 5", new Integer(400 << 5)),
153: // right shift
154: Pair.make("400 >> 5", new Integer(400 >> 5)),
155: // unsigned right shift
156: Pair.make("400 >>> 5", new Integer(400 >>> 5)),
157: // // less than
158: // Pair.make("5 < 4", Boolean.valueOf(5 < 4)),
159: // // less than or equal to
160: // Pair.make("4 <= 4", Boolean.valueOf(4 <= 4)), Pair.make("4 <= 5", Boolean.valueOf(4 <= 5)),
161: // // greater than
162: // Pair.make("5 > 4", Boolean.valueOf(5 > 4)), Pair.make("5 > 5", Boolean.valueOf(5 > 5)),
163: // // greater than or equal to
164: // Pair.make("5 >= 4", Boolean.valueOf(5 >= 4)), Pair.make("5 >= 5", Boolean.valueOf(5 >= 5)),
165: // // equal to
166: // Pair.make("5 == 5", Boolean.valueOf(5 == 5)), Pair.make("5 == 6", Boolean.valueOf(
167: // 5 == 6)),
168: // // not equal to
169: Pair.make("5 != 6", Boolean.valueOf(5 != 6)),
170: Pair.make("5 != 5", Boolean.valueOf(5 != 5)) };
171: tester(cases);
172: }
173:
174: /**
175: * put your documentation comment here
176: */
177: public void testDoubleOps() throws ExceptionReturnedException {
178: Pair[] cases = new Pair[] {
179: // less than
180: Pair.make("5.6 < 6.7", Boolean.valueOf(5.6 < 6.7)),
181: // less than or equal to
182: Pair.make("5.6 <= 5.6", Boolean.valueOf(5.6 <= 5.6)),
183: // greater than
184: Pair.make("5.6 > 4.5", Boolean.valueOf(5.6 > 4.5)),
185: // greater than or equal to
186: Pair.make("5.6 >= 56.4", Boolean.valueOf(5.6 >= 56.4)),
187: // equal to
188: Pair.make("5.4 == 5.4", Boolean.valueOf(5 == 5)),
189: // not equal to
190: Pair.make("5.5 != 5.5", Boolean.valueOf(5 != 5)),
191: // unary plus
192: Pair.make("+5.6", new Double(+5.6)),
193: // unary minus
194: Pair.make("-5.6", new Double(-5.6)),
195: // times
196: Pair.make("5.6 * 4.5", new Double(5.6 * 4.5)),
197: // divide
198: Pair.make("5.6 / 3.4", new Double(5.6 / 3.4)),
199: // modulo
200: Pair.make("5.6 % 3.4", new Double(5.6 % 3.4)),
201: // plus
202: Pair.make("5.6 + 6.7", new Double(5.6 + 6.7)),
203: // minus
204: Pair.make("4.5 - 3.4", new Double(4.5 - 3.4)), };
205: tester(cases);
206: }
207:
208: /**
209: * put your documentation comment here
210: */
211: public void testStringOps() throws ExceptionReturnedException {
212: Pair[] cases = new Pair[] {
213: // concatenation
214: Pair.make("\"yeah\" + \"and\"", "\"yeah" + "and\""),
215: // equals
216: Pair.make("\"yeah\".equals(\"yeah\")", Boolean
217: .valueOf("yeah".equals("yeah"))),
218:
219: };
220: tester(cases);
221: }
222:
223: /**
224: * put your documentation comment here
225: */
226: public void testCharacterOps() throws ExceptionReturnedException {
227: Pair[] cases = new Pair[] {
228: // equals
229: Pair.make("'c' == 'c'", Boolean.valueOf('c' == 'c')) };
230: tester(cases);
231: }
232:
233: /**
234: * Tests that String and character declarations do not return
235: * a result, while the variables themselves return a quoted result.
236: */
237: public void testSemicolon() throws ExceptionReturnedException {
238: Pair[] cases = new Pair[] {
239: Pair.make("'c' == 'c'", Boolean.valueOf('c' == 'c')),
240: Pair.make("'c' == 'c';", JavaInterpreter.NO_RESULT),
241: Pair.make("String s = \"hello\"",
242: JavaInterpreter.NO_RESULT),
243: Pair.make("String x = \"hello\";",
244: JavaInterpreter.NO_RESULT),
245: Pair.make("char c = 'c'", JavaInterpreter.NO_RESULT),
246: Pair.make("Character d = new Character('d')",
247: JavaInterpreter.NO_RESULT),
248: Pair.make("s", "\"hello\""),
249: Pair.make("s;", JavaInterpreter.NO_RESULT),
250: Pair.make("x", "\"hello\""),
251: Pair.make("x;", JavaInterpreter.NO_RESULT),
252: Pair.make("c", "'c'"), Pair.make("d", "'d'") };
253: tester(cases);
254: }
255:
256: /**
257: * Tests that null can be used in instanceof expressions.
258: */
259: public void testNullInstanceOf() throws ExceptionReturnedException {
260: Pair[] cases = new Pair[] {
261: Pair.make("null instanceof Object", Boolean
262: .valueOf(null instanceof Object)),
263: Pair.make("null instanceof String", Boolean
264: .valueOf(null instanceof String)) };
265: tester(cases);
266: }
267:
268: /**
269: * Tests simple variable definitions which broke the initial implementation
270: * of variable redefinition (tested by testVariableRedefinition).
271: */
272: public void testVariableDefinition()
273: throws ExceptionReturnedException {
274: _interpreter.interpret("int a = 5;");
275: _interpreter.interpret("int b = a;");
276:
277: _interpreter.interpret("int c = a++;");
278: }
279:
280: /**
281: * Tests that variables are assigned default values.
282: */
283: public void testVariableDefaultValues()
284: throws ExceptionReturnedException {
285: _interpreter.interpret("byte b");
286: _interpreter.interpret("short s");
287: _interpreter.interpret("int i");
288: _interpreter.interpret("long l");
289: _interpreter.interpret("float f");
290: _interpreter.interpret("double d");
291: _interpreter.interpret("char c");
292: _interpreter.interpret("boolean bool");
293: _interpreter.interpret("String str");
294: Pair[] cases = new Pair[] {
295: Pair.make("b", new Byte((byte) 0)),
296: Pair.make("s", new Short((short) 0)),
297: Pair.make("i", new Integer(0)),
298: Pair.make("l", new Long(0L)),
299: Pair.make("f", new Float(0.0f)),
300: Pair.make("d", new Double(0.0d)),
301: Pair.make("c", "'" + new Character('\u0000') + "'"), // quotes are added around chars
302: Pair.make("bool", Boolean.valueOf(false)),
303: Pair.make("str", null) };
304: tester(cases);
305: }
306:
307: /**
308: * Tests that variable declarations with errors will not allow the interpreter
309: * to not define the variable. This will get rid of annoying "Error:
310: * Redefinition of 'variable'" messages after fixing the error. Note that if
311: * the error occurs during the evaluation of the right hand side then the
312: * variable is defined. This is for two reasons: The compiler would have
313: * accepted this variable declaration so that no more variables could have
314: * been defined with the same name afterwards, and we don't know how to make
315: * sure the evaluation doesn't return errors without actually evaluating which
316: * may have side-effects.
317: */
318: public void testVariableRedefinition()
319: throws ExceptionReturnedException {
320: // test error in NameVisitor
321: try {
322: _interpreter.interpret("String s = abc;");
323: fail("variable definition should have failed");
324: } catch (ExceptionReturnedException e) {
325: // Correct; it should fail
326: }
327: // test error in TypeChecker
328: try {
329: _interpreter.interpret("Vector v = new Vector();");
330: fail("variable definition should have failed");
331: } catch (ExceptionReturnedException e) {
332: // Correct; it should fail
333: }
334: try {
335: _interpreter.interpret("File f;");
336: fail("variable definition should have failed");
337: } catch (ExceptionReturnedException e) {
338: // Correct; it should fail
339: }
340: try {
341: // make sure we can redefine
342: _interpreter.interpret("import java.util.Vector;");
343: _interpreter.interpret("Vector v = new Vector();");
344: _interpreter.interpret("String s = \"abc\";");
345: _interpreter.interpret("import java.io.File;");
346: _interpreter.interpret("File f = new File(\"\");");
347: } catch (ExceptionReturnedException e) {
348: fail("These interpret statements shouldn't cause errors");
349: }
350: // test error in EvaluationVisitor
351:
352: // Integer.getInteger("somebadproperty") should be null
353: try {
354: _interpreter
355: .interpret("String z = new String(Integer.getInteger(\"somebadproperty\").toString());");
356: fail("variable definition should have failed");
357: } catch (ExceptionReturnedException e) {
358: }
359: // The DynamcjavaAdapter should have undone the binding made
360: // for "z" when the first definition fails. Defining it again
361: // should work.
362: _interpreter.interpret("String z = \"z\";");
363:
364: }
365:
366: /** Ensure that the interpreter rejects assignments where the right type is not a subclass of the left type. */
367: public void testIncompatibleAssignment()
368: throws ExceptionReturnedException {
369: try {
370: _interpreter.interpret("Integer i = new Object()");
371: fail("incompatible assignment should have failed");
372: } catch (ExceptionReturnedException e) {
373: // Correct; it should fail
374: }
375: try {
376: _interpreter
377: .interpret("Integer i2 = (Integer)new Object();");
378: fail("incompatible assignment should have failed");
379: } catch (ExceptionReturnedException e) {
380: // Correct; it should fail
381: }
382:
383: // Check that a correct assignment doesn't fail
384: _interpreter.interpret("Object o = new Integer(3)");
385: }
386:
387: /** Test the operation of the TypeCheckerExtension by performing the operations ((false) ? 2/0 : 1) and
388: * ((false) ? 2%0 : 1), which should not throw Exceptions in the Java interpreter.
389: */
390: public void testTypeCheckerExtension() {
391: try {
392: _interpreter.interpret("(false) ? 2/0 : 1 ");
393: } catch (ExceptionReturnedException e) {
394: if (e.getContainedException() instanceof ArithmeticException) {
395: fail("testTypeCheckerExtension failed to prevent short circuit DivideByZeroException");
396: }
397: }
398:
399: try {
400: _interpreter.interpret("(false) ? 2%0 : 1 ");
401: } catch (ExceptionReturnedException e) {
402: if (e.getContainedException() instanceof ArithmeticException) {
403: fail("testTypeCheckerExtension failed to prevent short circuit DivideByZeroException");
404: }
405: }
406: }
407:
408: /** Test the operation of the EvaluationVisitorExtension by performing a computation with no results (interpreter
409: * should return NO_RESULT and not null)
410: */
411: public void testEvaluationVisitorExtensionNO_RESULT() {
412: try {
413: Object out = _interpreter.interpret("true;");
414: assertEquals("testEvaluationVisitorExtension",
415: JavaInterpreter.NO_RESULT, out);
416: } catch (ExceptionReturnedException e) {
417: fail("testEvaluationVisitorExtension Exception returned for none exceptional code!"
418: + e);
419: }
420: }
421:
422: /** Test that a variable can be defined in the interpreter by an external source. */
423: public void testDefineVariableExternally()
424: throws ExceptionReturnedException {
425: _interpreter.defineVariable("foo", "hello");
426: assertEquals("manipulated externally defined variable",
427: "\"ello\"", _interpreter
428: .interpret("foo.substring(1,5)"));
429: _interpreter.defineVariable("x", 3);
430: assertEquals("externally defined variable x", new Integer(3),
431: _interpreter.interpret("x"));
432: assertEquals("incremented externally defined variable x",
433: new Integer(4), _interpreter.interpret("++x"));
434: }
435:
436: /** Test that the value of a variable can be queried externally. */
437: public void testQueryVariableExternally() {
438: _interpreter.defineVariable("x", 7);
439: // Get value of variable externally
440: assertEquals("external query for x", new Integer(7),
441: _interpreter.getVariable("x"));
442:
443: // Undefined variable
444: try {
445: _interpreter.getVariable("undefined");
446: fail("Should have thrown IllegalStateException");
447: } catch (IllegalStateException e) {
448: // good, that's what we want
449: }
450: }
451:
452: /** Test that a constant can be defined in the interpreter by an external source. */
453: public void testDefineConstantExternally() {
454: _interpreter.defineConstant("y", 3);
455: try {
456: _interpreter.interpret("y = 4");
457: fail("should not be able to assign to a constant");
458: } catch (ExceptionReturnedException e) {
459: // correct, it should fail
460: }
461: }
462:
463: /** Test that arrays initializers are accepted. */
464: public void testInitializeArrays()
465: throws ExceptionReturnedException {
466: try {
467: _interpreter.interpret("int i[] = new int[]{1,2,3};");
468: _interpreter
469: .interpret("int j[][] = new int[][]{{1}, {2,3}};");
470: _interpreter
471: .interpret("int k[][][][] = new int[][][][]{{{{1},{2,3}}}};");
472: } catch (IllegalArgumentException iae) {
473: fail("Legal array initializations were not accepted.");
474: }
475: }
476:
477: /** Test that array cloning works. */
478: public void testArrayCloning() throws ExceptionReturnedException {
479: try {
480: _interpreter.interpret("new int[]{0}.clone()");
481: } catch (RuntimeException e) {
482: fail("Array cloning failed.");
483: }
484: }
485:
486: /** Test that the Interactions Pane will or won't allow access to private members
487: * given the value of the ALLOW_PRIVATE_ACCESS configuration option.
488: */
489: public void testAllowPrivateAccess()
490: throws ExceptionReturnedException {
491: // The real option listener is in DefaultGlobalModel, so add one here.
492: DrJava.getConfig().addOptionListener(
493: OptionConstants.ALLOW_PRIVATE_ACCESS,
494: new OptionListener<Boolean>() {
495: public void optionChanged(OptionEvent<Boolean> oce) {
496: _interpreter.setPrivateAccessible(oce.value
497: .booleanValue());
498: }
499: });
500: DrJava.getConfig().setSetting(
501: OptionConstants.ALLOW_PRIVATE_ACCESS,
502: Boolean.valueOf(false));
503: Utilities.clearEventQueue();
504: // System.err.println("\nPrivate Access = " + _interpreter.getPrivateAccessible());
505: try {
506: _interpreter.interpret("class A { private int i = 0; }");
507: _interpreter.interpret("new A().i");
508: System.out.println("Private access erroneously succeeded");
509: fail("Should not have access to the private field i inside class A.");
510: } catch (ExceptionReturnedException ere) {
511: assertTrue(ere.getContainedException() instanceof IllegalAccessException);
512: }
513: DrJava.getConfig().setSetting(
514: OptionConstants.ALLOW_PRIVATE_ACCESS,
515: Boolean.valueOf(true));
516: Utilities.clearEventQueue();
517: assertEquals(
518: "Should be able to access private field i whose value should be 0",
519: new Integer(0), _interpreter.interpret("new A().i"));
520: }
521:
522: /**
523: * Tests that declaring a void method in the Interactions Pane won't cause a bad type
524: * exception. Tests bug #915906 "Methods in Interactions no longer work".
525: */
526: public void testDeclareVoidMethod() {
527: try {
528: _interpreter.interpret("void method() {}");
529: } catch (ExceptionReturnedException ere) {
530: fail("Should be able to declare void methods.");
531: }
532: }
533:
534: /**
535: * Tests that a call to user-defined void method returns NO_RESULT, instead of null.
536: * This test does not pass, it is currently broken.
537: */
538: public void testUserDefinedVoidMethod()
539: throws ExceptionReturnedException {
540: Object result = _interpreter
541: .interpret("public void foo() {}; foo()");
542: assertSame("Should have returned NO_RESULT.",
543: Interpreter.NO_RESULT, result);
544: }
545: }
546:
547: /**
548: * A structure to contain a String and an Object pair.
549: * This class is used to help test the JavaInterpreter.
550: * TODO: Is there a reason this is declared explicitly? Maybe for the
551: * sake of serialization or reflection?
552: */
553: class Pair extends edu.rice.cs.plt.tuple.Pair<String, Object> {
554: /**
555: * Constructs a pair.
556: * @param f the first item in the pair
557: * @param s the second in the pair
558: */
559: public Pair(String f, Object s) {
560: super (f, s);
561: }
562:
563: /**
564: * Makes a new pair.
565: * @param first the first item in the pair
566: * @param second the second in the pair
567: * @return the new Pair
568: */
569: public static Pair make(String first, Object second) {
570: return new Pair(first, second);
571: }
572:
573: }
|