/interpreter/tags/at2-build270707/test/edu/vub/at/objects/mirrors/MirrorTest.java

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