/interpreter/tags/at2-build270707/test/edu/vub/at/objects/symbiosis/SymbiosisTest.java
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 { }