PageRenderTime 78ms CodeModel.GetById 42ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2-build190607/test/edu/vub/at/objects/mirrors/MirageTest.java

http://ambienttalk.googlecode.com/
Java | 292 lines | 172 code | 44 blank | 76 comment | 0 complexity | e9bbfea769d0d87f7ab0f893eb427e7b MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * MirageTest.java created on Aug 11, 2006 at 10:54:29 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 */
 28package edu.vub.at.objects.mirrors;
 29
 30import edu.vub.at.AmbientTalkTest;
 31import edu.vub.at.exceptions.InterpreterException;
 32import edu.vub.at.objects.ATObject;
 33import edu.vub.at.objects.ATTable;
 34import edu.vub.at.objects.natives.NATBoolean;
 35import edu.vub.at.objects.natives.NATNil;
 36import edu.vub.at.objects.natives.NATNumber;
 37import edu.vub.at.objects.natives.grammar.AGSymbol;
 38
 39/**
 40 * MirageTest tests the creation of Mirages (objects with custom meta-behaviour) given
 41 * a NATIntercessiveMirror instance.
 42 * 
 43 * @author smostinc
 44 */
 45public class MirageTest extends AmbientTalkTest {
 46
 47	public static void main(String[] args) {
 48		junit.swingui.TestRunner.run(MirageTest.class);
 49	}
 50	
 51	protected void setUp() throws Exception {
 52		super.setUp();
 53		
 54		evalAndReturn("def let: clo { clo(); };");
 55	}
 56	
 57	
 58	/**
 59	 * This test verifies invariants with respect to the creation of mirages in AmbientTalk.
 60	 * First of all, it tests the relationship between the original mirror and the mirage's
 61	 * mirror (they should be non-identical yet cloned from each other).
 62	 */
 63	public void testMirageCreation() throws InterpreterException {
 64		ATObject mirrorP = evalAndReturn(
 65				"def mirrorP := mirror: { \n" +
 66				"  def iAm := \"the original\"; \n" +
 67				"  def init(@args) { \n" +
 68				"    iAm := \"a clone\"; \n" +
 69				"    super^init(@args); \n" +
 70				"  } \n" +
 71				"} \n");
 72		ATObject baseP   = evalAndReturn(
 73				"def baseP   := mirrorP.base;");
 74		
 75		// when creating a mirror, its base field is initialised to an empty mirage
 76		assertEquals(baseP.getClass(), NATMirage.class);
 77		evalAndCompareTo("mirrorP.listMethods().length", "3"); // default objects have three primitve methods
 78		evalAndCompareTo( // default objects have one primitive field super
 79				"mirrorP.listFields()",
 80				"[<field:super>]");
 81				
 82		// when creating an ex nihilo object, the init is not called
 83		evalAndCompareTo(
 84				"mirrorP.iAm",
 85				"\"the original\"");
 86
 87		ATObject subject = evalAndReturn(
 88				"def subject := object: { \n" +
 89				"  def field := `field; \n" +
 90				"  def canonical() { nil }; \n" +
 91				"  def keyworded: arg1 message: arg2 { nil }; \n" +
 92				"} mirroredBy: mirrorP; \n");
 93		ATObject mirror  = evalAndReturn(
 94				"def mirror  := reflect: subject;");
 95
 96		// mirror should be a clone of the mirrorP prototype
 97		assertNotSame(mirrorP, mirror);
 98		assertTrue(mirror.meta_isCloneOf(mirrorP).asNativeBoolean().javaValue);
 99		
100		// test for identical methods by printing the table of methods (both return a new table)
101		assertEquals( 
102				mirrorP.meta_listMethods().toString(),
103				mirror.meta_listMethods().toString());
104		
105		// the init method should have been called, which has set the iAm field
106		evalAndCompareTo(
107				"mirror.iAm",
108				"\"a clone\"");
109	}
110	
111
112	/**
113	 * This test verifies invariants with respect to the cloning of custom mirrors in AmbientTalk.
114	 * First of all, it tests the relationship between the original mirror and the clone to ensure
115	 * they are proper clones of each other. Secondly, when cloning custom mirrors their base field
116	 * is also cloned.
117	 *  
118	 * @throws InterpreterException
119	 */	public void testCustomMirrorCloning() throws InterpreterException {
120		ATObject subject = evalAndReturn(
121				"def subject  := object: { \n" +
122				"  def field  := `field; \n" +
123				"  def canonical() { nil }; \n" +
124				"  def keyworded: arg1 message: arg2 { nil }; \n" +
125				"} mirroredBy: (mirror: { nil }); \n");
126		ATObject mirror  = evalAndReturn(
127				"def mirror   := reflect: subject;");
128		
129		// clone the mirror
130		ATObject mirrorC  = evalAndReturn(
131				"def mirrorC  := clone: mirror;");
132
133		// mirror should be a clone of the mirrorP prototype
134		assertNotSame(mirror, mirrorC);
135		assertTrue(mirrorC.meta_isCloneOf(mirror).asNativeBoolean().javaValue);
136		
137		// test for identical methods by printing the table of methods (both return a new table)
138		assertEquals( 
139				mirror.meta_listMethods().toString(),
140				mirrorC.meta_listMethods().toString());
141		
142		ATObject subjectC = evalAndReturn(
143				"def subjectC := mirrorC.base;");
144		
145		// mirror should be a clone of the mirrorP prototype
146		assertNotSame(subject, subjectC);
147		assertTrue(subjectC.meta_isCloneOf(subject).asNativeBoolean().javaValue);
148		
149		// test for identical methods by printing the table of methods (both return a new table)
150		assertEquals( 
151				subject.meta_listMethods().toString(),
152				subjectC.meta_listMethods().toString());
153
154	}
155	
156	/**
157	 * This test ensures that default extensions of mirrors share the base object of the parent.
158	 */
159	public void testChildMirrorSharesBase() throws InterpreterException {
160		ATObject result = evalAndReturn(
161				"def meta := mirror:  { nil }; \n" +
162				"def base := object:  { nil } mirroredBy: meta; \n" +
163				"    meta := reflect: base; \n" +
164				"def extension := extend: meta with: { nil }; \n" +
165				"(extension.base == meta.base);"); 
166		assertTrue(result.asNativeBoolean().javaValue);
167	}
168	
169	/**
170	 * This test ensures that the 'base' field of a mirror is not modified
171	 * when the mirror is used to create a new mirage.
172	 */
173	public void testMirrorBaseNotModifiedOnClone() {
174		ATObject result = evalAndReturn(
175				"def origMirror := mirror:  { nil };\n" +
176				"def origBase := origMirror.base;\n" +
177				"def newBase := object: { nil } mirroredBy: origMirror; \n" +
178				"(origMirror.base) == origBase\n"); 
179		assertEquals(NATBoolean._TRUE_, result);
180	}
181	
182	public void testMirageInvocation() throws InterpreterException {
183		class BufferedEcho extends NativeClosure {
184			
185			StringBuffer output;
186			
187			public BufferedEcho() {
188				super(NATNil._INSTANCE_);
189				initialize();
190			}
191			
192			public ATObject base_apply(ATTable arguments) throws InterpreterException {
193				output.append(arguments.base_at(NATNumber.ONE).asNativeText().javaValue + "\n");
194				return scope_;
195			}
196			
197			public void initialize() {
198				output = new StringBuffer();
199			}
200			
201			public String readOutput() {
202				return output.toString();
203			}
204		}
205		
206		BufferedEcho buffer = new BufferedEcho();
207		
208		ctx_.base_getLexicalScope().meta_defineField(AGSymbol.jAlloc("echo:"), buffer);
209
210		
211		// define a simple logging mirror which uses a shared lexical scope to logs method invocations with proper indentation
212		evalAndReturn(
213				"def loggingMirror := let: { | defaultSpacing := 2 | \n" +
214				"	def indentLevel := 0;" +
215				"	def spaces() {" +
216				"		def result := \"\";" +
217				"		indentLevel.doTimes: { | i |" +
218				"			defaultSpacing.doTimes: { | j |" +
219				"				result := result + \" \";" +
220				"			}" +
221				"		};" +
222				"		result;" +
223				"	};" +
224				"" +
225				"	mirror: {" +
226				"		def invoke(receiver, selector, args) {" +
227				"			echo: (spaces() + \"Invocation of method \" + selector + \" with arguments \" + args + \" on \" + receiver + \"(\" + super.base + \")\");" +
228				"			indentLevel := indentLevel + 1;" +
229				"			def result := super^invoke(receiver, selector, args);" +
230				"			indentLevel := indentLevel - 1;" +
231				"			echo: (spaces() + \"Invocation of method \" + selector + \" yielded \" + result );" +
232				"			result;" +
233				"		}" +
234				"	};" +
235				"};");
236		
237		// Test setup 1: parent with logging mirror
238		ATObject parent = evalAndReturn(
239				"def mirroredParent := object: {" +
240				"  def m() { self.n() };" +
241				"  def n() { \"ok\" };" +
242				"} mirroredBy: loggingMirror;");
243		
244		evalAndReturn("echo: mirroredParent.m();");
245		
246		assertEquals(
247				"Invocation of method m with arguments [] on " + parent + "(" + parent + ")\n" +
248				"  Invocation of method n with arguments [] on " + parent + "(" + parent + ")\n" +
249				"  Invocation of method n yielded ok\n" +
250				"Invocation of method m yielded ok\n" +
251				"ok\n",
252				buffer.readOutput());
253		
254		buffer.initialize();
255		
256		ATObject child = evalAndReturn(
257				"def unmirroredChild := object: {" +
258				"  super := mirroredParent; " + 
259				"  def m() { " +
260				"    echo: \"My parent will start logging now\";" +
261				"    super^m();" + 
262				"  };" + 
263				"};");
264		
265		evalAndReturn("echo: unmirroredChild.m();");
266		
267		assertEquals(
268				"My parent will start logging now\n" +
269				"Invocation of method m with arguments [] on " + child + "(" + parent + ")\n" +
270				"  Invocation of method n with arguments [] on " + child + "(" + parent + ")\n" +
271				"  Invocation of method n yielded ok\n" +
272				"Invocation of method m yielded ok\n" +
273				"ok\n",
274				buffer.readOutput());
275		
276		buffer.initialize();
277		
278//		child = evalAndReturn(
279//				"def mirroredChild := object: {\n" +
280//				"  super := mirroredParent;\n" +
281//				"  def n() { \n" +
282//				"    echo: \"    Indentation of this call should be correct as the lexical scope is shared by both mirrors\"; \n" +
283//				"    super^n(); \n" +
284//				"  }; \n" +
285//				"} mirroredBy: loggingMirror;");
286//		
287//		evalAndReturn("echo: mirroredChild.m();");
288//		
289//		assertEquals("", buffer.readOutput());
290	}
291	
292}