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

http://ambienttalk.googlecode.com/ · Java · 831 lines · 481 code · 76 blank · 274 comment · 4 complexity · 32305565cdd92b79f3f33820f35de252 MD5 · raw file

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