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