/interpreter/tags/at2dist041108/test/edu/vub/at/objects/mirrors/MirrorTest.java

http://ambienttalk.googlecode.com/ · Java · 393 lines · 254 code · 35 blank · 104 comment · 1 complexity · c3c8c98d6f14834995350a0066e117c5 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * MirrorTest.java created on Aug 11, 2006 at 11:27:03 PM
  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.mirrors;
  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.XSelectorNotFound;
  33. import edu.vub.at.exceptions.XAmbienttalk;
  34. import edu.vub.at.objects.ATMethod;
  35. import edu.vub.at.objects.ATObject;
  36. import edu.vub.at.objects.ATTable;
  37. import edu.vub.at.objects.coercion.NativeTypeTags;
  38. import edu.vub.at.objects.grammar.ATSymbol;
  39. import edu.vub.at.objects.natives.NATBoolean;
  40. import edu.vub.at.objects.natives.NATNumber;
  41. import edu.vub.at.objects.natives.NATTable;
  42. import edu.vub.at.objects.natives.grammar.AGSymbol;
  43. public class MirrorTest extends AmbientTalkTest {
  44. public static void main(String[] args) {
  45. junit.swingui.TestRunner.run(MirrorTest.class);
  46. }
  47. protected void setUp() throws Exception {
  48. super.setUp();
  49. ctx_.base_lexicalScope().meta_defineField(AGSymbol.jAlloc("Mirror"), NativeTypeTags._MIRROR_);
  50. ctx_.base_lexicalScope().meta_defineField(AGSymbol.jAlloc("Closure"), NativeTypeTags._CLOSURE_);
  51. ctx_.base_lexicalScope().meta_defineField(AGSymbol.jAlloc("context"), ctx_);
  52. evalAndReturn(
  53. "def at := object: { \n" +
  54. " def mirrors := object: { \n" +
  55. " def Factory := object: {" +
  56. " def createMirror(o) { reflect: o }" +
  57. " }" +
  58. " }; \n" +
  59. " def unit := object: { \n" +
  60. " def XUnitFailed := object: { \n" +
  61. " def message := \"Unittest Failed\"; \n" +
  62. " def init(@args) { \n" +
  63. " if: (args.length > 0) then: { \n" +
  64. " message := args[1]; \n" +
  65. " } \n" +
  66. " } \n" +
  67. " }; \n" +
  68. " def fail( message ) { raise: XUnitFailed.new( message ) }; \n" +
  69. " }; \n" +
  70. "}; \n" +
  71. "\n" +
  72. "def symbol( text ) { jlobby.edu.vub.at.objects.natives.grammar.AGSymbol.alloc( text ) }; \n");
  73. }
  74. /**
  75. * Tests to see whether all symbol names defined in 'names' are present in the table of symbols resulting
  76. * from the evaluation of 'toEval'.
  77. */
  78. private void evalAndTestContainsAll(String toEval, String[] names) throws InterpreterException {
  79. ATTable methodNames = evalAndReturn(toEval).asTable();
  80. for (int i = 0; i < names.length; i++) {
  81. assertTrue(methodNames.base_contains(AGSymbol.jAlloc(names[i])).asNativeBoolean().javaValue);
  82. }
  83. }
  84. /**
  85. * This test invokes all meta-level operations defined on objects and tests whether they
  86. * return the proper results. As all these meta-level operations should return mirrors on
  87. * the 'actual' return values, this test also covers the stratification with respect to
  88. * return values. A full test of stratified mirror access is provided below.
  89. */
  90. public void testObjectMetaOperations() throws InterpreterException {
  91. ATObject subject = evalAndReturn(
  92. "def subject := object: { \n" +
  93. " def field := `field; \n" +
  94. " def canonical() { nil }; \n" +
  95. " def keyworded: arg1 message: arg2 { nil }; \n" +
  96. "}; \n");
  97. evalAndCompareTo(
  98. "def mirror := reflect: subject;",
  99. "<mirror on:" + subject.toString() + ">");
  100. evalAndCompareTo(
  101. "mirror.base.super;",
  102. Evaluator.getNil());
  103. /*evalAndCompareTo( => bad unit test: order of field names is undefined
  104. "mirror.listFields();",
  105. "[<field:super>, <field:field>]");*/
  106. evalAndTestContainsAll("mirror.listFields().map: { |field| field.name };",
  107. new String[] { "super", "field" });
  108. evalAndTestContainsAll("mirror.listMethods().map: { |meth| meth.name };",
  109. new String[] { "keyworded:message:", "canonical" });
  110. // methodNames should equal the following table (apart from the ordering of the elements):
  111. // [<method:keyworded:message:>, <method:canonical>]
  112. evalAndCompareTo(
  113. "mirror.eval(context);",
  114. subject);
  115. evalAndCompareTo(
  116. "mirror.quote(context);",
  117. subject);
  118. evalAndCompareTo(
  119. "mirror.print();",
  120. "\"" + subject.toString() + "\"");
  121. evalAndCompareTo(
  122. "mirror.isRelatedTo(nil);",
  123. NATBoolean._TRUE_);
  124. evalAndCompareTo(
  125. "mirror.isCloneOf(object: { nil });",
  126. NATBoolean._FALSE_);
  127. evalAndCompareTo(
  128. "mirror.typeTags;",
  129. NATTable.EMPTY);
  130. evalAndCompareTo(
  131. "mirror.isTaggedAs(Mirror);",
  132. NATBoolean._FALSE_);
  133. }
  134. /**
  135. * In order to build a full reflective tower, it is necessary to be able to create and use
  136. * mirrors on mirrors as well. This test covers the creation and use of default introspective
  137. * mirrors on mirrors
  138. */
  139. public void testReflectingOnIntrospectiveMirrors() {
  140. evalAndCompareTo(
  141. "def meta := reflect: false; \n" +
  142. "def metaMeta := reflect: meta;",
  143. "<mirror on:<mirror on:false>>");
  144. evalAndCompareTo(
  145. "def select := metaMeta.select(meta, `select)",
  146. "<native closure:select>");
  147. // this test fails since a select on a mirror first tries to select with a meta prefix
  148. // if this fails it performs the select using the nil implementation yet this one does not
  149. // take the receiver into account.
  150. evalAndCompareTo(
  151. "def succeeded := select(false, `not)(); \n",
  152. "true");
  153. }
  154. /**
  155. * In order to build a full reflective tower, it is necessary to be able to create and use
  156. * mirrors on mirrors as well. This test covers the creation and use of default introspective
  157. * mirrors on custom intercessive mirrors
  158. */
  159. public void testReflectingOnIntercessiveMirrors() throws InterpreterException {
  160. // When they are never instantiated the mirror is a direct child of the mirror root
  161. // implying that their base object is the default base object, changes to this object
  162. // can corrupt future mirrors. Therefore we explicitly instantiate the mirror which
  163. // clones the ObjectMirrorRoot and gives the mirror its own base object.
  164. ATObject meta = evalAndReturn(
  165. // "def meta := mirror: { nil }; \n");
  166. "def meta := reflect: (object: { nil } mirroredBy: (mirror: { nil })); \n");
  167. assertTrue(meta.meta_isTaggedAs(NativeTypeTags._MIRROR_).asNativeBoolean().javaValue);
  168. evalAndCompareTo(
  169. "def metaMeta := reflect: meta;",
  170. "<mirror on:"+ meta +">");
  171. evalAndCompareTo(
  172. "def defineField := metaMeta.select(meta, `defineField)",
  173. "<native closure:defineField>");
  174. evalAndCompareTo(
  175. "defineField(`boolValue, true); \n" +
  176. "meta.base.boolValue",
  177. "true");
  178. }
  179. /**
  180. * To uphold stratification, values returned from invocations on a mirror should be
  181. * automatically wrapped in a mirror. When returning a value that is itself a mirror, this
  182. * property should be upheld as otherwise it is impossible at the meta-level to distinguish
  183. * whether the value of a field is a base or meta-level entity. This test illustrates this
  184. * possible source for confusion.
  185. */
  186. public void testMirrorWrapping() {
  187. evalAndReturn(
  188. "def subject := object: { \n" +
  189. " def thisBase := nil; \n" +
  190. " def thisMeta := nil; \n" +
  191. "}; \n" +
  192. "def mirror := reflect: subject; \n" +
  193. "subject.thisBase := subject; \n" +
  194. "subject.thisMeta := mirror;");
  195. ATObject base = evalAndReturn(
  196. "mirror.invoke(subject, .thisBase());");
  197. ATObject meta = evalAndReturn(
  198. "mirror.invoke(subject, .thisMeta());");
  199. assertNotSame(base, meta);
  200. assertEquals("<mirror on:"+ base.toString() + ">", meta.toString());
  201. }
  202. /**
  203. * Tests the correctness of the up-down relation in AmbientTalk :
  204. * - down(up(o)) == o
  205. */
  206. public void testMirrorBaseRelation() {
  207. evalAndReturn(
  208. "def emptyObject := object: { def getSuper() { super } }; \n" +
  209. "def objects := [ nil, true, 1, emptyObject, emptyObject.getSuper(), reflect: nil, mirror: { nil } ]; \n" +
  210. "def mirrors[objects.length] { nil }; \n" +
  211. "\n" +
  212. "1.to: objects.length do: { | i | \n" +
  213. " mirrors[i] := reflect: objects[i]; \n" +
  214. "}; \n" +
  215. "1.to: objects.length do: { | i | \n" +
  216. " (objects[i] == mirrors[i].base) \n" +
  217. " .ifFalse: { at.unit.fail(\"down(up(\" + objects[i] + \")) != \" + objects[i]); } \n" +
  218. "} \n");
  219. }
  220. public void testMirrorInvocation() {
  221. evalAndReturn(
  222. "def trueMirror := at.mirrors.Factory.createMirror(true);" +
  223. "def responds := trueMirror.respondsTo( symbol( \"ifTrue:\" ) );" +
  224. "responds.ifFalse: { at.unit.fail(\"Incorrect Mirror Invocation\"); }");
  225. }
  226. public void testMirrorFieldAccess() {
  227. evalAndReturn(
  228. "def basicClosure := { raise: (object: { def [ message, stackTrace ] := [ nil, nil ] }) }; \n" +
  229. "def extendedMirroredClosure := \n" +
  230. " object: { super := basicClosure } \n" +
  231. " taggedAs: [ Closure ] \n" +
  232. " mirroredBy: (mirror: { nil }); \n" +
  233. "def intercessiveMirror := \n" +
  234. " reflect: extendedMirroredClosure");
  235. evalAndTestException(
  236. "intercessiveMirror.base.super.apply([]); \n",
  237. XAmbienttalk.class);
  238. // Can no longer set the mirror of a mirage the final 1-1 mapping is now stricly enforced
  239. // // Cannot assign a base-level entity to a meta-level variable
  240. // evalAndTestException(
  241. // "intercessiveMirror.mirror := \n" +
  242. // " object: { \n" +
  243. // " def invoke(@args) { reflect: \"ok\"}; \n" +
  244. // " };\n" +
  245. // "extendedMirroredClosure.whatever()",
  246. // XTypeMismatch.class);
  247. //
  248. // // Cannot assign a base-level entity to a meta-level variable
  249. // evalAndReturn(
  250. // "intercessiveMirror.mirror := \n" +
  251. // " mirror: { \n" +
  252. // " def invoke(@args) { reflect: \"ok\"}; \n" +
  253. // " };\n" +
  254. // "extendedMirroredClosure.whatever()");
  255. }
  256. /**
  257. * This test tests the listMethods meta operations and ensures the following properties:
  258. * Empty objects contain three primitive methods: namely new, init and ==. These methods
  259. * can be overridden with custom behaviour which shadows the primitive implementation.
  260. * Also external method definitions are closures which properly inserted into the table
  261. * of methods. Whether an object has a intercessive or introspective mirror does not matter
  262. * unless the intercessive mirror intercepts the listMethods operation.
  263. */
  264. public void testListMethods() throws InterpreterException {
  265. evalAndTestContainsAll(
  266. "def test := object: { }; \n" +
  267. "(reflect: test).listMethods().map: { |meth| meth.name }; \n",
  268. new String[] { });
  269. evalAndTestContainsAll(
  270. "def testMirrored := object: { nil } mirroredBy: (mirror: { nil }); \n" +
  271. "(reflect: testMirrored).listMethods().map: { |meth| meth.name }; \n",
  272. new String[] { });
  273. evalAndTestContainsAll(
  274. "test := object: { \n" +
  275. " def init(); \n" +
  276. "}; \n" +
  277. "(reflect: test).listMethods().map: { |meth| meth.name }; \n",
  278. new String[] { "init" });
  279. evalAndTestContainsAll(
  280. "testMirrored := object: { \n" +
  281. " def init(); \n" +
  282. "} mirroredBy: (mirror: { nil }); \n" +
  283. "(reflect: testMirrored).listMethods().map: { |meth| meth.name }; \n",
  284. new String[] { "init" });
  285. evalAndTestContainsAll(
  286. "def test.hello() { \"hello world\" }; \n" +
  287. "(reflect: test).listMethods().map: { |meth| meth.name }; \n",
  288. new String[] { "hello" });
  289. evalAndTestContainsAll(
  290. "def testMirrored.hello() { \"hello world\" }; \n" +
  291. "(reflect: test).listMethods().map: { |meth| meth.name }; \n",
  292. new String[] { "hello" });
  293. }
  294. /**
  295. * Following Bug report 0000001: Test whether the correct selector is returned when invoking
  296. * meta_operations on a custom mirror which themselves may throw XSelectorNotFound exceptions.
  297. * @throws InterpreterException
  298. */
  299. public void testNotFoundReporting() throws InterpreterException {
  300. // The meta operation select will be found, but the field x will not be found.
  301. try {
  302. evalAndThrowException(
  303. "def emptyMirror := mirror: { nil }; \n" +
  304. "def emptyObject := object: { nil }; \n" +
  305. "emptyObject.x;",
  306. XSelectorNotFound.class);
  307. } catch (XSelectorNotFound e) {
  308. assertEquals(e.getSelector(), AGSymbol.jAlloc("x"));
  309. }
  310. // Intentionally misspelled the name of a meta operation to test whether failures to
  311. // find meta operations are still properly reported.
  312. try {
  313. evalAndThrowException(
  314. "def emptyObject.x := 42; \n" +
  315. "emptyMirror.selct(emptyObject, `x);",
  316. XSelectorNotFound.class);
  317. } catch (XSelectorNotFound e) {
  318. assertEquals(e.getSelector(), AGSymbol.jAlloc("selct"));
  319. }
  320. }
  321. public void testMethodSlotRemoval() throws InterpreterException {
  322. ATSymbol m_sym = AGSymbol.jAlloc("m");
  323. ATObject obj = evalAndReturn("def removalObj := object: { def m(); }");
  324. assertTrue(obj.meta_respondsTo(m_sym).asNativeBoolean().javaValue);
  325. assertEquals(3, obj.meta_listSlots().base_length().asNativeNumber().javaValue);
  326. ATMethod slotVal = obj.meta_removeSlot(m_sym).asMethod();
  327. assertEquals(m_sym, slotVal.base_name());
  328. evalAndTestException("removalObj.m()", XSelectorNotFound.class);
  329. assertEquals(2, obj.meta_listSlots().base_length().asNativeNumber().javaValue);
  330. }
  331. public void testFieldSlotRemoval() throws InterpreterException {
  332. ATSymbol x_sym = AGSymbol.jAlloc("x");
  333. ATObject obj = evalAndReturn("def removalObj := object: { def x := 1; }");
  334. assertTrue(obj.meta_respondsTo(x_sym).asNativeBoolean().javaValue);
  335. assertEquals(4, obj.meta_listSlots().base_length().asNativeNumber().javaValue);
  336. assertEquals(NATNumber.ONE, obj.meta_removeSlot(x_sym));
  337. evalAndTestException("removalObj.x", XSelectorNotFound.class);
  338. assertEquals(2, obj.meta_listSlots().base_length().asNativeNumber().javaValue);
  339. }
  340. public void testCustomFieldSlotRemoval() throws InterpreterException {
  341. ATSymbol x_sym = AGSymbol.jAlloc("x");
  342. ATObject obj = evalAndReturn("def removalObj := object: { }");
  343. obj.meta_addField(evalAndReturn("" +
  344. "deftype Field;" +
  345. "object: {" +
  346. " def name := `x;" +
  347. " def readField() { 1 };" +
  348. " def writeField(newx) { };" +
  349. " def accessor() { (&readField).method };" +
  350. " def mutator() { (&writeField).method };" +
  351. "} taggedAs: [Field]").asField());
  352. assertTrue(obj.meta_respondsTo(x_sym).asNativeBoolean().javaValue);
  353. assertEquals(4, obj.meta_listSlots().base_length().asNativeNumber().javaValue);
  354. assertEquals(NATNumber.ONE, obj.meta_removeSlot(x_sym));
  355. evalAndTestException("removalObj.x", XSelectorNotFound.class);
  356. assertEquals(2, obj.meta_listSlots().base_length().asNativeNumber().javaValue);
  357. }
  358. }