PageRenderTime 29ms CodeModel.GetById 15ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

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