PageRenderTime 90ms CodeModel.GetById 8ms app.highlight 74ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2-build270707/test/edu/vub/at/objects/symbiosis/SymbiosisTest.java

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