PageRenderTime 57ms CodeModel.GetById 11ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2dist041108/test/edu/vub/at/objects/symbiosis/SymbiosisTest.java

http://ambienttalk.googlecode.com/
Java | 831 lines | 481 code | 76 blank | 274 comment | 4 complexity | 32305565cdd92b79f3f33820f35de252 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * SymbiosisTest.java created on 13-nov-2006 at 15:10:58
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.symbiosis;
 29
 30import java.io.Serializable;
 31import java.util.Collection;
 32import java.util.List;
 33import java.util.Set;
 34import java.util.Vector;
 35
 36import edu.vub.at.AmbientTalkTest;
 37import edu.vub.at.eval.Evaluator;
 38import edu.vub.at.exceptions.InterpreterException;
 39import edu.vub.at.exceptions.XArityMismatch;
 40import edu.vub.at.exceptions.XClassNotFound;
 41import edu.vub.at.exceptions.XDuplicateSlot;
 42import edu.vub.at.exceptions.XIllegalOperation;
 43import edu.vub.at.exceptions.XNotInstantiatable;
 44import edu.vub.at.exceptions.XSelectorNotFound;
 45import edu.vub.at.exceptions.XSymbiosisFailure;
 46import edu.vub.at.exceptions.XTypeMismatch;
 47import edu.vub.at.objects.ATClosure;
 48import edu.vub.at.objects.ATField;
 49import edu.vub.at.objects.ATMethod;
 50import edu.vub.at.objects.ATObject;
 51import edu.vub.at.objects.ATTypeTag;
 52import edu.vub.at.objects.natives.NATContext;
 53import edu.vub.at.objects.natives.NATException;
 54import edu.vub.at.objects.natives.NATFraction;
 55import edu.vub.at.objects.natives.NATNumber;
 56import edu.vub.at.objects.natives.NATObject;
 57import edu.vub.at.objects.natives.NATTable;
 58import edu.vub.at.objects.natives.NATText;
 59import edu.vub.at.objects.natives.NativeATObject;
 60import edu.vub.at.objects.natives.grammar.AGAssignmentSymbol;
 61import edu.vub.at.objects.natives.grammar.AGSymbol;
 62
 63/**
 64 * Tests the symbiosis with Java. This is primarily done by wrapping
 65 * the SymbiosisTest class and instances itself and invoking some of
 66 * their non-test prefixed methods.
 67 * 
 68 * @author tvcutsem
 69 */
 70public class SymbiosisTest extends AmbientTalkTest {
 71	
 72	public static void main(String[] args) {
 73		junit.swingui.TestRunner.run(SymbiosisTest.class);
 74		/*Test test= new SymbiosisTest() {
 75		        public void runTest() throws Exception {
 76		        	  testBugfixOverloadedConstructor();
 77		        }
 78		 };
 79		 junit.textui.TestRunner.run(test);*/
 80	}
 81	
 82	// test fixture
 83	private Class jTestClass;
 84	private JavaClass atTestClass;
 85	
 86	private SymbiosisTest jTestObject;
 87	private JavaObject atTestObject;
 88	
 89	private JavaPackage jLobby_;
 90	
 91	// these fields and methods will be reflectively invoked from within AmbientTalk
 92	public int xtest;
 93	public static String ytest = "AmbientTalk";
 94	
 95	public SymbiosisTest(int xval) {
 96		xtest = xval;
 97	}
 98	
 99	public SymbiosisTest() {
100		xtest = 0;
101	}
102
103	public SymbiosisTest(SymbiosisTest t) {
104		xtest = -1;
105	}
106	
107	public SymbiosisTest(AmbientTalkTest t) {
108		xtest = -1;
109	}
110	
111	private static class ExceptionTest extends Exception {
112		private static final long serialVersionUID = 1L;
113	}
114
115	public SymbiosisTest(JavaClass c) throws ExceptionTest {
116		throw new ExceptionTest();
117	}
118	
119	public static final int TEST_OBJECT_INIT = 42;
120	
121	public int gettertest() { return xtest; }
122	public void settertest(int xval) { xtest = xval; }
123	public static String prefix(String msg) { return msg + ytest; }
124	public boolean identitytest(Object obj) { return obj == this; }
125	
126	public String overloadedtest() { return "()"; }
127	public String overloadedtest(int x) { return "(int)"; }
128	public String overloadedtest(Object[] vals) { return "(Object[])"; }
129	public String overloadedtest(double x) { return "(double)"; }
130	public String overloadedtest(SymbiosisTest x) { return "(SymbiosisTest)"; }
131	
132	public Object overloadedvararg(ATObject[] varargs) { return null; }
133	public int overloadedvararg(int x) { return x; }
134	
135	public String overloadedmatch2(Object x) { return "(Object)"; }
136	public String overloadedmatch2(SymbiosisTest x) { return "(SymbiosisTest)"; }
137	
138	public void setUp() {
139		jTestClass = SymbiosisTest.class;
140		atTestClass = JavaClass.wrapperFor(SymbiosisTest.class);
141		
142		jTestObject = new SymbiosisTest(TEST_OBJECT_INIT);
143		atTestObject = JavaObject.wrapperFor(jTestObject);
144		
145		jLobby_ = new JavaPackage("");
146	}
147	
148	/**
149	 * Test the conversion function Symbiosis.ambientTalkToJava for various kinds of input.
150	 */
151	public void testAT2JavaConversion() {
152		try {
153			// -- WRAPPED JAVA OBJECTS --
154			assertEquals(jTestObject, Symbiosis.ambientTalkToJava(atTestObject, SymbiosisTest.class));
155			// -- PRIMITIVE TYPES --
156			assertEquals(new Integer(5), Symbiosis.ambientTalkToJava(NATNumber.atValue(5), int.class));
157			// -- STRINGS --
158			assertEquals(ytest, Symbiosis.ambientTalkToJava(NATText.atValue(ytest), String.class));
159			// -- ARRAYS --
160			assertEquals(0.5, ((double[]) Symbiosis.ambientTalkToJava(NATTable.atValue(new ATObject[] { NATFraction.atValue(0.5) }), double[].class))[0], 0.0);
161			// -- EXCEPTIONS --
162			assertEquals(XIllegalOperation.class, Symbiosis.ambientTalkToJava(new XIllegalOperation("AT2JavaTest").getAmbientTalkRepresentation(), Exception.class).getClass());
163			// -- CLASS OBJECTS --
164			assertEquals(jTestClass, Symbiosis.ambientTalkToJava(atTestClass, Class.class));
165			// -- nil => NULL if converting to Java --
166			assertEquals(null, Symbiosis.ambientTalkToJava(Evaluator.getNil(), Runnable.class));
167			// -- nil => nil if remaining within AT --
168			assertEquals(Evaluator.getNil(), Symbiosis.ambientTalkToJava(Evaluator.getNil(), ATObject.class));
169			// beware with types such as java.lang.Object that match both Java and AT types!
170			// if the ATObject can be assigned to the Java type, the ATObject will be kept
171			assertEquals(Evaluator.getNil(), Symbiosis.ambientTalkToJava(Evaluator.getNil(), Object.class));
172			// -- INTERFACE TYPES AND NAT CLASSES --
173			assertTrue(Symbiosis.ambientTalkToJava(new NATObject(), Runnable.class) instanceof Runnable);
174			try {
175				Symbiosis.ambientTalkToJava(new NATObject(), Symbiosis.class);
176				fail();
177			} catch (XTypeMismatch e) {
178				// expected: coercion does not work for non-interface class types
179			}
180		} catch (InterpreterException e) {
181			fail(e.getMessage());
182		}
183	}
184	
185	/**
186	 * Test the conversion function Symbiosis.javaToAmbientTalk for various kinds of input.
187	 */
188	public void testJava2ATConversion() {
189		try {
190			// -- WRAPPED JAVA OBJECTS --
191			assertEquals(atTestObject, Symbiosis.javaToAmbientTalk(jTestObject));
192			// -- PRIMITIVE TYPES --
193			assertEquals(NATNumber.atValue(5), Symbiosis.javaToAmbientTalk(new Integer(5)));
194			// -- STRINGS --
195			assertEquals(NATText.atValue(ytest), Symbiosis.javaToAmbientTalk(ytest));
196			// -- ARRAYS --
197			assertEquals(NATFraction.atValue(0.5), Symbiosis.javaToAmbientTalk(new double[] { 0.5 }).asNativeTable().elements_[0]);
198			// -- EXCEPTIONS --
199			assertEquals(XIllegalOperation.class, ((NATException)Symbiosis.javaToAmbientTalk(new XIllegalOperation("Java2ATTest"))).getWrappedException().getClass());
200			// -- CLASS OBJECTS --
201			assertEquals(atTestClass, Symbiosis.javaToAmbientTalk(jTestClass));
202			// -- nil => NULL --
203			assertEquals(Evaluator.getNil(), Symbiosis.javaToAmbientTalk(null));
204			// -- INTERFACE TYPES AND NAT CLASSES --
205			ATObject orig = new NATObject();
206			Object proxy = Symbiosis.ambientTalkToJava(orig, Runnable.class);
207			assertEquals(orig, Symbiosis.javaToAmbientTalk(proxy));
208		} catch (InterpreterException e) {
209			fail(e.getMessage());
210		}
211	}
212	
213	/**
214	 * Invokes the two instance methods gettertest and settertest on atTestObject.
215	 * Also performs a selection of the field 'xtest'
216	 * 
217	 * Also invokes the method 'identitytest' to see whether AT->Java conversion does proper unwrapping
218	 */
219	public void testWorkingInstanceInvocation() {
220		try {
221			// def result := atTestObject.gettertest(); assert(42 == result)
222			ATObject result = atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("gettertest"), NATTable.EMPTY);
223			assertEquals(TEST_OBJECT_INIT, result.asNativeNumber().javaValue);
224			
225			// result := atTestObject.settertest(1); assert(result == nil); assert(atTestObject.xtest == 1)
226			result = atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("settertest"), NATTable.atValue(new ATObject[] { NATNumber.ONE }));
227			assertEquals(Evaluator.getNil(), result);
228			assertEquals(1, jTestObject.xtest);
229			// result := atTestObject.xtest
230			result = atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("xtest"), NATTable.EMPTY);
231			assertEquals(NATNumber.ONE, result);
232			
233			// atTestObject.identitytest(atTestObject) == atTestObject
234			assertTrue(atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("identitytest"),
235					                           NATTable.atValue(new ATObject[] { atTestObject })).asNativeBoolean().javaValue);
236		} catch (InterpreterException e) {
237			fail(e.getMessage());
238		}
239	}
240
241	/**
242	 * Invokes the class method 'prefix' and performs a selection and assignment of the static 'ytest' field
243	 */
244	public void testWorkingClassInvocation() {
245		try {
246			// def result := atTestClass.prefix("Hello, "); assert("Hello, " + ytest == result)
247			String txt = "Hello, ";
248			NATText prefix = NATText.atValue(txt);
249			ATObject result = atTestClass.impl_invoke(atTestClass, AGSymbol.jAlloc("prefix"), NATTable.atValue(new ATObject[] { prefix }));
250			assertEquals(txt + ytest, result.asNativeText().javaValue);
251			
252			// result := atTestClass.ytest; assert(result == ytest);
253			result = atTestClass.impl_invoke(atTestClass, AGSymbol.jAlloc("ytest"), NATTable.EMPTY);
254			assertEquals(ytest, result.asNativeText().javaValue);
255			
256			// atTestClass.ytest := "Hello, "; assert(ytest == "Hello, ")
257			result = atTestClass.impl_invoke(atTestClass, AGAssignmentSymbol.jAlloc("ytest:="), NATTable.of(prefix));
258			assertEquals(prefix, result);
259			assertEquals(txt, ytest);
260		} catch (InterpreterException e) {
261			fail(e.getMessage());
262		}		
263	}
264	
265	/**
266	 * Invokes the method 'gettertest' with one argument instead of zero.
267	 */
268	public void testFaultyArity() {
269		try {
270			atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("gettertest"), NATTable.atValue(new ATObject[] { Evaluator.getNil() }));
271			fail("Expected an arity mismatch exception");
272		} catch(XArityMismatch e) {
273			// expected exception: success
274		} catch(InterpreterException e) {
275			fail(e.getMessage());
276		}
277	}
278	
279	/**
280	 * Invokes the method 'settertest' with a double (fraction) instead of an int (number)
281	 */
282	public void testIllegalArgs() {
283		try {
284			atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("settertest"), NATTable.atValue(new ATObject[] { NATFraction.atValue(0.1) }));
285			fail("Expected an illegal argument exception");
286		} catch(XTypeMismatch e) {
287			// Java expects an int, so AT expects a native number, but is given a native fraction
288			if (e.getExpectedType() == NATNumber.class) {
289				// expected exception: success
290			} else {
291				fail(e.getMessage());
292			}
293		} catch(InterpreterException e) {
294			fail(e.getMessage());
295		}
296	}
297	
298	/**
299	 * Tries to assign to a final public field
300	 */
301	public void testIllegalAssignment() {
302		try {
303			atTestClass.impl_invoke(atTestClass,
304					AGAssignmentSymbol.jAlloc("TEST_OBJECT_INIT:="), NATTable.of(NATNumber.atValue(0)));
305			fail("Expected an illegal assignment exception");
306		} catch(XSelectorNotFound e) {
307			// expected exception: success
308		} catch(InterpreterException e) {
309			fail(e.getMessage());
310		}
311	}
312	
313	/**
314	 * Test whether variable arguments work
315	 */
316	public void testVarArgInvocation() {
317		try {
318			ATObject result = atTestObject.impl_invoke(atTestObject,
319													AGSymbol.jAlloc("overloadedvararg"),
320													NATTable.atValue(new ATObject[] { NATNumber.ZERO, NATNumber.ONE }));
321			assertEquals(Evaluator.getNil(), result);
322		} catch (InterpreterException e) {
323			fail(e.getMessage());
324		}
325	}
326	
327	/**
328	 * Tests whether overloaded methods can be properly invoked if they can be resolved
329	 * to one method using the actual arguments.
330	 */
331	public void testOverloadedInvWithOneMatch() {
332		try {
333			// invokes overloadedtest(int)
334			ATObject result = atTestObject.impl_invoke(atTestObject,
335													AGSymbol.jAlloc("overloadedtest"),
336													NATTable.atValue(new ATObject[] { NATNumber.ZERO }));
337			assertEquals("(int)", result.asNativeText().javaValue);
338			// invokes overloadedtest(SymbiosisTest)
339			result = atTestObject.impl_invoke(atTestObject,
340											AGSymbol.jAlloc("overloadedtest"),
341											NATTable.atValue(new ATObject[] { atTestObject }));
342			assertEquals("(SymbiosisTest)", result.asNativeText().javaValue);
343			// invokes overloadedtest()
344			result = atTestObject.impl_invoke(atTestObject,
345											AGSymbol.jAlloc("overloadedtest"),
346											NATTable.EMPTY);
347			assertEquals("()", result.asNativeText().javaValue);
348		} catch (InterpreterException e) {
349			fail(e.getMessage());
350		}
351	}
352	
353	/**
354	 * Invokes an overloaded method where the symbiosis cannot disambiguate automatically.
355	 */
356	public void testOverloadedInvWithMultipleMatches() {
357		try {
358			// invokes overloadedmatch2(Object|SymbiosisTest) => error
359			atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("overloadedmatch2"),
360											     NATTable.atValue(new ATObject[] { atTestObject }));
361			fail("Expected a symbiosis exception");
362		} catch (XSymbiosisFailure e) {
363			// success: expected exception
364		} catch (InterpreterException e) {
365			fail(e.getMessage());
366		}
367	}
368	
369	/**
370	 * Invokes an overloaded method that does not match the specified argument type
371	 */
372	public void testOverloadedInvWithNoMatch() {
373		try {
374			// invokes overloadedtest(NATObject) => error
375			atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("overloadedtest"),
376											     NATTable.atValue(new ATObject[] { new NATObject() }));
377			fail("Expected a symbiosis exception");
378		} catch (XSymbiosisFailure e) {
379			// success: expected exception
380		} catch (InterpreterException e) {
381			fail(e.getMessage());
382		}
383	}
384	
385	/**
386	 * Invokes a method that is not defined in the class.
387	 */
388	public void testNonExistentMethod() {
389		try {
390			// invokes foo(1) => error
391			atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("foo"),
392											     NATTable.atValue(new ATObject[] { NATNumber.ONE }));
393			fail("Expected a selector not found exception");
394		} catch (XSelectorNotFound e) {
395			// success: expected exception
396		} catch (InterpreterException e) {
397			fail(e.getMessage());
398		}
399	}
400	
401	/**
402	 * Tests first-class field access for both instances and classes
403	 */
404	public void testFirstClassFields() {
405		try {
406			// def result := (reflect: atTestObject).grabField("xtest")
407			ATField result = atTestObject.meta_grabField(AGSymbol.jAlloc("xtest")).asField();
408			assertEquals("xtest", result.base_name().toString());
409			assertEquals(TEST_OBJECT_INIT, result.base_readField().asNativeNumber().javaValue);
410			
411			// result := (reflect: atTestClass).grabField("ytest")
412			result = atTestClass.meta_grabField(AGSymbol.jAlloc("ytest")).asField();
413			assertEquals("ytest", result.base_name().toString());
414			assertEquals(ytest, result.base_readField().asNativeText().javaValue);
415		} catch (InterpreterException e) {
416			fail(e.getMessage());
417		}
418	}
419	
420	/**
421	 * Tests first-class method access for both instances and classes
422	 */
423	public void testFirstClassMethods() {
424		try {
425			// def result := (reflect: atTestObject).grabMethod("gettertest")
426			ATMethod result = atTestObject.meta_grabMethod(AGSymbol.jAlloc("gettertest")).asMethod();
427			assertEquals("gettertest", result.base_name().toString());
428			// assert (42 == result())
429			assertEquals(TEST_OBJECT_INIT, result.base_apply(NATTable.EMPTY,
430					new NATContext(atTestObject, atTestObject)).asNativeNumber().javaValue);
431			
432			// clo := atTestObject.gettertest
433			ATClosure clo = atTestObject.meta_select(atTestObject, AGSymbol.jAlloc("gettertest"));
434			// assert (42 == clo())
435			assertEquals(TEST_OBJECT_INIT, clo.base_apply(NATTable.EMPTY).asNativeNumber().javaValue);
436			
437			// result := (reflect: atTestClass).grabMethod("prefix")
438			result = atTestClass.meta_grabMethod(AGSymbol.jAlloc("prefix")).asMethod();
439			assertEquals("prefix", result.base_name().toString());
440			// assert ("AmbientTalk" == result(""))
441			assertEquals(ytest, result.base_apply(NATTable.atValue(new ATObject[] { NATText.atValue("") }),
442					new NATContext(atTestClass, atTestClass)).asNativeText().javaValue);	
443		} catch (InterpreterException e) {
444			fail(e.getMessage());
445		}
446	}
447	
448	/**
449	 * Tests casting to manually resolve overloaded method invocations
450	 * Selecting a method from a Java object results in a JavaClosure instance. Such a Java
451	 * closure understands the message 'cast', which allows the programmer to manually restrict
452	 * the wrapped JavaMethods to specific type signatures. In this case, the two choices
453	 *   overloadedmatch2(Object)
454	 *   overloadedmatch2(SymbiosisTest)
455	 * are manually restricted such that only the second one remains applicable
456	 */
457	public void testCasting() {
458		try {
459			// invokes overloadedmatch2(SymbiosisTest) via explicit casting
460			ATClosure method = atTestObject.meta_select(atTestObject, AGSymbol.jAlloc("overloadedmatch2"));
461			ATClosure castedMethod = method.impl_invoke(method, AGSymbol.jAlloc("cast"), NATTable.atValue(new ATObject[] { atTestClass })).asClosure();
462			castedMethod.base_apply(NATTable.atValue(new ATObject[] { atTestObject }));
463		} catch (InterpreterException e) {
464			fail(e.getMessage());
465		}
466	}
467	
468	/**
469	 * Tests whether the parent pointers of the AT symbionts refer to the proper objects.
470	 */
471	public void testSymbiontParents() {
472		try {
473			// the dynamic parent of atTestObject is atTestClass
474			assertEquals(atTestClass, atTestObject.base_super());
475			// the dynamic parent of atTestClass is nil
476			assertEquals(Evaluator.getNil(), atTestClass.base_super());
477			
478			// the lexical parent of atTestObject is the lexical root
479			assertEquals(Evaluator.getGlobalLexicalScope(), atTestObject.impl_lexicalParent());
480			// the lexical parent of atTestClass is the lexical root
481			assertEquals(Evaluator.getGlobalLexicalScope(), atTestClass.impl_lexicalParent());
482		} catch (InterpreterException e) {
483			fail(e.getMessage());
484		}
485	}
486	
487	/**
488	 * Tests whether new per-instance methods and fields can be added to a wrapped Java object.
489	 */
490	public void testSymbiontInstanceAdditions() {
491		try {
492			// (reflect: atTestObject).defineField("x", 1)
493			atTestObject.meta_defineField(AGSymbol.jAlloc("x"), NATNumber.ONE);
494			// assert(atTestObject.x == 1)
495			assertEquals(NATNumber.ONE, atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("x"), NATTable.EMPTY));
496			
497			// (reflect: atTestObject).addMethod(<method:"foo",[x],{x}>)
498			ATMethod foo = evalAndReturn("def foo(x) { x }; &foo").asClosure().base_method();
499			atTestObject.meta_addMethod(foo);
500			// assert(atTestObject.foo(0) == 0)
501			assertEquals(NATNumber.ZERO, atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("foo"),
502					NATTable.atValue(new ATObject[] { NATNumber.ZERO })));
503		} catch (InterpreterException e) {
504			fail(e.getMessage());
505		}
506	}
507	
508	/**
509	 * Tests whether no duplicate methods or fields can be added to a wrapped Java object.
510	 */
511	public void testSymbiontDuplicates() {
512		try {
513			try {
514				// def atTestObject.xtest := 1
515				atTestObject.meta_defineField(AGSymbol.jAlloc("xtest"), NATNumber.ONE);
516				fail("expected a duplicate slot exception");
517			} catch (XDuplicateSlot e) {
518			    // expected exception: success
519			}
520			try {
521				// def atTestObject.gettertest() { nil }
522				ATMethod getter = evalAndReturn("def gettertest() { nil }; &gettertest").asClosure().base_method();
523				atTestObject.meta_addMethod(getter);
524				fail("expected a duplicate slot exception");
525			} catch (XDuplicateSlot e) {
526			    // expected exception: success
527			}
528		} catch (InterpreterException e) {
529			fail(e.getMessage());
530		}
531	}
532
533	/**
534	 * Tests whether new per-class methods and fields can be added to a wrapped Java class.
535	 * Also tests whether existing instances can make use of these newly added methods
536	 */
537	public void testSymbiontClassAdditions() {
538		try {
539			// (reflect: atTestClass).defineField("z", 1)
540			atTestClass.meta_defineField(AGSymbol.jAlloc("z"), NATNumber.ONE);
541			// assert(atTestClass.z == 1)
542			assertEquals(NATNumber.ONE, atTestClass.impl_invokeAccessor(atTestClass, AGSymbol.jAlloc("z"), NATTable.EMPTY));
543			// assert(aTestObject.z == 1) -> delegation to class
544			assertEquals(NATNumber.ONE, atTestObject.impl_invokeAccessor(atTestObject, AGSymbol.jAlloc("z"), NATTable.EMPTY));
545			
546			// (reflect: atTestClass).addMethod(<method:"get",[],{self.xtest}>)
547			ATMethod get = evalAndReturn("def get() { self.xtest }; &get").asClosure().base_method();
548			atTestClass.meta_addMethod(get);
549			// assert(atTestObject.xtest == atTestObject.get())
550			assertEquals(atTestObject.impl_invokeAccessor(atTestObject, AGSymbol.jAlloc("xtest"), NATTable.EMPTY),
551					     atTestObject.impl_invoke(atTestObject, AGSymbol.jAlloc("get"), NATTable.EMPTY));
552		} catch (InterpreterException e) {
553			fail(e.getMessage());
554		}
555	}
556	
557	/**
558	 * Tests cloning behaviour for both wrapped class instances and classes.
559	 */
560	public void testCloning() {
561		try {
562			// cloning a class results in the same class
563			assertEquals(atTestClass, atTestClass.meta_clone());
564			
565			try {
566				// cloning a java object results in an error
567				atTestObject.meta_clone();
568				fail("expected an illegal operation exception");
569			} catch (XIllegalOperation e) {
570				// expected exception: success
571			}
572		} catch (InterpreterException e) {
573			fail(e.getMessage());
574		}
575	}
576	
577	/**
578	 * Tests the invocation of new on a wrapped Java Class.
579	 * Instantiates the Java class via the default init implementation.
580	 */
581	public void testWorkingInstanceCreation() {
582		try {
583			// def instance := atTestClass.new(1)
584			ATObject instance = atTestClass.meta_newInstance(NATTable.atValue(new ATObject[] { NATNumber.ONE }));
585			assertEquals(JavaObject.class, instance.getClass());
586			assertEquals(NATNumber.ONE, instance.impl_invoke(instance, AGSymbol.jAlloc("xtest"), NATTable.EMPTY));
587			
588			Object realInstance = instance.asJavaObjectUnderSymbiosis().getWrappedObject();
589			assertEquals(SymbiosisTest.class, realInstance.getClass());
590			assertEquals(1, ((SymbiosisTest) realInstance).xtest);
591		} catch (InterpreterException e) {
592			fail(e.getMessage());
593		}
594	}
595	
596	/**
597	 * Tests whether classes with private constructors terminate cleanly.
598	 */
599	public void testNonInstantiatableCreation() {
600		try {
601			// def instance := JavaObject.new(1)
602			JavaClass.wrapperFor(JavaObject.class).meta_newInstance(NATTable.atValue(new ATObject[] { NATNumber.ONE }));
603			fail("expected a not instantiatable exception");
604		} catch (XNotInstantiatable e) {
605			// expected exception: success
606		} catch (InterpreterException e) {
607			fail(e.getMessage());
608		}
609	}
610	
611	/**
612	 * Tests whether incorrect arguments passed to constructor terminate cleanly.
613	 */
614	public void testIllegalArgsInstanceCreation() {
615		try {
616			// def instance := atTestClass.new(1.0)
617			atTestClass.meta_newInstance(NATTable.atValue(new ATObject[] { NATFraction.atValue(1.0) }));
618			fail("expected a symbiosis failure with 0 matches");
619		} catch (XSymbiosisFailure e) {
620			// expected exception: success
621		} catch (InterpreterException e) {
622			fail(e.getMessage());
623		}
624	}
625	
626	/**
627	 * Tests whether overloaded constructors which cannot be resolved terminates cleanly.
628	 */
629	public void testOverloadedInstanceCreation() {
630		try {
631			// def instance := atTestClass.new(atTestObject) => 2 matches
632			atTestClass.meta_newInstance(NATTable.atValue(new ATObject[] { atTestObject }));
633			fail("expected a symbiosis failure with 2 matches");
634		} catch (XSymbiosisFailure e) {
635			// expected exception: success
636		} catch (InterpreterException e) {
637			fail(e.getMessage());
638		}
639	}
640	
641	/**
642	 * Tests an instance creation that raises an exception
643	 */
644	public void testExceptionInInstanceCreation() {
645		try {
646			// def instance := atTestClass.new(atTestClass)
647			atTestClass.meta_newInstance(NATTable.atValue(new ATObject[] { atTestClass }));
648			fail("expected the constructor to throw an exception");
649		} catch (XJavaException e) {
650			// expected exception: success if it was an ExceptionTest instance
651			assertEquals(ExceptionTest.class, e.getWrappedJavaException().getClass());
652		} catch (InterpreterException e) {
653			fail(e.getMessage());
654		}
655	}
656	
657	/**
658	 * Tests the invocation of new on a wrapped Java Object, rather than on a Java Class.
659	 */
660	public void testCreationViaJavaObject() {
661		try {
662			// def instance := atTestObject.new(55)
663			ATObject instance = atTestObject.meta_newInstance(
664					NATTable.atValue(new ATObject[] { NATNumber.atValue(55) }));
665			
666			assertEquals(55, instance.impl_invokeAccessor(instance, AGSymbol.jAlloc("xtest"), NATTable.EMPTY).asNativeNumber().javaValue);
667			assertEquals(atTestClass, instance.base_super());
668			assertEquals(jTestObject.xtest, atTestObject.impl_invokeAccessor(atTestObject,
669					AGSymbol.jAlloc("xtest"), NATTable.EMPTY).asNativeNumber().javaValue);
670		} catch (InterpreterException e) {
671			fail(e.getMessage());
672		}
673	}
674	
675	/**
676	 * Tests the invocation of new on a wrapped Java Class.
677	 * Instantiates the Java class via a custom new implementation.
678	 * 
679	 * BEWARE: this test should be the last for testing symbiotic instance creation as it
680	 * MODIFIES the test fixture (the JavaClass wrapper object)! Ths is because the JavaClass wrapper
681	 * is pooled and reused throughout subsequent tests.
682	 */
683	public void testCustomInstanceCreation() {
684		try {
685			// def orignew := atTestClass.new; def atTestClass.customNew(x,y) { def o := orignew(x); def o.ytest := y; o }
686			ATClosure newClo = evalAndReturn("def customNew(x,y) { def o := orignew(x); def o.ytest := y; o }; &customNew").asClosure();
687			atTestClass.meta_defineField(AGSymbol.jAlloc("orignew"), atTestClass.meta_select(atTestClass, AGSymbol.jAlloc("new")));
688			atTestClass.meta_addMethod(newClo.base_method());
689			
690			// def instance := atTestClass.customNew(10, 11)
691			ATObject instance = atTestClass.impl_invoke(atTestClass, AGSymbol.jAlloc("customNew"),
692					NATTable.atValue(new ATObject[] { NATNumber.atValue(10), NATNumber.atValue(11) }));
693			
694			assertEquals(10, instance.impl_invokeAccessor(instance, AGSymbol.jAlloc("xtest"), NATTable.EMPTY).asNativeNumber().javaValue);
695			assertEquals(11, instance.impl_invokeAccessor(instance, AGSymbol.jAlloc("ytest"), NATTable.EMPTY).asNativeNumber().javaValue);
696		} catch (InterpreterException e) {
697			e.printStackTrace();
698			fail(e.getMessage());
699		}
700	}
701	
702	/**
703	 * Tests whether jlobby.java results in a new JavaPackage.
704	 * Tests whether jlobby.java.lang results in a new JavaPackage.
705	 * Tests whether jlobby.java.lang.Object results in the proper loading of that class
706	 */
707	public void testJLobbyPackageLoading() throws InterpreterException {
708		ATObject jpkg = jLobby_.impl_invokeAccessor(jLobby_, AGSymbol.jAlloc("java"), NATTable.EMPTY);
709		assertEquals(JavaPackage.class, jpkg.getClass());
710		assertTrue(jLobby_.meta_respondsTo(AGSymbol.jAlloc("java")).asNativeBoolean().javaValue);
711		ATObject jlpkg = jpkg.impl_invokeAccessor(jpkg, AGSymbol.jAlloc("lang"), NATTable.EMPTY);
712		assertEquals(JavaPackage.class, jlpkg.getClass());
713		assertTrue(jpkg.meta_respondsTo(AGSymbol.jAlloc("lang")).asNativeBoolean().javaValue);
714		ATObject jObject = jlpkg.impl_invokeAccessor(jlpkg, AGSymbol.jAlloc("Object"), NATTable.EMPTY);
715		assertEquals(JavaClass.class, jObject.getClass());
716		assertTrue(jlpkg.meta_respondsTo(AGSymbol.jAlloc("Object")).asNativeBoolean().javaValue);
717	}
718	
719	/**
720	 * Tests whether lowercase classes can be loaded via the class method of a JavaPackage.
721	 */
722	public void testJLobbyExplicitClassLoading() throws InterpreterException {
723		ATObject eduVubAtObjectsSymbiosisPkg = new JavaPackage("edu.vub.at.objects.symbiosis.");
724
725		// load the class manually: invoke pkg.class("lowercaseClassTest")
726		ATObject cls = eduVubAtObjectsSymbiosisPkg.impl_invoke(
727				eduVubAtObjectsSymbiosisPkg,
728				AGSymbol.jAlloc("class"),
729				NATTable.atValue(new ATObject[] { AGSymbol.jAlloc("lowercaseClassTest") }));
730		assertEquals(JavaClass.class, cls.getClass());
731		assertTrue(eduVubAtObjectsSymbiosisPkg.meta_respondsTo(
732				    AGSymbol.jAlloc("lowercaseClassTest")).asNativeBoolean().javaValue);
733	}
734	
735	/**
736	 * Tests whether access to a nonexistent class gives rise to a selector not found exception.
737	 */
738	public void testJLobbyNonexistentClassLoading() throws InterpreterException {
739		try {
740			jLobby_.impl_invokeAccessor(jLobby_, AGSymbol.jAlloc("Foo"), NATTable.EMPTY);
741			fail("expected a class not found exception");
742		} catch (XClassNotFound e) {
743			// success: expected exception
744		}
745	}
746	
747	/**
748	 * Tests whether the uppercase package 'foo.Bar' can be loaded via the package method of a JavaPackage.
749	 */
750	public void testJLobbyExplicitPackageLoading() throws InterpreterException {
751		// def fooPkg := jLobby.foo;
752		ATObject fooPkg = jLobby_.impl_invokeAccessor(jLobby_, AGSymbol.jAlloc("foo"), NATTable.EMPTY);
753		// def BarPkg := foo.package(`Bar);
754		ATObject BarPkg = fooPkg.impl_invoke(fooPkg,
755										   AGSymbol.jAlloc("package"),
756										   NATTable.atValue(new ATObject[] { AGSymbol.jAlloc("Bar") }));
757		assertEquals(JavaPackage.class, BarPkg.getClass());
758		assertTrue(fooPkg.meta_respondsTo(AGSymbol.jAlloc("Bar")).asNativeBoolean().javaValue);
759	}
760	
761	/**
762	 * BUGFIX TEST: jlobby.java.lang.StringBuffer.new(10) failed to discriminate between constructors
763	 * StringBuffer(String) and StringBuffer(int), reason was that anything native was convertible to
764	 * NATText and also to String. Fixed by reimplementing asNativeText in {@link NativeATObject}
765	 * to throw a type exception as usual.
766	 * 
767	 * This constructor is yet again overloaded starting from Java 1.5 because StringBuffer
768	 * now also defines the constructor StringBuffer(CharSequence). Because CharSequence is
769	 * an interface it can be satisfied by any AmbientTalk object. Hence,
770	 * the method must be explicitly cast.
771	 * 
772	 * Currently, the test is commented out because we cannot cast constructors of Java methods
773	 * (selecting new returns a native closure, not a javaclosure which we can cast)
774	 */
775	public void testBugfixOverloadedConstructor() throws InterpreterException {
776		// def jStringBuffer := jLobby.java.lang.StringBuffer;
777		ATObject jStringBuffer = JavaClass.wrapperFor(StringBuffer.class);
778		// jStringBuffer.new(10);
779		jStringBuffer.impl_invoke(jStringBuffer, AGSymbol.jAlloc("new"),
780				                  NATTable.atValue(new ATObject[] { NATNumber.atValue(10) }));
781	}
782	
783	/**
784	 * Tests whether Java interface types are correctly treated as AT/2 types.
785	 * Test cases: interface java.util.Set extends java.util.Collection
786	 */
787	public void testInterfacesAndTypes() throws InterpreterException {
788		JavaClass jSet = JavaClass.wrapperFor(Set.class);
789		JavaClass jCollection = JavaClass.wrapperFor(Collection.class);
790		ATTypeTag atSet = jSet.asTypeTag();
791		ATTypeTag atCollection = jCollection.asTypeTag();
792		// type name = 'java.util.Set'
793		assertEquals(AGSymbol.jAlloc("java.util.Set"), atSet.base_typeName());
794		// type parents = [ java.util.Collection ]
795		assertEquals(jCollection, atSet.base_superTypes().base_at(NATNumber.ONE));
796		// Set isSubtypeOf Collection? true
797		assertTrue(atSet.base_isSubtypeOf(atCollection).asNativeBoolean().javaValue);
798		// Collection isSubtypeOf Set? false
799		assertFalse(atCollection.base_isSubtypeOf(atSet).asNativeBoolean().javaValue);
800		// Set isSubtypeOf Set? true
801		assertTrue(atSet.base_isSubtypeOf(atSet).asNativeBoolean().javaValue);
802	}
803	
804	/**
805	 * Test whether JavaObject wrappers are correctly typed with all
806	 * of the interfaces of the wrapped instance's class.
807	 * 
808	 * Test case: java.util.Vector implements List, RandomAccess, Cloneable, Serializable
809	 */
810	public void testTypedJavaObject() throws InterpreterException {
811		JavaClass jVector = JavaClass.wrapperFor(Vector.class);
812		JavaObject vec = jVector.meta_newInstance(NATTable.EMPTY).asJavaObjectUnderSymbiosis();
813		
814		ATTypeTag jListType = JavaClass.wrapperFor(List.class).asTypeTag();
815		ATTypeTag jCollectionType = JavaClass.wrapperFor(Collection.class).asTypeTag();
816		ATTypeTag jSerializableType = JavaClass.wrapperFor(Serializable.class).asTypeTag();
817		ATTypeTag jSetType = JavaClass.wrapperFor(Set.class).asTypeTag();
818		
819		// vec is tagged with List? true
820		assertTrue(vec.meta_isTaggedAs(jListType).asNativeBoolean().javaValue);
821		// vec is tagged with Collection? true
822		assertTrue(vec.meta_isTaggedAs(jCollectionType).asNativeBoolean().javaValue);
823		// vec is tagged with Serializable? true
824		assertTrue(vec.meta_isTaggedAs(jSerializableType).asNativeBoolean().javaValue);
825		// vec is tagged with Set? false
826		assertFalse(vec.meta_isTaggedAs(jSetType).asNativeBoolean().javaValue);
827	}
828	
829}
830
831class lowercaseClassTest { }