PageRenderTime 24ms CodeModel.GetById 8ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

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