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

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